Android proporciona una implementación de referencia de todos los componentes necesarios para implementar Android Virtualization Framework. Actualmente, esta implementación se limita a ARM64. En esta página, se explica la arquitectura del framework.
Segundo plano
La arquitectura de Arm permite hasta cuatro niveles de excepción, en los que el nivel de excepción 0 (EL0) es el menos privilegiado y el nivel de excepción 3 (EL3) es el más privilegiado. La parte más grande de la base de código de Android (todos los componentes del espacio de usuario) se ejecuta en EL0. El resto de lo que se conoce comúnmente como "Android" es el kernel de Linux, que se ejecuta en EL1.
La capa EL2 permite la introducción de un hipervisor que permite aislar la memoria y los dispositivos en pVM individuales en EL1/EL0, con garantías de confidencialidad e integridad sólidas.
Hipervisor
La máquina virtual basada en kernel protegida (pKVM) se basa en el hipervisor KVM de Linux, que se amplió con la capacidad de restringir el acceso a las cargas útiles que se ejecutan en máquinas virtuales invitadas marcadas como “protegidas” en el momento de la creación.
KVM/arm64 admite diferentes modos de ejecución según la disponibilidad de ciertas funciones de la CPU, es decir, las extensiones de host de virtualización (VHE) (ARMv8.1 y versiones posteriores). En uno de esos modos, conocido como modo no VHE, el código del hipervisor se divide de la imagen del kernel durante el inicio y se instala en EL2, mientras que el kernel se ejecuta en EL1. Aunque forma parte de la base de código de Linux, el componente EL2 de KVM es un componente pequeño a cargo del cambio entre varios EL1. El componente del hipervisor se compila con Linux, pero se encuentra en una sección separada y dedicada de la memoria de la imagen vmlinux
. La pKVM aprovecha este diseño extendiendo el código del hipervisor con funciones nuevas, lo que le permite establecer restricciones en el kernel del host de Android y el espacio del usuario, y limitar el acceso del host a la memoria de invitado y al hipervisor.
Módulos de proveedores de la pKVM
Un módulo de proveedor de pKVM es un módulo específico de hardware que contiene funcionalidad específica del dispositivo, como los controladores de la unidad de administración de memoria de entrada y salida (IOMMU). Estos módulos te permiten transferir funciones de seguridad que requieren acceso de nivel de excepción 2 (EL2) a la pKVM.
Para obtener información sobre cómo implementar y cargar un módulo de proveedores de la pKVM, consulta Implementa un módulo de proveedores de la pKVM.
Procedimiento de inicio
En la siguiente figura, se muestra el procedimiento de inicio de la pKVM:
- El bootloader ingresa al kernel genérico en EL2.
- El kernel genérico detecta que se está ejecutando en EL2 y se quita los privilegios para pasar a EL1, mientras que la pKVM y sus módulos siguen ejecutándose en EL2. Además, los módulos de proveedores de la pKVM se cargan en este momento.
- El kernel genérico se inicia de forma normal y carga todos los controladores del dispositivo necesarios hasta llegar al espacio del usuario. En este punto, pKVM está en su lugar y controla las tablas de páginas de la etapa 2.
El procedimiento de inicio confía en el bootloader para mantener la integridad de la imagen del kernel solo durante el inicio anticipado. Cuando se quitan los privilegios del kernel, el hipervisor ya no lo considera confiable, por lo que es responsable de protegerse incluso si el kernel está comprometido.
Tener el kernel de Android y el hipervisor en la misma imagen binaria permite una interfaz de comunicación muy bien acoplada entre ellos. Esta vinculación estrecha garantiza actualizaciones atómicas de los dos componentes, lo que evita la necesidad de mantener la interfaz entre ellos estable y ofrece una gran flexibilidad sin comprometer la capacidad de mantenimiento a largo plazo. La vinculación estrecha también permite optimizaciones de rendimiento cuando ambos componentes pueden cooperar sin afectar las garantías de seguridad que proporciona el hipervisor.
Además, la adopción de GKI en el ecosistema de Android permite que el hipervisor de pKVM se implemente automáticamente en dispositivos Android en el mismo binario que el kernel.
Protección de acceso a la memoria de la CPU
La arquitectura de Arm especifica una unidad de administración de memoria (MMU) dividida en dos etapas independientes, que se pueden usar para implementar la traducción de direcciones y el control de acceso a diferentes partes de la memoria. La MMU de la etapa 1 está controlada por EL1 y permite un primer nivel de traducción de direcciones. Linux usa la MMU de etapa 1 para administrar el espacio de direcciones virtual que se proporciona a cada proceso de espacio de usuario y a su propio espacio de direcciones virtual.
EL2 controla la MMU de etapa 2 y permite aplicar una segunda traducción de dirección en la dirección de salida del MMU de etapa 1, lo que genera una dirección física (PA). Los hipervisores pueden usar la traducción de la etapa 2 para controlar y traducir los accesos a la memoria de todas las VMs invitadas. Como se muestra en la figura 2, cuando ambas etapas de traducción están habilitadas, la dirección de salida de la etapa 1 se denomina dirección física intermedia (IPA). Nota: La dirección virtual (VA) se traduce en un IPA y, luego, en un PA.
Históricamente, KVM se ejecuta con la traducción de la etapa 2 habilitada mientras se ejecutan invitados y con la etapa 2 inhabilitada mientras se ejecuta el kernel de Linux del host. Esta arquitectura permite que los accesos a la memoria de la MMU de la etapa 1 del host pasen por la MMU de la etapa 2, lo que permite un acceso sin restricciones del host a las páginas de memoria de invitado. Por otro lado, pKVM habilita la protección de la etapa 2 incluso en el contexto del host y pone al hipervisor a cargo de proteger las páginas de memoria de invitados en lugar del host.
KVM aprovecha al máximo la traducción de direcciones en la etapa 2 para implementar asignaciones complejas de IPA/PA para invitados, lo que crea la ilusión de memoria contigua para los invitados a pesar de la fragmentación física. Sin embargo, el uso de la MMU de etapa 2 para el host se limita solo al control de acceso. La etapa 2 del host se asigna a la identidad, lo que garantiza que la memoria contigua en el espacio IPA del host sea contigua en el espacio de PA. Esta arquitectura permite el uso de asignaciones grandes en la tabla de páginas y, en consecuencia, reduce la presión sobre el búfer de almacenamiento en búfer de traducción (TLB). Debido a que PA puede indexar una asignación de identidad, la etapa 2 del host también se usa para hacer un seguimiento de la propiedad de la página directamente en la tabla de páginas.
Protección de acceso directo a memoria (DMA)
Como se describió anteriormente, anular la asignación de páginas de invitados del host de Linux en las tablas de páginas de la CPU es un paso necesario, pero insuficiente para proteger la memoria de invitados. pKVM también debe proteger contra los accesos a la memoria que realizan los dispositivos compatibles con DMA bajo el control del kernel del host y la posibilidad de un ataque de DMA iniciado por un host malicioso. Para evitar que un dispositivo de este tipo acceda a la memoria de invitado, pKVM requiere hardware de unidad de administración de memoria de entrada y salida (IOMMU) para cada dispositivo compatible con DMA en el sistema, como se muestra en la Figura 3.
Como mínimo, el hardware de IOMMU proporciona los medios para otorgar y revocar el acceso de lectura y escritura de un dispositivo a la memoria física a nivel de la granularidad de la página. Sin embargo, este hardware de IOMMU limita el uso de dispositivos en pVM, ya que supone una etapa 2 con asignación de identidad.
Para garantizar el aislamiento entre máquinas virtuales, la IOMMU debe poder distinguir las transacciones de memoria generadas en nombre de diferentes entidades para que se pueda usar el conjunto adecuado de tablas de páginas para la traducción.
Además, reducir la cantidad de código específico de SoC en EL2 es una estrategia clave para reducir la base de procesamiento confiable (TCB) general de la pKVM y se contrapone a la inclusión de controladores IOMMU en el hipervisor. Para mitigar este problema, el host en EL1 es responsable de las tareas de administración auxiliares de IOMMU, como la administración de energía, la inicialización y, cuando corresponda, el manejo de interrupciones.
Sin embargo, poner el host en control del estado del dispositivo impone requisitos adicionales a la interfaz de programación del hardware de IOMMU para garantizar que las verificaciones de permisos no se puedan omitir por otros medios, por ejemplo, después de un restablecimiento del dispositivo.
Una IOMMU estándar y bien admitida para dispositivos Arm que permite el aislamiento y la asignación directa es la arquitectura de la unidad de administración de memoria del sistema (SMMU) de Arm. Esta arquitectura es la solución de referencia recomendada.
Propiedad de la memoria
En el momento del inicio, se supone que toda la memoria que no es del hipervisor es propiedad del host y el hipervisor realiza un seguimiento de ella como tal. Cuando se crea una pVM, el host dona páginas de memoria para permitir que se inicie, y el hipervisor transfiere la propiedad de esas páginas del host a la pVM. Por lo tanto, el hipervisor aplica restricciones de control de acceso en la tabla de páginas de la etapa 2 del host para evitar que vuelva a acceder a las páginas, lo que proporciona confidencialidad al invitado.
La comunicación entre el host y los invitados es posible gracias al uso compartido de memoria controlado entre ellos. Los invitados pueden compartir algunas de sus páginas con el host a través de una hiperllamada, que le indica al hipervisor que vuelva a asignar esas páginas en la tabla de páginas de la etapa 2 del host. De manera similar, la comunicación del host con TrustZone es posible gracias a las operaciones de préstamo o uso compartido de memoria, que pKVM supervisa y controla de cerca con la especificación de Framework de firmware para Arm (FF-A).
Dado que los requisitos de memoria de una pVM pueden cambiar con el tiempo, se proporciona una hiperllamada que permite que la propiedad de las páginas especificadas que pertenecen al llamador se devuelva al host. En la práctica, esta hiperllamada se usa con el protocolo de globo de Virtio para permitir que el VMM solicite memoria de la pVM y que esta notifique al VMM las páginas cedidas de forma controlada.
El hipervisor es responsable de hacer un seguimiento de la propiedad de todas las páginas de memoria del sistema y de si se comparten o se prestan a otras entidades. La mayor parte de este seguimiento de estado se realiza con metadatos adjuntos a las tablas de páginas de la etapa 2 del host y los invitados, con bits reservados en las entradas de la tabla de páginas (PTE), que, como su nombre lo sugiere, están reservadas para el uso de software.
El host debe asegurarse de no intentar acceder a páginas a las que el hipervisor haya bloqueado el acceso. Un acceso al host ilegal provoca que el hipervisor inserte una excepción síncrona en el host, lo que puede provocar que la tarea de espacio de usuario responsable reciba una señal SEGV o que falle el kernel del host. Para evitar accesos accidentales, el kernel host hace que las páginas donadas a los invitados no sean aptas para intercambiarse o fusionarse.
Control de interrupciones y temporizadores
Las interrupciones son una parte esencial de la forma en que un invitado interactúa con los dispositivos y para la comunicación entre las CPU, donde las interrupciones del interprocesador (IPIs) son el principal mecanismo de comunicación. El modelo de KVM es delegar toda la administración de interrupciones virtuales al host en EL1, que para ese propósito se comporta como una parte no confiable del hipervisor.
pKVM ofrece una emulación completa del controlador de interrupciones genérico versión 3 (GICv3) basada en el código KVM existente. El temporizador y los IPIs se controlan como parte de este código de emulación no confiable.
Compatibilidad con GICv3
La interfaz entre EL1 y EL2 debe garantizar que el estado de interrupción completo sea visible para el host de EL1, incluidas las copias de los registros del hipervisor relacionados con las interrupciones. Por lo general, esta visibilidad se logra con regiones de memoria compartidas, una por CPU virtual (vCPU).
El código de compatibilidad del entorno de ejecución del registro del sistema se puede simplificar para admitir solo la captura de registros del registro de interrupción generado por software (SGIR) y del registro de interrupción desactivada (DIR). La arquitectura exige que estos registros siempre se capturen en EL2, mientras que las otras trampas hasta ahora solo han sido útiles para mitigar errores. Todo lo demás se maneja en hardware.
En el lado de MMIO, todo se emula en EL1 y se reutiliza toda la infraestructura actual en KVM. Por último, Wait for Interrupt (WFI) siempre se retransmite a EL1, ya que esta es una de las primitivas de programación básicas que usa KVM.
Compatibilidad con temporizadores
El valor del comparador para el temporizador virtual debe exponerse a EL1 en cada WFI de captura para que EL1 pueda insertar interrupciones del temporizador mientras la CPU virtual está bloqueada. El temporizador físico se emula por completo y todas las trampas se reenvían a EL1.
Manejo de MMIO
Para comunicarse con el monitor de máquina virtual (VMM) y realizar la emulación GIC, las trampas de MMIO deben retransmitirse al host en EL1 para una clasificación adicional. La pKVM requiere lo siguiente:
- IPA y tamaño del acceso
- Datos en caso de una operación de escritura
- Orden de bytes de la CPU en el punto de captura
Además, las trampas con un registro de propósito general (GPR) como fuente o destino se retransmiten con un pseudoregistro de transferencia abstracto.
Interfaces de invitados
Un invitado puede comunicarse con un invitado protegido mediante una combinación de hiperllamadas y acceso a la memoria de las regiones atrapadas. Las hiperllamadas se exponen según el estándar SMCCC, con un rango reservado para una asignación de proveedores por parte de KVM. Las siguientes hiperllamadas son de importancia particular para los invitados de pKVM.
Hiperllamadas genéricas
- PSCI proporciona un mecanismo estándar para que el invitado controle el ciclo de vida de sus vCPU, lo que incluye la activación, la desactivación y el apagado del sistema.
- La TRNG proporciona un mecanismo estándar para que el invitado solicite entropía al pKVM, que retransmite la llamada a EL3. Este mecanismo es particularmente útil cuando no se puede confiar en el host para virtualizar un generador de números aleatorios (RNG) de hardware.
Hiperllamadas de la pKVM
- Uso compartido de la memoria con el organizador. Inicialmente, el host no puede acceder a toda la memoria de invitado, pero el acceso del host es necesario para la comunicación de memoria compartida y para los dispositivos paravirtualizados que dependen de búferes compartidos. Las hiperllamadas para compartir y dejar de compartir páginas con el host permiten que el invitado decida exactamente a qué partes de la memoria se puede acceder desde el resto de Android sin necesidad de un protocolo de enlace.
- Renuncia de memoria al host. Por lo general, toda la memoria del invitado le pertenece hasta que se destruye. Este estado puede ser inadecuado para las VMs de larga duración con requisitos de memoria que varían con el tiempo. La hiperllamada
relinquish
permite que un invitado transfiera explícitamente la propiedad de las páginas al host sin necesidad de que se cancele el invitado. - Captura de acceso a la memoria en el host Por lo general, si un invitado de KVM accede a una dirección que no corresponde a una región de memoria válida, el subproceso de CPU virtual sale al host, y el acceso se suele usar para MMIO y el VMM lo emula en el espacio de usuario. Para facilitar este manejo, pKVM debe anunciar detalles sobre la instrucción con errores, como su dirección, los parámetros de registro y, posiblemente, su contenido al host, lo que podría exponer de forma involuntaria datos sensibles de un invitado protegido si no se anticipó la trampa. pKVM resuelve este problema tratando estos errores como fatales, a menos que el invitado haya emitido previamente una hiperllamada para identificar el rango de IPA con errores como uno para el que los accesos pueden atrapar al host. Esta solución se conoce como guardia de MMIO.
Dispositivo de E/S virtual (virtio)
Virtio es un estándar popular, portátil y maduro para implementar dispositivos paravirtualizados y, luego, interactuar con ellos. La mayoría de los dispositivos expuestos a invitados protegidos se implementan con virtio. Virtio también respalda la implementación vsock que se usa para la comunicación entre un invitado protegido y el resto de Android.
Por lo general, el VMM implementa los dispositivos virtio en el espacio de usuario del host, que intercepta los accesos de memoria atrapados del invitado a la interfaz MMIO del dispositivo virtio y emula el comportamiento esperado. El acceso a MMIO es relativamente costoso porque cada acceso al dispositivo requiere un viaje de ida y vuelta al VMM y viceversa, por lo que la mayor parte de la transferencia de datos real entre el dispositivo y el invitado se realiza con un conjunto de virtqueues en la memoria. Una suposición clave de virtio es que el host puede acceder a la memoria del invitado de forma arbitraria. Esta suposición es evidente en el diseño de la virtqueue, que puede contener punteros a búferes en el invitado a los que la emulación de dispositivos está destinada a acceder directamente.
Aunque los hiperllamadas de uso compartido de memoria que se describieron antes se podrían usar para compartir búferes de datos de Virtio del invitado al host, este uso compartido se realiza necesariamente en el nivel de detalle de la página y podría exponer más datos de los requeridos si el tamaño del búfer es menor que el de una página. En cambio, el invitado se configura para asignar las virtqueues y sus búferes de datos correspondientes desde una ventana fija de memoria compartida, con datos que se copian (rebotan) hacia y desde la ventana según sea necesario.
Interacción con TrustZone
Aunque los invitados no pueden interactuar directamente con TrustZone, el host aún debe poder emitir llamadas a SMC en el mundo seguro. Estas llamadas pueden especificar búferes de memoria con dirección física a los que no puede acceder el host. Debido a que el software seguro generalmente no conoce la accesibilidad del búfer, un host malicioso podría usar este búfer para realizar un ataque de confusión (análogo a un ataque de DMA). Para evitar estos ataques, pKVM atrapa todas las llamadas de SMC del host a EL2 y actúa como proxy entre el host y el monitor seguro en EL3.
Las llamadas a PSCI del host se reenvían al firmware de EL3 con modificaciones mínimas. Específicamente, el punto de entrada de una CPU que se conecta o reanuda desde la suspensión se vuelve a escribir para que la tabla de páginas de la etapa 2 se instale en EL2 antes de volver al host en EL1. Durante el inicio, pKVM aplica esta protección.
Esta arquitectura se basa en el SoC que admite PSCI, preferiblemente a través del uso de una versión actualizada de TF-A como su firmware EL3.
El framework de firmware para Arm (FF-A) estandariza las interacciones entre los mundos normal y seguro, en particular, en presencia de un hipervisor seguro. Una parte importante de la especificación define un mecanismo para compartir memoria con el mundo seguro, con un formato de mensaje común y un modelo de permisos bien definido para las páginas subyacentes. pKVM proxy los mensajes de FF-A para garantizar que el host no intente compartir memoria con el lado seguro para el que no tiene permisos suficientes.
Esta arquitectura se basa en el software del mundo seguro que aplica el modelo de acceso a la memoria para garantizar que las apps de confianza y cualquier otro software que se ejecute en el mundo seguro puedan acceder a la memoria solo si es propiedad exclusiva del mundo seguro o se compartió de forma explícita con él mediante FF-A. En un sistema con S-EL2, un Secure Partition Manager Core (SPMC), como Hafnium, que mantiene las tablas de páginas de etapa 2 para el mundo seguro, debe aplicar el modelo de acceso a la memoria. En un sistema sin S-EL2, el TEE puede aplicar un modelo de acceso a la memoria a través de sus tablas de páginas de etapa 1.
Si la llamada de SMC a EL2 no es una llamada a la PSCI o un mensaje definido por FF-A, las SMC no controladas se reenvían a EL3. Se supone que el firmware seguro (necesario de confianza) puede manejar las SMC no administradas de forma segura, ya que el firmware comprende las precauciones necesarias para mantener el aislamiento de la pVM.
Monitor de máquina virtual
crosvm es un monitor de máquina virtual (VMM) que ejecuta máquinas virtuales a través de la interfaz KVM de Linux. Lo que hace que crosvm sea único es su enfoque en la seguridad con el uso del lenguaje de programación Rust y una zona de pruebas alrededor de los dispositivos virtuales para proteger el kernel del host. Para obtener más información sobre crosvm, consulta su documentación oficial aquí.
Descriptores de archivos y ioctls
KVM expone el dispositivo de caracteres /dev/kvm
al espacio de usuario con ioctls que conforman la API de KVM. Los ioctls pertenecen a las siguientes categorías:
- Las ioctls del sistema consultan y establecen los atributos globales que afectan a todo el subsistema de KVM, y crean pVMs.
- Los ioctls de VM consultan y establecen atributos que crean CPUs y dispositivos virtuales, y afectan a una pVM completa, como incluir el diseño de la memoria y la cantidad de CPUs y dispositivos virtuales.
- Los ioctls de vCPU consultan y establecen atributos que controlan el funcionamiento de una sola CPU virtual.
- Las ioctls del dispositivo consultan y establecen atributos que controlan el funcionamiento de un solo dispositivo virtual.
Cada proceso de crosvm ejecuta exactamente una instancia de una máquina virtual. En este proceso, se usa la ioctl de sistema KVM_CREATE_VM
para crear un descriptor de archivos de VM que se puede usar para emitir ioctl de pVM. Un ioctl KVM_CREATE_VCPU
o KVM_CREATE_DEVICE
en un FD de VM crea una CPU virtual/dispositivo y muestra un descriptor de archivos que apunta al
recurso nuevo. Se pueden usar ioctls en un FD de CPU virtual o dispositivo para controlar el dispositivo
que se creó con ioctl en un FD de VM. En el caso de las vCPU, esto incluye la tarea importante de ejecutar código invitado.
De forma interna, crosvm registra los descriptores de archivos de la VM con el kernel usando la interfaz epoll
activada por borde. Luego, el kernel notifica a crosvm cada vez que hay un evento nuevo pendiente en cualquiera de los descriptores de archivos.
pKVM agrega una nueva función, KVM_CAP_ARM_PROTECTED_VM
, que se puede usar para obtener información sobre el entorno de pVM y configurar el modo protegido para una VM. crosvm usa esto durante la creación de pVM si se pasa la marca --protected-vm
para consultar y reservar la cantidad adecuada de memoria para el firmware de pVM y, luego, habilitar el modo protegido.
Asignación de memoria
Una de las principales responsabilidades de un VMM es asignar su memoria y administrar su diseño de memoria. crosvm genera un diseño de memoria fijo que se describe de forma general en la siguiente tabla.
FDT en modo normal | PHYS_MEMORY_END - 0x200000
|
Liberar | ...
|
Ramdisk | ALIGN_UP(KERNEL_END, 0x1000000)
|
Kernel | 0x80080000
|
Bootloader | 0x80200000
|
FDT en modo BIOS | 0x80000000
|
Base de memoria física | 0x80000000
|
firmware de pVM | 0x7FE00000
|
Memoria del dispositivo | 0x10000 - 0x40000000
|
La memoria física se asigna con mmap
y se dona a la VM para propagar sus regiones de memoria, llamadas memslots, con el ioctl KVM_SET_USER_MEMORY_REGION
. Por lo tanto, toda la memoria de pVM de invitado se atribuye a la instancia de crosvm que la administra y puede provocar que se cancele el proceso (se cierre la VM) si el host comienza a quedarse sin memoria libre. Cuando se detiene una VM, el hipervisor limpia de forma automática la memoria y la devuelve al kernel del host.
Con un KVM normal, la VMM retiene el acceso a toda la memoria de invitado. Con la pKVM, la memoria de invitado no se asigna del espacio de direcciones físicas del host cuando se dona al invitado. La única excepción es la memoria que el invitado comparte de forma explícita, como en el caso de los dispositivos virtio.
Las regiones de MMIO en el espacio de direcciones del invitado no se asignan. El acceso del invitado a estas regiones se atrapa y genera un evento de E/S en el FD de la VM. Este mecanismo se usa para implementar dispositivos virtuales. En el modo protegido, el invitado debe confirmar que una región de su espacio de direcciones se usa para MMIO a través de un hiperllamada, a fin de reducir el riesgo de filtración accidental de información.
Programación
Cada CPU virtual está representada por un subproceso POSIX y está programada por el programador de Linux del host. El subproceso llama al ioctl KVM_RUN
en el FD de la CPU virtual, lo que hace que el hipervisor cambie al contexto de la CPU virtual invitada. El programador del host considera el tiempo que se pasa en un contexto de invitado como el tiempo que usa el subproceso de la CPU virtual correspondiente. KVM_RUN
se muestra cuando hay un evento que el VMM debe controlar, como E/S, el final de la interrupción o la detención de la CPU virtual. El VMM controla el evento y vuelve a llamar a KVM_RUN
.
Durante KVM_RUN
, el programador host sigue teniendo el subproceso interrumpible, excepto por la ejecución del código del hipervisor EL2, que no es interrumpible. La pVM invitada no tiene ningún mecanismo para controlar este comportamiento.
Como todos los subprocesos de vCPU se programan como cualquier otra tarea del espacio de usuario, están sujetos a todos los mecanismos de QoS estándar. Específicamente, cada subproceso de vCPU se puede ajustar a las CPUs físicas, colocar en cpusets, aumentar o limitar con el límite de uso, cambiar su política de prioridad o programación, y mucho más.
Dispositivos virtuales
crosvm admite varios dispositivos, incluidos los siguientes:
- virtio-blk para imágenes de disco compuestos, de solo lectura o de lectura y escritura
- vhost-vsock para la comunicación con el host
- virtio-pci como transporte de virtio
- reloj en tiempo real (RTC) pl030
- UART 16550a para comunicación serie
Firmware de pVM
El firmware de pVM (pvmfw) es el primer código que ejecuta una pVM, similar a la ROM de inicio de un dispositivo físico. El objetivo principal de pvmfw es iniciar el inicio seguro y derivar el secreto único de la pVM. pvmfw no se limita a usarse con ningún SO específico, como Microdroid, siempre y cuando crosvm admita el SO y se haya firmado correctamente.
El binario pvmfw se almacena en una partición de memoria flash con el mismo nombre y se actualiza mediante OTA.
Inicio del dispositivo
Se agrega la siguiente secuencia de pasos al procedimiento de inicio de un dispositivo habilitado para pKVM:
- El bootloader de Android (ABL) carga pvmfw desde su partición en la memoria y verifica la imagen.
- La ABL obtiene sus secretos del Motor de composición identificador de dispositivos (DICE) (identificadores de dispositivos compuestos (CDI) y cadena de certificados del DICE) de una raíz de confianza.
- El ABL deriva los CDI necesarios para pvmfw y los adjunta al objeto binario de pvmfw.
- El ABL agrega un nodo de región de memoria reservada
linux,pkvm-guest-firmware-memory
al DT, que describe la ubicación y el tamaño del binario pvmfw y los secretos que derivó en el paso anterior. - El ABL le entrega el control a Linux, que inicializa pKVM.
- pKVM desasigna la región de memoria de pvmfw de las tablas de páginas de la etapa 2 del host y la protege del host (y de los invitados) durante el tiempo de actividad del dispositivo.
Después de iniciar el dispositivo, se inicia Microdroid según los pasos de la sección Boot sequence del documento Microdroid.
Inicio de pVM
Cuando se crea una pVM, crosvm (o cualquier otro VMM) debe crear un espacio de memoria lo suficientemente grande para que el hipervisor lo complete con la imagen de pvmfw. El VMM también está restringido en la lista de registros cuyo valor inicial puede establecer (x0-x14 para la CPU virtual principal, ninguno para las CPUs virtuales secundarias). Los registros restantes están reservados y forman parte de la ABI de hypervisor-pvmfw.
Cuando se ejecuta la pVM, el hipervisor primero entrega el control de la vCPU principal a pvmfw. El firmware espera que crosvm haya cargado un kernel firmado por AVB, que puede ser un bootloader o cualquier otra imagen, y un FDT sin firmar en la memoria en offsets conocidos. pvmfw valida la firma de AVB y, si se realiza correctamente, genera un árbol de dispositivos de confianza a partir del FDT recibido, borra sus secretos de la memoria y se ramifica al punto de entrada de la carga útil. Si falla uno de los pasos de verificación, el firmware emite una hiperllamada SYSTEM_RESET
de PSCI.
Entre los inicios, la información sobre la instancia de pVM se almacena en una partición (dispositivo virtio-blk) y se encripta con el secreto de pvmfw para garantizar que, después de un reinicio, el secreto se aprovisione en la instancia correcta.