1 puntos por GN⁺ 2024-01-01 | 1 comentarios | Compartir por WhatsApp
  • SteamOS 3 “Holo” es una distribución basada en Arch para Steam Deck, pero para corregir el problema al reanudar desde suspensión en una PC de sala fue necesario revertir un commit del kernel y hacer un fork directo hasta de la imagen rootfs
  • La estructura de actualización usa un esquema A/B: instala un nuevo rootfs de solo lectura en la partición inactiva y luego reinicia; /etc conserva los cambios mediante overlayfs
  • Los parches del kernel de Valve siguen un flujo en el que se clona un repositorio Git bare desde tarballs de código fuente como linux-neptune-61-6.1.52.valve9-1.src.tar.gz del mirror de fuentes de pacman, y se compilan paquetes con tags propios y PKGBUILD
  • Reempaquetar el rootfs implica extraer rootfs.img.caibx del bundle RAUC de SteamOS, convertirlo en imagen, cambiar el UUID de Btrfs, reemplazar paquetes, cambiar el buildid, reemplazar las URL de actualización y el certificado RAUC, y volver a empaquetarlo como bundle RAUC
  • Si un servidor web propio ofrece live.json y se cambian QueryUrl, ImagesUrl y MetaUrl de steamos-atomupd, también se puede actualizar una instalación existente de SteamOS con una imagen propia

Por qué hice un fork de SteamOS para una PC de sala

  • SteamOS 3 “Holo” es una distribución Linux basada en Arch para Steam Deck, la PC portátil de videojuegos de Valve Software
  • Su método de actualización usa una estructura de actualizaciones atómicas A/B: descarga un nuevo rootfs de solo lectura en la partición inactiva y reinicia hacia esa partición
  • El usuario puede ejecutar steamos-devmode para desbloquear el rootfs y normalizar la base de datos de pacman, y así tratarlo como una distribución Linux común
  • El objetivo era crear un fork adecuado que pudiera modificar la propia imagen rootfs, en lugar de esquivarlo fácilmente con steamos-devmode
  • En la PC de sala, SteamOS funcionaba casi por completo, pero fallaba únicamente al reanudar desde suspensión
    • En la misma computadora, otras distribuciones que usan kernels mainline o stable sí reanudan desde suspensión correctamente
    • Tras encontrar el código fuente del kernel de Valve y ejecutar git bisect, el resultado fue que un commit que parece corregir la reanudación desde suspensión en el hardware de Steam Deck causaba problemas en esta PC
    • La razón directa de todo el trabajo fue tener que revertir ese commit y compilar el kernel manualmente
  • También existía la opción de usar Arch directamente, entre otras, pero si había que ajustar una distribución Linux para ejecutar juegos, era preferible apoyarse en el conjunto de paquetes probado por Valve

Particiones y estructura de actualización de SteamOS

  • El sistema SteamOS usa 8 particiones
    • La EFI system partition contiene el stage 1 bootloader y metadatos para seleccionar el conjunto de particiones A/B
    • Cada conjunto A/B incluye GRUB como stage 2 bootloader, el root filesystem y una partición /var
    • El resto del espacio del disco lo ocupa una única partición home
  • Durante el arranque se montan además varios pseudo-filesystems
    • Casi una docena de directorios, como /var/log, /root y /nix, se montan con bind mount desde /home/.steamos/offload para hacer persistentes los datos
  • /etc se maneja con overlayfs
    • Los cambios se guardan en /var/lib/overlays/etc/upper
    • Se conservan elementos que normalmente deben permanecer en /etc, como machine-id y las conexiones de NetworkManager
    • Los archivos de configuración que no se hayan tocado pueden actualizarse
    • Este método gestiona a la vez la conservación y la actualización de archivos de configuración dentro de una estructura de particiones A/B, sin lógica de gestor de paquetes
  • La actualización del sistema comienza cuando el cliente de Steam o un usuario en la terminal ejecuta steamos-update
    • Este comando ejecuta el programa Python steamos-atomupd-client
    • El cliente envía la información del SO actual y la configuración del canal de actualización del usuario a la URL en /etc/steamos-atomupd/client.conf para comprobar si hay una actualización nueva
  • Si hay una actualización nueva, el servidor responde con la ruta de un bundle RAUC
    • El cliente descarga el bundle y ejecuta rauc install
    • RAUC verifica la firma del bundle y busca rootfs.img.caibx
    • Con casync extract, descarga los fragmentos de la nueva imagen y los escribe en la partición rootfs inactiva
    • El script post-install sincroniza selectivamente datos desde el /var activo hacia el /var inactivo, y cambia la configuración del stage 1 bootloader en la EFI system partition para que arranque desde el nuevo conjunto de particiones

Crear paquetes desde el código fuente del kernel de Valve

  • Valve usa en SteamOS un kernel de Linux muy modificado, y el código fuente está disponible para descarga
  • El código fuente de la imagen actual de SteamOS puede encontrarse en sources/holo-3.5 y sources/jupiter-3.5 del mirror pacman de Valve
  • Al momento de escribir, el kernel de la imagen stable era 6.1.52-valve9-1-neptune-61, y el tarball de código fuente correspondiente pesa 2.9GiB
  • El tarball es grande porque incluye todo el árbol Git de Linux
    • Dentro del tarball hay elementos como PKGBUILD, config, config-neptune y archlinux-linux-neptune/
    • archlinux-linux-neptune/ no es un working tree normal listo para trabajar, sino un repositorio bare
  • El PKGBUILD apunta como fuente a un repositorio GitLab privado con la forma git+ssh://git@gitlab.steamos.cloud/jupiter/linux-integration.git#tag=$_tag
    • No se puede clonar directamente ni enlazar commits
    • Desde las fuentes de makepkg se puede obtener un snapshot que contiene todo el historial de commits de cada tag
    • Gracias a esta estructura fue posible hacer bisect del commit que rompía la reanudación desde suspensión en la PC de sala
  • El flujo de trabajo consiste en clonar el repositorio bare como un working tree normal y mantener una rama y tags propios
    • El ejemplo consiste en crear my-branch desde el tag 6.1.52-valve9
    • Los cambios propios se suben a otro host Git, y en PKGBUILD se cambia el source a ese repositorio y a un tag propio
    • Como repositorio de ejemplo está linux
  • Se puede crear el paquete del kernel con makepkg
    • makepkg MAKEFLAGS=-j$(nproc) o actualizar /etc/makepkg.conf resulta útil cuando no se está en una VM pequeña
    • En lo revisado, los paquetes específicos de SteamOS también usaban una estructura similar, con un repositorio Git como primer source
  • Para facilitar los pasos posteriores, se configuró un repo pacman propio
    • Se colocan los paquetes en un directorio, se ejecuta repo-add $REPO_NAME.db.tar.zst [PACKAGES...] y luego se sube a un host web
    • Este repo también ayuda a que las herramientas funcionen correctamente si más tarde se ejecuta steamos-devmode

Obtener y montar el root filesystem

  • Como no se encontraron scripts de release engineering, se optó por reempaquetar el root filesystem existente según las necesidades
  • Los scripts sin explicaciones ni comentarios están en fauxlo
  • El método habitual para obtener la imagen rootfs de SteamOS es comprar una Steam Deck o descargar la imagen de recuperación de Steam Deck, pero ambos requieren aceptar el Steam End User License Agreement
  • La versión de release actual puede verse en el JSON de snapshots que parece ser la URL fallback del sistema de actualizaciones
    • Al momento de escribir, la versión stable era 20231122.1
    • También hay un JSON de snapshots separado para el canal preview
  • La descarga del rootfs sigue el mismo orden que steamos-atomupd-client
    • Se descarga el archivo .raucb, que es el bundle RAUC
    • Del bundle, que es un filesystem SquashFS, se extrae rootfs.img.caibx
    • Con casync extract, se toman fragmentos desde el store .castr y se genera rootfs.img
    • La URL del store .castr es la URL del bundle RAUC reemplazando .raucb por .castr
    • Este comportamiento está hardcodeado en steamos-atomupd
    • El script de automatización está en fetch-current.sh
  • Los archivos .img.zip y .img.zst cercanos no son rootfs, sino una imagen de recuperación booteable separada
    • También se podría extraer la partición rootfs de la imagen de recuperación y usarla en el siguiente paso
    • Pero no era idéntica bit a bit a la imagen recibida con RAUC y casync, y de todos modos esas herramientas son necesarias para volver a crear el update bundle
  • Antes de modificar el rootfs hay que cambiar el UUID del filesystem
    • Si se actualiza desde una imagen existente de SteamOS a una imagen personalizada sin cambiar el UUID, dos filesystems distintos tendrán el mismo UUID
    • Ese estado puede causar problemas
    • Un ejemplo es btrfstune -fu rootfs.img
  • Valve usa una imagen Btrfs con compresión zstd
    • Para mantener la compresión durante los cambios, se monta con mount -o compress=zstd rootfs.img rootfs
    • Como SteamOS usa la propiedad de subvolume readonly de Btrfs, se desactiva con btrfs property set -ts rootfs ro false
  • Modificar paquetes como el kernel de Linux puede disparar scripts que requieren /dev y /proc
    • Se montan devtmpfs y proc debajo del rootfs
    • Para evitar escrituras en directorios que se montarán en el sistema booteado, se monta tmpfs en /tmp, /run, /var y /home
    • Se hace bind mount del /etc/resolv.conf del host para que la resolución de nombres funcione dentro del chroot

Reemplazar paquetes y modificar metadatos de la imagen

  • El repositorio propio se agrega como primera entrada de repo en /etc/pacman.conf
    • Así, aunque haya paquetes de versiones más recientes en los repos de Valve, los paquetes propios tienen prioridad
    • Incluso si más adelante se ejecuta steamos-devmode, se pueden reinstalar los paquetes propios
  • El stanza de repo de ejemplo usa [fauxlo], Server = https://fauxlo.ili.fyi/pacman/$arch y SigLevel = Never
    • SigLevel = Never permite paquetes sin firma
    • Para instalar paquetes con firma GPG hay que poblar el keyring de pacman
    • En lugar de tocar el keyring vacío de /etc/pacman.d/gnupg, se usó un método que puebla un keyring nuevo sobre tmpfs
  • La instalación de paquetes se realiza con una forma como pacman --sysroot rootfs --noconfirm -Sy linux-neptune-61
    • En el script real se evita -y y solo se sincroniza por detrás la base de datos del repo propio de pacman
    • Esto permite fijar el estado de los demás repositorios al momento en que se construyó la imagen original
    • Es una decisión para reducir los cambios que aparecen en el diff de la imagen
  • steamos-atomupd lee la versión de la imagen actual y el build ID desde /lib/steamos-atomupd/manifest.json; si no existe, usa /etc/os-release
    • Si el build ID de la actualización ofrecida por el servidor es igual al de la imagen actual, rechaza la actualización
    • También es útil para identificar qué imagen se está ejecutando
  • El build ID debe estar obligatoriamente en formato YYYYMMDD.N
    • Si el formato no coincide, steamos-atomupd termina con un traceback de Python
    • Para evitar incrementarlo manualmente, se puede poner HHMMSS o un timestamp Unix en N
    • Se cambian juntos el buildid de manifest.json y el BUILD_ID de os-release
    • El fragmento de script Bash para esta tarea está en repack.sh
  • RAUC usa certificados X.509 para la configuración de confianza
    • El certificado confiable está en /etc/rauc/keyring.pem
    • Basta con un certificado simple self-signed
    • Se instala el nuevo certificado en rootfs/etc/rauc/keyring.pem
  • También se cambian las URL de rootfs/etc/steamos-atomupd/client.conf al servidor propio
    • QueryUrl
    • ImagesUrl
    • MetaUrl
  • Mientras no se exceda el espacio de la imagen Btrfs de 5GiB, se pueden hacer otros cambios
    • Por ejemplo, si se quiere encontrar el dispositivo SteamOS en la red como hostname.local, se puede eliminar rootfs/usr/lib/systemd/resolved.conf.d/00-disable-mdns.conf
    • También se podría sobrescribir mediante la configuración overlay de /etc, pero se consideró engorroso
  • Como principio, los cambios que pueden hacerse fácilmente sin tocar la imagen no se incluyen en el rootfs
    • Firefox puede instalarse en el rootfs
    • Pero habría que volver a reempaquetar la imagen con cada actualización de seguridad de Firefox

Desmontar el rootfs y crear el bundle RAUC

  • Al terminar las modificaciones, el filesystem se marca otra vez como read-only
    • btrfs property set -ts rootfs ro true
  • Los bloques no usados se descartan con fstrim -v rootfs
  • Para desmontar resulta útil umount --recursive rootfs
    • Permite procesar también los pseudo-filesystems montados antes
  • Antes de crear el bundle RAUC hay que generar el casync store y el blob index
    • Un ejemplo es casync make --store=rootfs.img.castr bundle/rootfs.img.caibx rootfs.img
  • El bundle RAUC necesita tres archivos
    • manifest.raucm
    • rootfs.img.caibx
    • UUID, con el UUID del filesystem
  • manifest.raucm contiene información de la actualización y de la imagen rootfs
    • compatible=steamos-amd64
    • version=$version
    • sha256
    • size
    • filename=rootfs.img.caibx
  • El archivo UUID se crea con blkid -s UUID -o value rootfs.img >bundle/UUID
  • Después de preparar los tres archivos, se ejecuta rauc bundle
    • Se especifican el certificado y la clave en --signing-keyring, --cert y --key
    • El resultado es rootfs.img.raucb
  • rootfs.img.raucb y rootfs.img.caibx se suben al servidor web al que apunta ImagesUrl en client.conf
    • Ambos archivos deben estar en el mismo directorio

Servidor de actualizaciones propio y aplicación

  • El servidor web usado en QueryUrl y MetaUrl debe servir archivos JSON
  • En una configuración simple, basta con un único live.json
    • El objeto .minor.candidates[0].image debe ser igual a /lib/steamos-atomupd/manifest.json dentro de la imagen
    • update_path es la ruta que el cliente de actualización agrega después de ImagesUrl para descargar el bundle
  • El ejemplo de configuración de Caddy reescribe hacia live.json las solicitudes que steamos-atomupd envía a QueryUrl y MetaUrl
    • Reescribe /updates a /live.json
    • Reescribe /meta/*/*/*/*.json y /meta/*/*/*/*/*.json a /live.json
    • Usa file_server browse
  • Las QueryUrl y MetaUrl reales de SteamOS parecen tener más lógica, pero solo con esta configuración steamos-atomupd ya puede encontrar una actualización nueva
  • Hay lógica para evitar la actualización si la imagen ya anunciada es la que se está ejecutando actualmente
  • Para actualizar una instalación existente de SteamOS a una imagen propia, basta con modificar /etc/rauc/keyring.pem y /etc/steamos-atomupd/client.conf
    • No hace falta steamos-readonly disable
    • Los cambios entran en el overlay de /etc
    • Después de ejecutar steamos-update, conviene considerar limpiar esos cambios desde /var/lib/overlays/etc/upper
  • Parece que también se podría instalar un SteamOS modificado editando una de las imágenes de recuperación de Valve y reemplazando el rootfs por una imagen propia, pero este método no fue probado

1 comentarios

 
GN⁺ 2024-01-01
Opiniones de Hacker News
  • Me gustan estos artículos sobre personalizar a fondo el software/OS de un dispositivo propio. También me alegra que en Steam Deck no haya que preocuparse por la tivoización.
    La parte que me pareció más interesante del artículo fue la partición /nix. No sabía que Steam Deck soportara nixpkgs; investigando un poco más, vi que, aunque no viene en la instalación por defecto, sí se puede poner en el dispositivo sin tener que bifurcar todo el OS.

    • Nix siempre se pudo instalar en cualquier OS tipo *nix sin “bifurcar todo el OS”.
      El repositorio de Nix puede estar en cualquier ubicación con permisos de escritura, y basta con cambiar $PATH para que apunte a un directorio de enlaces simbólicos.
    • ¿Alguien sabe qué cosas de nixpkgs se usan en Steam Deck? Yo también uso bastante nixpkgs y me da curiosidad, pero lamentablemente no tengo una Steam Deck.
  • Es un artículo realmente minucioso e interesante. Personalmente, creo que jamás llegaría tan lejos.
    Mi experiencia con Linux se limita a la época en que usaba una Raspberry Pi, y aun eso habrá sido como un 1%, así que el autor me parece admirable.

    • Yo estuve en una situación parecida a la del autor. Durante bastante tiempo tuve que compilar yo mismo un kernel de Red Hat por una razón muy particular: para saltarme la verificación RMRR y hacer passthrough de la GPU a una VM con Windows.
      Algo parecido a https://github.com/kiler129/relax-intel-rmrr, aunque ese no es mi repositorio.
      La causa de fondo solo se puede resolver con una actualización de ROM del fabricante, pero mi viejo DL360 ya no tiene soporte de HPE.
      El parche en sí es un cambio de una sola línea, pero actualizar el kernel es engorroso. Hay que conseguir el SRPM; como no hay repositorio Git, hay que desempaquetar el SRPM, aplicar el parche y luego volver a compilar e instalar.
    • Si quieres un televisor, puede que pronto ya no queden opciones reales.
  • Ya existen distribuciones basadas en componentes de SteamOS y adaptadas para usarse en PC con controladores. ChimeraOS incluso incluye EmuDeck, una herramienta adicional para Steam Deck, y en mi entorno funciona bastante sin problemas.

  • Pedí una GPU para correr Steam Headless en un servidor NAS con unRaid usando una imagen Docker muy buena, y conectarme desde una laptop con Windows mediante un cliente como Moonlight.
    Si funciona bien, es mucho mejor que comprar otra vez hardware de escritorio para juegos teniendo un NAS que está inactivo la mayor parte del tiempo. Eso sí, hay que mantener la configuración de energía de la tarjeta Nvidia en reposo cuando no se use. Espero que se pueda con una llamada a nvidia-persistenced.
    1: https://github.com/Steam-Headless/docker-steam-headless

    • Yo también pasé bastante tiempo intentando hacer casi lo mismo con GOW. Fue mucho más difícil de lo que esperaba, e incluso necesité un dummy HDMI para ajustar la configuración del servidor X.
      1: https://github.com/games-on-whales/gow
    • Como otra alternativa, también puedes levantar KVM con passthrough de GPU y usar cloud-init para ejecutar Sunshine y el juego, o simplemente usar el monitor directamente.
      https://kubevirt.io/user-guide/virtual_machines/host-devices...
      ¡Ejecución de juegos declarativa y cloud native!
      kubectl apply -f crysis.yaml
    • Se ve bien. Ahora uso Sunshine + Moonlight, pero pronto pienso probar el rendimiento de Steam Headless.
    • Bastante interesante. Al hacer streaming en una red local de esta forma, ¿se nota alguna latencia de entrada o limitación en la calidad de video?
    • Suena bien. Hace tiempo vengo imaginando una configuración para dejar corriendo en un servidor juegos por turnos en modo hotseat, como Civilization, y conectarnos remotamente desde el navegador para jugar partidas largas por turnos con amigos, en cualquier momento y desde cualquier lugar.
  • Hoy conocí RAUC (https://rauc.io/). Me preguntaba cómo había implementado Valve el esquema de actualizaciones A/B.

  • ¿No extrañas un poco el favicon de lluvia de meteoros de Netscape?

  • Artículo interesante. Las actualizaciones A/B me parecen un poco excesivas. Si algo sale mal, puedes arrancar una distribución live o instalar un sistema de recuperación de una versión anterior en una partición separada.
    En los últimos años usé NixOS y luego volví a Arch; antes de eso también había usado Arch durante mucho tiempo, y creo que las preocupaciones del autor no están bien encaminadas.
    Arch es sin duda una distribución muy seria y madura, y me resulta más confiable que Valve.
    Me pasé a Arch por la calidad de los paquetes. El repositorio principal se actualiza realmente rápido y en AUR hay muchos paquetes útiles.

    • Tú y yo podemos arrancar una distribución live, pero la abrumadora mayoría de los usuarios de computadoras no puede. Valve claramente se concentra en construir para el usuario promedio, y las distribuciones Linux, por mucho que me gusten, todavía no hacen bien eso.
      Que el sistema se recupere automáticamente después de una actualización fallida es casi indispensable para un OS de bajo mantenimiento hoy en día.
    • No se debería esperar que un usuario de Steam Deck tenga que hacer arranque desde una distribución live para arreglar una actualización rota. Debe ser fluido y resolverse por detrás.
    • Se podría hacer, claro, pero ahora existe tecnología para volver innecesario eso y los discos tampoco son tan caros, así que no hay mucha razón para no usarla.
    • Steam Deck, en esencia, se parece a una Chromebook para videojuegos, así que el esquema de particiones difícil de romper de ChromeOS parece una idea razonable.
    • ¿Podrías dar ejemplos de lo que mencionas sobre haberte pasado desde NixOS por la calidad de los paquetes de Arch?
      En general, yo considero que la calidad de los paquetes de NixOS es alta.
  • Hace poco conseguí una Legion Go, un dispositivo portátil para gaming, y estoy metiéndome más en Linux. Antes lo evitaba porque parecía una pérdida de tiempo interminable en ajustes y parches, y además tenía compatibilidad limitada con las cosas que realmente quería usar.
    Me dio curiosidad al leer sobre sistemas de archivos inmutables y sobre cómo el Linux tradicional les concede fácilmente permisos de root a todo tipo de software arbitrario.
    Ahora estoy usando NixOS y, aunque sin duda puede convertirse en una pérdida de tiempo haciendo ajustes, es muy bueno para explorar. Permite probar fácilmente distintos componentes y, si decides no mantenerlos, puedes eliminarlos por completo salvo por cierta contaminación en ~/.config. Parchear antes de instalar también es algo trivial, así que es fácil agregar parches del kernel que permitan usar Linux en hardware peculiar, como dispositivos portátiles para gaming.
    Una comunidad de NixOS llamada Jovian está reconstruyendo los tarballs arbitrarios de SteamOS de Valve como commits etiquetados en GitHub, así que puedes revisar el código fuente como si fueras empleado de Valve. Con solo agregar unas líneas a la configuración de Nix, permiten instalar tu propia copia de SteamOS sobre NixOS.
    Está claro que son expertos en Linux y, al ver el código fuente, se puede comprobar que reciben los paquetes de Valve prácticamente sin modificarlos, salvo por ajustes simples como detectar la ubicación del botón de encendido en vez de codificarla de forma fija.
    Si quieres una experiencia SteamOS pura, pero no quieres operar tu propio mirror del sistema de actualizaciones de Valve, o si quieres explorar el código fuente de Valve sin descargar un tarball de 3 GB, vale la pena probar Jovian.
    Guía de instalación: https://jovian-experiments.github.io/Jovian-NixOS/getting-st...
    Mirror del código fuente de Valve: https://github.com/orgs/Jovian-Experiments/repositories?type...

    • Yo también uso Jovian-NixOS en mi Steam Deck sin problemas, así que lo recomiendo mucho.
  • bazzite.gg también hace esto muy bien. En hardware AMD, 120Hz VRR funcionó de inmediato, y el soporte de HDR también se puede probar en alfa.

    • No había oído hablar de Bazzite.
      “Bazzite es una imagen OCI que puede usarse como sistema operativo alternativo para Steam Deck, y es un entorno similar a SteamOS listo para jugar, para computadoras de escritorio, PC de home theater para la sala y varios PC portátiles”.
      https://github.com/ublue-os/bazzite/
      Aunque no te interese, vale la pena leer el README. La lista de cosas incluidas es enorme, y muchas parecen bastante geniales y útiles, especialmente para gamers o streamers.
    • Bazzite y Linux inmutable en general son interesantes.
      No he investigado lo suficiente como para explicarlo de forma concisa y perfecta en un comentario de HN, pero la idea central es tener en la raíz una distribución Linux verificada y de solo lectura, y encima apilar paquetes en capas. Es una estructura muy inspirada en los contenedores del lado servidor.
      Busca ser más seguro, más confiable, reproducible y más fácil de personalizar que el Linux tradicional. Si anotas los paquetes que quieres en un manifiesto de contenedor, cuando sale una actualización la ejecuta y luego reinstala esos paquetes encima.
    • Lo más relevante es que Bazzite se puede forkear con relativa facilidad para agregar paquetes faltantes o la configuración que necesites a tu propia imagen personalizada, dejando la mayor parte del trabajo de infraestructura a GitHub Actions.
      https://universal-blue.org/guide/fork-your-own/
      Y como es un OS inmutable, si algo sale mal también puedes volver a una imagen anterior.
    • Según el resumen anual de Steam, 2023 fue el primer año en el que jugué exclusivamente en Linux, e incluso incluyó algunos lanzamientos de este año.
      La mayor parte fue en Steam Deck o corriendo Bazzite en una máquina virtual con passthrough de GPU, y está realmente muy bien hecho.
    • Me sorprende que Bazzite no sea más conocido. Es exactamente lo que soñaba, pero hasta hace poco ni siquiera sabía que existía.
  • ¿PC para la sala? ¿Ya casi no se usan expresiones como HTPC o “centro multimedia”?