Arquitetura AVF

O Android oferece uma implementação de referência de todos os componentes necessários para implementar o framework de virtualização do Android. No momento, essa implementação está limitada ao ARM64. Esta página explica a arquitetura do framework.

Contexto

A arquitetura Arm permite até quatro níveis de exceção, sendo que o nível de exceção 0 (EL0) é o menos privilegiado e o nível de exceção 3 (EL3) é o mais privilegiado. A maior parte da base de código do Android (todos os componentes do espaço do usuário) é executada no EL0. O restante do que é comumente chamado de "Android" é o kernel do Linux, que é executado em EL1.

A camada EL2 permite a introdução de um hipervisor que permite isolar a memória e os dispositivos em pVMs individuais no EL1/EL0, com garantias de confidencialidade e integridade.

Hipervisor

A máquina virtual protegida baseada em kernel (pKVM, na sigla em inglês) é criada com base no hipervisor KVM do Linux, que foi estendido com a capacidade de restringir o acesso aos payloads executados em máquinas virtuais guest marcadas como "protegidas" no momento da criação.

O KVM/arm64 oferece suporte a diferentes modos de execução, dependendo da disponibilidade de alguns recursos da CPU, como as extensões de host de virtualização (VHE, na sigla em inglês) (ARMv8.1 e mais recentes). Em um desses modos, conhecido como modo não-VHE, o código do hipervisor é dividido da imagem do kernel durante a inicialização e instalado no EL2, enquanto o kernel é executado no EL1. Embora faça parte da base do código do Linux, o componente EL2 da KVM é um pequeno componente responsável pela alternância entre vários EL1s. O componente do hipervisor é compilado com o Linux, mas fica em uma seção de memória separada e dedicada da imagem vmlinux. O pKVM aproveita esse design estendendo o código do hipervisor com novos recursos, permitindo colocar restrições no kernel do host do Android e no espaço do usuário, além de limitar o acesso do host à memória do convidado e ao hipervisor.

Módulos de fornecedor pKVM

Um módulo de fornecedor pKVM é um módulo específico do hardware que contém funcionalidades específicas do dispositivo, como drivers de unidade de gerenciamento de memória de entrada/saída (IOMMU). Esses módulos permitem que você transfira recursos de segurança que exigem acesso ao nível 2 de exceção (EL2) para o pKVM.

Para saber como implementar e carregar um módulo de fornecedor pKVM, consulte Implementar um módulo de fornecedor pKVM.

Procedimento de inicialização

A figura a seguir mostra o procedimento de inicialização do pKVM:

Procedimento de inicialização do pKVM

Figura 1. Procedimento de inicialização do pKVM

  1. O carregador de inicialização entra no kernel genérico no EL2.
  2. O kernel genérico detecta que está sendo executado no EL2 e se desprivilegia para o EL1, enquanto o pKVM e os módulos continuam sendo executados no EL2. Além disso, os módulos de fornecedor pKVM são carregados no momento.
  3. O kernel genérico continua a inicialização normalmente, carregando todos os drivers necessários até chegar ao espaço do usuário. Nesse ponto, o pKVM está em vigor e processa as tabelas de página da fase 2.

O procedimento de inicialização confia no carregador de inicialização para manter a integridade da imagem do kernel somente durante a inicialização inicial. Quando o kernel é desprivilegiado, ele não é mais considerado confiável pelo hipervisor, que é responsável por se proteger mesmo que o kernel esteja comprometido.

Ter o kernel do Android e o hipervisor na mesma imagem binária permite uma interface de comunicação muito acoplada entre eles. Esse acoplamento estreito garante atualizações atômicas dos dois componentes, o que evita a necessidade de manter a interface entre eles estável e oferece uma grande flexibilidade sem comprometer a manutenção a longo prazo. O acoplamento rígido também permite otimizações de desempenho quando os dois componentes podem cooperar sem afetar as garantias de segurança fornecidas pelo hipervisor.

Além disso, a adoção do GKI no ecossistema Android permite automaticamente que o hipervisor pKVM seja implantado em dispositivos Android no mesmo binário que o kernel.

Proteção de acesso à memória da CPU

A arquitetura Arm especifica uma unidade de gerenciamento de memória (MMU) dividida em dois estágios independentes, que podem ser usados para implementar a tradução de endereço e o controle de acesso a diferentes partes da memória. A MMU de estágio 1 é controlada por EL1 e permite um primeiro nível de conversão de endereços. A MMU de estágio 1 é usada pelo Linux para gerenciar o espaço de endereço virtual fornecido a cada processo de espaço do usuário e ao próprio espaço de endereço virtual.

A MMU de estágio 2 é controlada pelo EL2 e permite a aplicação de uma segunda tradução de endereço no endereço de saída da MMU de estágio 1, resultando em um endereço físico (PA). A tradução no estágio 2 pode ser usada por hipervisores para controlar e traduzir acessos à memória de todas as VMs convidadas. Conforme mostrado na figura 2, quando os dois estágios de conversão estão ativados, o endereço de saída do estágio 1 é chamado de endereço físico intermediário (IPA). Observação: o endereço virtual (VA) é convertido em um IPA e depois em um PA.

Proteção contra acesso à memória da CPU

Figura 2. Proteção de acesso à memória da CPU

Historicamente, o KVM é executado com a tradução do estágio 2 ativada ao executar convidados e com o estágio 2 desativado ao executar o kernel Linux do host. Essa arquitetura permite que os acessos à memória da MMU de estágio 1 do host passem pela MMU de estágio 2, permitindo o acesso irrestrito do host às páginas de memória do convidado. Por outro lado, a pKVM ativa a proteção do estágio 2 mesmo no contexto do host e coloca o hipervisor responsável por proteger as páginas da memória do convidado em vez do host.

O KVM usa totalmente a tradução de endereços na fase 2 para implementar mapeamentos IPA/PA complexos para convidados, o que cria a ilusão de memória contígua para convidados, apesar da fragmentação física. No entanto, o uso da MMU de fase 2 para o host é restrito apenas ao controle de acesso. O estágio 2 do host é mapeado por identidade, garantindo que a memória contígua no espaço IPA do host seja contígua no espaço da PA. Essa arquitetura permite o uso de mapeamentos grandes na tabela de páginas e, consequentemente, reduz a pressão no buffer de reserva de tradução (TLB, na sigla em inglês). Como um mapeamento de identidade pode ser indexado por PA, a etapa 2 do host também é usada para rastrear a propriedade da página diretamente na tabela de páginas.

Proteção de acesso direto à memória (DMA)

Como descrito anteriormente, desassociar as páginas do convidado do host Linux nas tabelas de página da CPU é uma etapa necessária, mas insuficiente para proteger a memória do convidado. O pKVM também precisa se proteger contra acessos de memória feitos por dispositivos compatíveis com DMA sob o controle do kernel do host e a possibilidade de um ataque de DMA iniciado por um host malicioso. Para impedir que esse dispositivo acesse a memória do convidado, o pKVM exige hardware de unidade de gerenciamento de memória de entrada/saída (IOMMU) para cada dispositivo compatível com DMA no sistema, conforme mostrado na figura 3.

Proteção contra acesso à memória dma

Figura 3. Proteção contra acesso à memória DMA

No mínimo, o hardware do IOMMU fornece os meios de conceder e revogar o acesso de leitura/gravação de um dispositivo à memória física na granularidade da página. No entanto, esse hardware IOMMU limita o uso de dispositivos em pVMs, já que eles assumem um estágio 2 mapeado por identidade.

Para garantir o isolamento entre máquinas virtuais, as transações de memória geradas em nome de diferentes entidades precisam ser distinguíveis pela IOMMU, para que o conjunto apropriado de tabelas de páginas possa ser usado para a tradução.

Além disso, reduzir a quantidade de código específico do SoC no EL2 é uma estratégia importante para reduzir a base de computação confiável (TCB) geral do pKVM e vai contra a inclusão de drivers IOMMU no hipervisor. Para atenuar esse problema, o host em EL1 é responsável por tarefas de gerenciamento de IOMMU auxiliar, como gerenciamento de energia, inicialização e, quando apropriado, processamento de interrupções.

No entanto, colocar o host no controle do estado do dispositivo impõe requisitos adicionais à interface de programação do hardware do IOMMU para garantir que as verificações de permissão não possam ser ignoradas por outros meios, por exemplo, após uma redefinição do dispositivo.

Um IOMMU padrão e bem aceito para dispositivos Arm que torna possível o isolamento e a atribuição direta é a arquitetura da unidade de gerenciamento de memória do sistema Arm (SMMU). Essa arquitetura é a solução de referência recomendada.

Propriedade da memória

Na inicialização, toda a memória que não é do hipervisor é considerada propriedade do host e é rastreada como tal pelo hipervisor. Quando uma pVM é gerada, o host doa páginas de memória para permitir a inicialização, e o hipervisor transfere a propriedade dessas páginas do host para a pVM. Assim, o hipervisor coloca restrições de controle de acesso na tabela de páginas da fase 2 do host para impedir que ele acesse as páginas novamente, oferecendo confidencialidade ao convidado.

A comunicação entre o host e os convidados é possível graças ao compartilhamento controlado de memória entre eles. Os convidados podem compartilhar algumas das páginas com o host usando uma hiperchamada, que instrui o hipervisor a remapear essas páginas na tabela de páginas do estágio 2 do host. Da mesma forma, a comunicação do host com o TrustZone é possível por operações de compartilhamento e/ou empréstimo de memória, que são monitoradas e controladas de perto pelo pKVM usando a especificação do framework de firmware para Arm (FF-A).

Como os requisitos de memória de uma pVM podem mudar com o tempo, uma hiperchamada é fornecida, permitindo que a propriedade de páginas especificadas pertencentes ao autor da chamada seja devolvida ao host. Na prática, essa hiperchamada é usada com o protocolo de balão virtio para permitir que o VMM solicite a memória de volta da pVM e que a pVM notifique o VMM das páginas cedidas, de maneira controlada.

O hipervisor é responsável por rastrear a propriedade de todas as páginas de memória no sistema e se elas estão sendo compartilhadas ou emprestadas para outras entidades. A maior parte desse rastreamento de estado é feita usando metadados anexados às tabelas de página da fase 2 do host e dos convidados, usando bits reservados nas entradas de tabela de página (PTEs, na sigla em inglês), que, como o nome sugere, são reservadas para uso de software.

O host precisa garantir que não tente acessar páginas que foram tornadas inacessíveis pelo hipervisor. Um acesso ilegal ao host faz com que uma exceção síncrona seja injetada no host pelo hipervisor, o que pode resultar na tarefa do espaço do usuário responsável recebendo um sinal de SEGV ou na falha do kernel do host. Para evitar acessos acidentais, as páginas doadas aos convidados não são qualificadas para troca ou mesclagem pelo kernel do host.

Processamento de interrupção e timers

As interrupções são uma parte essencial da maneira como um convidado interage com dispositivos e para a comunicação entre CPUs, em que as interrupções entre processadores (IPIs) são o principal mecanismo de comunicação. O modelo KVM é delegar todo o gerenciamento de interrupção virtual ao host no EL1, que, para essa finalidade, se comporta como uma parte não confiável do hipervisor.

O pKVM oferece uma emulação completa do Generic Interrupt Controller versão 3 (GICv3) com base no código KVM atual. O timer e os IPIs são processados como parte desse código de emulação não confiável.

Suporte ao GICv3

A interface entre EL1 e EL2 precisa garantir que o estado de interrupção completo esteja visível para o host EL1, incluindo cópias dos registros do hipervisor relacionados a interrupções. Essa visibilidade geralmente é alcançada usando regiões de memória compartilhadas, uma por CPU virtual (vCPU).

O código de suporte ao ambiente de execução do registro do sistema pode ser simplificado para oferecer suporte apenas ao registro de interrupção gerado por software (SGIR, na sigla em inglês) e à captura de registro de desativação de interrupção (DIR, na sigla em inglês). A arquitetura exige que esses registros sempre capturem o EL2, enquanto as outras armadilhas só foram úteis para mitigar errata até agora. Todo o resto é processado no hardware.

No MMIO, tudo é emulado no EL1, reutilizando toda a infraestrutura atual no KVM. Por fim, o Wait for Interrupt (WFI) é sempre transmitido para EL1, porque essa é uma das primitivas de programação básicas usadas pelo KVM.

Suporte a timer

O valor do comparador para o timer virtual precisa ser exposto ao EL1 em cada WFI de interrupção para que o EL1 possa injetar interrupções de timer enquanto a vCPU está bloqueada. O timer físico é totalmente emulado, e todos os armadilhas são retransmitidos para EL1.

Processamento de MMIO

Para se comunicar com o monitor de máquina virtual (VMM) e executar a emulação do GIC, as armadilhas MMIO precisam ser retransmitidas de volta ao host no EL1 para triagem adicional. O pKVM requer o seguinte:

  • IPA e tamanho do acesso
  • Dados em caso de gravação
  • Endianidade da CPU no ponto de captura

Além disso, as armadilhas com um registro de propósito geral (GPR) como fonte/destino são retransmitidas usando um pseudoregistro de transferência abstrato.

Interfaces para convidados

Um convidado pode se comunicar com um convidado protegido usando uma combinação de hiperchamadas e acesso de memória a regiões capturadas. As chamadas hiperconectadas são expostas de acordo com o padrão SMCCC, com um intervalo reservado para uma alocação do fornecedor pelo KVM. As hiperchamadas a seguir são de particular importância para convidados de pKVM.

Hiperchamadas genéricas

  • O PSCI fornece um mecanismo padrão para o convidado controlar o ciclo de vida das vCPUs, incluindo ativação, desativação e desligamento do sistema.
  • O TRNG fornece um mecanismo padrão para que o convidado solicite entropia do pKVM, que retransmite a chamada para o EL3. Esse mecanismo é particularmente útil quando não é possível confiar no host para virtualizar um gerador de números aleatórios de hardware (RNG).

Hiperchamadas pKVM

  • Compartilhamento de memória com o host. Inicialmente, toda a memória do convidado é inacessível para o host, mas o acesso ao host é necessário para a comunicação de memória compartilhada e para dispositivos paravirtualizados que dependem de buffers compartilhados. As chamadas hipervisuais para compartilhar e cancelar o compartilhamento de páginas com o host permitem que o convidado decida exatamente quais partes da memória são acessadas pelo restante do Android sem a necessidade de uma negociação.
  • Renúncia de memória para o host. Toda a memória do convidado geralmente pertence a ele até ser destruída. Esse estado pode ser inadequado para VMs de longa duração com requisitos de memória que variam ao longo do tempo. A hiperchamada relinquish permite que um convidado transfira explicitamente a propriedade das páginas de volta ao host sem exigir o encerramento do convidado.
  • Armadilha de acesso à memória para o host. Tradicionalmente, se um convidado do KVM acessa um endereço que não corresponde a uma região de memória válida, a linha de execução da vCPU é encerrada no host, e o acesso é normalmente usado para MMIO e emulado pelo VMM no espaço do usuário. Para facilitar esse processamento, o pKVM precisa anunciar detalhes sobre a instrução com falha, como o endereço, os parâmetros de registro e, possivelmente, o conteúdo de volta ao host, o que pode expor dados sensíveis de um convidado protegido se a captura não for antecipada. O pKVM resolve esse problema tratando essas falhas como fatais, a menos que o convidado tenha emitido uma hiperchamada para identificar o intervalo de IPA com falha como um para o qual os acessos são permitidos para capturar de volta ao host. Essa solução é chamada de MMIO guard.

Dispositivo de E/S virtual (virtio)

O Virtio é um padrão conhecido, portátil e maduro para implementar e interagir com dispositivos paravirtualizados. A maioria dos dispositivos expostos a usuários protegidos é implementada usando o virtio. O Virtio também é a base da implementação do vsock usada para comunicação entre um convidado protegido e o restante do Android.

Os dispositivos Virtio normalmente são implementados no espaço do usuário do host pelo VMM, que intercepta os acessos à memória presa do convidado para a interface MMIO do dispositivo virtio e emula o comportamento esperado. O acesso MMIO é relativamente caro porque cada acesso ao dispositivo requer uma ida e volta ao VMM e vice-versa. Portanto, a maior parte da transferência de dados real entre o dispositivo e o convidado ocorre usando um conjunto de virtqueues na memória. Uma suposição importante do Virtio é que o host pode acessar a memória do convidado de forma arbitrária. Essa suposição é evidente no design da virtqueue, que pode conter ponteiros para buffers no convidado que a emulação do dispositivo pretende acessar diretamente.

Embora as hiperchamadas de compartilhamento de memória descritas anteriormente possam ser usadas para compartilhar buffers de dados virtio do convidado para o host, esse compartilhamento é necessariamente realizado na granularidade da página e pode acabar expondo mais dados do que o necessário se o tamanho do buffer for menor que o de uma página. Em vez disso, o convidado é configurado para alocar as virtqueues e os buffers de dados correspondentes de uma janela fixa de memória compartilhada, com os dados sendo copiados (rejeitados) para a janela conforme necessário.

Dispositivo virtual

Figura 4. dispositivo Virtio

Interação com o TrustZone

Embora os convidados não possam interagir diretamente com o TrustZone, o host ainda precisa emitir chamadas de SMC no mundo seguro. Essas chamadas podem especificar buffers de memória endereçados fisicamente que estão inacessíveis ao host. Como o software seguro geralmente não tem conhecimento da acessibilidade do buffer, um host malicioso pode usar esse buffer para realizar um ataque de confusão (análogo a um ataque de DMA). Para evitar esses ataques, o pKVM captura todas as chamadas de SMC do host para o EL2 e atua como um proxy entre o host e o monitor seguro no EL3.

As chamadas PSCI do host são encaminhadas para o firmware do EL3 com modificações mínimas. Especificamente, o ponto de entrada de uma CPU que está on-line ou retomando a suspensão é reescrito para que a tabela de páginas do estágio 2 seja instalada no EL2 antes de retornar ao host no EL1. Durante a inicialização, essa proteção é aplicada por pKVM.

Essa arquitetura depende do SoC compatível com o PSCI, de preferência pelo uso de uma versão atualizada do TF-A como firmware do EL3.

O Framework de firmware para Arm (FF-A) padroniza as interações entre os mundos normais e seguros, principalmente na presença de um hipervisor seguro. Uma parte importante da especificação define um mecanismo para compartilhar memória com o mundo seguro, usando um formato de mensagem comum e um modelo de permissões bem definido para as páginas subjacentes. O pKVM encaminha mensagens FF-A para garantir que o host não tente compartilhar memória com o lado seguro para o qual ele não tem permissões suficientes.

Essa arquitetura depende do software de mundo seguro que aplica o modelo de acesso à memória, para garantir que apps confiáveis e qualquer outro software em execução no mundo seguro possam acessar a memória somente se ela for exclusivamente de propriedade do mundo seguro ou se tiver sido explicitamente compartilhada com ela usando FF-A. Em um sistema com S-EL2, a aplicação do modelo de acesso à memória precisa ser feita por um núcleo do gerenciador de partição seguro (SPMC, na sigla em inglês), como Hafnium, que mantém as tabelas de páginas do estágio 2 para o mundo seguro. Em um sistema sem S-EL2, o TEE pode aplicar um modelo de acesso à memória usando as tabelas de página do estágio 1.

Se a chamada de SMC para EL2 não for uma chamada PSCI ou uma mensagem definida pelo FF-A, as SMCs não processadas serão encaminhadas para EL3. A suposição é que o firmware seguro (necessariamente confiável) pode processar SMCs não tratadas com segurança, porque ele entende as precauções necessárias para manter o isolamento de pVM.

Monitor de máquina virtual

O crosvm é um monitor de máquina virtual (VMM) que executa máquinas virtuais pela interface KVM do Linux. O que torna a crosvm única é o foco em segurança com o uso da linguagem de programação Rust e um sandbox para dispositivos virtuais para proteger o kernel do host. Para saber mais sobre o crosvm, consulte a documentação oficial neste link.

Descritores de arquivos e ioctls

A KVM expõe o dispositivo de caractere /dev/kvm ao espaço do usuário com ioctls que compõem a API KVM. Os ioctls pertencem às seguintes categorias:

  • Os ioctls do sistema consultam e definem atributos globais que afetam todo o subsistema KVM e criam pVMs.
  • Os ioctls da VM consultam e definem atributos que criam CPUs e dispositivos virtuais e afetam uma pVM inteira, como incluir o layout de memória e o número de CPUs e dispositivos virtuais.
  • Os ioctls de vCPU consultam e definem atributos que controlam a operação de uma única CPU virtual.
  • O ioctls do dispositivo consulta e define atributos que controlam a operação de um único dispositivo virtual.

Cada processo crosvm executa exatamente uma instância de uma máquina virtual. Esse processo usa o ioctl do sistema KVM_CREATE_VM para criar um descritor de arquivo de VM que pode ser usado para emitir ioctls de pVM. Um ioctl KVM_CREATE_VCPU ou KVM_CREATE_DEVICE em um FD de VM cria uma vCPU/dispositivo e retorna um descritor de arquivo que aponta para o novo recurso. ioctls em uma vCPU ou FD de dispositivo podem ser usados para controlar o dispositivo que foi criado usando o ioctl em um FD de VM. Para vCPUs, isso inclui a importante tarefa de executar o código do convidado.

Internamente, o crosvm registra os descritores de arquivo da VM com o kernel usando a interface epoll acionada por borda. Em seguida, o kernel notifica a crosvm sempre que há um novo evento pendente em qualquer um dos descritores do arquivo.

O pKVM adiciona um novo recurso, KVM_CAP_ARM_PROTECTED_VM, que pode ser usado para receber informações sobre o ambiente de pVM e configurar o modo protegido para uma VM. O crosvm usa isso durante a criação de pVM se a flag --protected-vm for transmitida, para consultar e reservar a quantidade adequada de memória para o firmware do pVM e, em seguida, ativar o modo protegido.

Alocação de memória

Uma das principais responsabilidades de um VMM é alocar a memória da VM e gerenciar o layout de memória. O crosvm gera um layout de memória fixo descrito de forma vaga na tabela abaixo.

FDT no modo normal PHYS_MEMORY_END - 0x200000
Lib espaço ...
Ramdisk ALIGN_UP(KERNEL_END, 0x1000000)
Kernel 0x80080000
Carregador de inicialização 0x80200000
FDT no modo BIOS 0x80000000
Base de memória física 0x80000000
firmware do pVM 0x7FE00000
Memória do dispositivo 0x10000 - 0x40000000

A memória física é alocada com mmap e é doada à VM para preencher as regiões de memória, denominadas memslots, com o ioctl KVM_SET_USER_MEMORY_REGION. Toda a memória da pVM do convidado é atribuída à instância crosvm que a gerencia e pode resultar na eliminação do processo (encerramento da VM) se o host começar a ficar sem memória livre. Quando uma VM é interrompida, a memória é apagada automaticamente pelo hipervisor e retornada ao kernel do host.

No KVM regular, o VMM mantém o acesso a toda a memória do convidado. Com o pKVM, a memória do convidado é desmapeada do espaço de endereço físico do host quando é doada ao convidado. A única exceção é a memória compartilhada explicitamente pelo convidado, como em dispositivos virtio.

As regiões MMIO no espaço de endereço do convidado não são mapeadas. O acesso a essas regiões pelo convidado é bloqueado e resulta em um evento de E/S no FD da VM. Esse mecanismo é usado para implementar dispositivos virtuais. No modo protegido, o convidado precisa reconhecer que uma região do espaço de endereço dele é usada para MMIO usando uma hiperchamada para reduzir o risco de vazamento acidental de informações.

Programação

Cada CPU virtual é representada por uma linha de execução POSIX e programada pelo programador host do Linux. A linha de execução chama o ioctl KVM_RUN no FD da vCPU, resultando na troca do hipervisor para o contexto da vCPU do convidado. O programador do host considera o tempo gasto em um contexto de convidado como o tempo usado pela linha de execução da vCPU correspondente. KVM_RUN é retornado quando há um evento que precisa ser processado pelo VMM, como E/S, fim da interrupção ou vCPU interrompida. O VMM processa o evento e chama KVM_RUN novamente.

Durante KVM_RUN, a linha de execução continua sendo preemptível pelo programador do host, exceto para a execução do código do hipervisor EL2, que não é preemptível. A pVM convidada não tem um mecanismo para controlar esse comportamento.

Como todas as linhas de execução da vCPU são programadas como qualquer outra tarefa do espaço do usuário, elas estão sujeitas a todos os mecanismos de QoS padrão. Especificamente, cada linha de execução de vCPU pode ser afinada para CPUs físicas, colocada em cpusets, acelerada ou limitada usando a fixação de utilização, ter a política de prioridade/programação alterada e muito mais.

Dispositivos virtuais

O crosvm oferece suporte a vários dispositivos, incluindo:

  • virtio-blk para imagens de disco compostas, somente leitura ou leitura-gravação
  • vhost-vsock para comunicação com o host
  • virtio-pci como transporte virtio
  • pl030 relógio em tempo real (RTC)
  • UART 16550a para comunicação serial

Firmware da pVM

O firmware da pVM (pvmfw) é o primeiro código executado por uma pVM, semelhante à ROM de inicialização de um dispositivo físico. O objetivo principal do pvmfw é inicializar o boot seguro e derivar o segredo exclusivo da pVM. O pvmfw não é limitado ao uso com um SO específico, como o Microdroid, desde que o SO seja compatível com o crosvm e tenha sido assinado corretamente.

O binário pvmfw é armazenado em uma partição flash com o mesmo nome e é atualizado usando OTA.

Inicialização do dispositivo

A sequência de etapas a seguir é adicionada ao procedimento de inicialização de um dispositivo habilitado para pKVM:

  1. O carregador de inicialização do Android (ABL) carrega o pvmfw da partição para a memória e verifica a imagem.
  2. A ABL recebe as chaves secretas do Mecanismo de composição do identificador do dispositivo (DICE, na sigla em inglês), identificadores de dispositivos compostos (CDIs, na sigla em inglês) e cadeia de certificados DICE, de uma raiz de confiança.
  3. O ABL deriva os CDIs necessários para pvmfw e os anexa ao binário pvmfw.
  4. O ABL adiciona um nó de região de memória reservada linux,pkvm-guest-firmware-memory ao DT, descrevendo a localização e o tamanho do binário pvmfw e os segredos derivados na etapa anterior.
  5. O ABL transfere o controle para o Linux, que inicializa o pKVM.
  6. O pKVM desmapeia a região de memória do pvmfw das tabelas de página de estágio 2 do host e a protege do host (e convidados) durante o tempo de atividade do dispositivo.

Após a inicialização do dispositivo, o Microdroid é inicializado de acordo com as etapas na seção Sequência de inicialização do documento Microdroid.

inicialização de pVM

Ao criar uma pVM, o crosvm (ou outro VMM) precisa criar um slot de memória grande o suficiente para ser preenchido com a imagem pvmfw pelo hipervisor. O VMM também é restringido na lista de registros cujo valor inicial pode ser definido (x0-x14 para a vCPU principal, nenhum para vCPUs secundárias). Os registros restantes são reservados e fazem parte da ABI hypervisor-pvmfw.

Quando a pVM é executada, o hipervisor transfere o controle da vCPU primária para o pvmfw. O firmware espera que o crosvm tenha carregado um kernel assinado pelo AVB, que pode ser um carregador de inicialização ou qualquer outra imagem, e um FDT não assinado para a memória em deslocamentos conhecidos. O pvmfw valida a assinatura AVB e, se bem-sucedido, gera uma árvore de dispositivos confiáveis do FDT recebido, exclui os segredos da memória e ramifica para o ponto de entrada do payload. Se uma das etapas de verificação falhar, o firmware emite uma hiperchamada SYSTEM_RESET do PSCI.

Entre as inicializações, as informações sobre a instância pVM são armazenadas em uma partição (dispositivo virtio-blk) e criptografadas com o secret do pvmfw para garantir que, após uma reinicialização, o secret seja provisionado para a instância correta.