Android предоставляет эталонную реализацию всех компонентов, необходимых для реализации платформы виртуализации Android. В настоящее время эта реализация ограничена ARM64. На этой странице объясняется архитектура платформы.
Фон
Архитектура Arm допускает до четырех уровней исключений, при этом уровень исключения 0 (EL0) является наименее привилегированным, а уровень исключения 3 (EL3) — максимальным. Большая часть кодовой базы Android (все компоненты пользовательского пространства) работает на EL0. Остальная часть того, что обычно называют «Android», — это ядро Linux, работающее на уровне EL1.
Уровень EL2 позволяет внедрить гипервизор, который позволяет изолировать память и устройства в отдельных pVM на уровне EL1/EL0 с строгими гарантиями конфиденциальности и целостности.
Гипервизор
Защищенная виртуальная машина на основе ядра (pKVM) построена на основе гипервизора Linux KVM , который был расширен возможностью ограничения доступа к полезным нагрузкам, выполняемым на гостевых виртуальных машинах, помеченных как «защищенные» во время создания.
KVM/arm64 поддерживает различные режимы выполнения в зависимости от доступности определенных функций ЦП, а именно расширений хоста виртуализации (VHE) (ARMv8.1 и более поздних версий). В одном из этих режимов, широко известном как режим без VHE, код гипервизора отделяется от образа ядра во время загрузки и устанавливается в EL2, тогда как само ядро работает в EL1. Компонент EL2 KVM является частью кодовой базы Linux, но представляет собой небольшой компонент, отвечающий за переключение между несколькими EL1. Компонент гипервизора скомпилирован с Linux, но находится в отдельном выделенном разделе памяти образа vmlinux
. pKVM использует эту конструкцию, расширяя код гипервизора новыми функциями, позволяющими налагать ограничения на ядро хоста Android и пользовательское пространство, а также ограничивать доступ хоста к гостевой памяти и гипервизору.
Модули поставщиков pKVM
Модуль поставщика pKVM — это аппаратный модуль, содержащий специфичные для устройства функции, такие как драйверы блока управления памятью ввода-вывода (IOMMU). Эти модули позволяют переносить функции безопасности, требующие доступа уровня исключения 2 (EL2), в pKVM.
Чтобы узнать, как реализовать и загрузить модуль поставщика pKVM, обратитесь к разделу «Реализация модуля поставщика pKVM» .
Процедура загрузки
На следующем рисунке показана процедура загрузки pKVM:
- Загрузчик входит в общее ядро на уровне EL2.
- Общее ядро обнаруживает, что оно работает на уровне EL2, и лишает себя права доступа к EL1, в то время как pKVM и его модули продолжают работать на уровне EL2. Кроме того, в это время загружаются модули поставщика pKVM.
- Обычное ядро загружается нормально, загружая все необходимые драйверы устройств, пока не достигнет пользовательского пространства. На данный момент pKVM уже установлен и обрабатывает таблицы страниц этапа 2.
Процедура загрузки доверяет загрузчику поддерживать целостность образа ядра только во время ранней загрузки. Когда ядро лишено привилегий, оно больше не считается доверенным гипервизором, который затем несет ответственность за свою защиту, даже если ядро скомпрометировано.
Наличие ядра Android и гипервизора в одном двоичном образе обеспечивает очень тесно связанный интерфейс связи между ними. Такая тесная связь гарантирует атомарные обновления двух компонентов, что позволяет избежать необходимости поддерживать стабильность интерфейса между ними и обеспечивает большую гибкость без ущерба для долговременной ремонтопригодности. Тесная связь также позволяет оптимизировать производительность, когда оба компонента могут взаимодействовать, не влияя на гарантии безопасности, предоставляемые гипервизором.
Более того, внедрение GKI в экосистему Android автоматически позволяет развертывать гипервизор pKVM на устройствах Android в том же двоичном виде, что и ядро.
Защита доступа к памяти процессора
Архитектура Arm определяет блок управления памятью (MMU), разделенный на два независимых этапа, оба из которых могут использоваться для реализации трансляции адресов и контроля доступа к различным частям памяти. MMU этапа 1 управляется EL1 и обеспечивает первый уровень трансляции адресов. MMU этапа 1 используется Linux для управления виртуальным адресным пространством, предоставляемым каждому процессу пользовательского пространства, а также его собственному виртуальному адресному пространству.
MMU этапа 2 управляется EL2 и позволяет применить вторую трансляцию адреса к выходному адресу MMU этапа 1, в результате чего получается физический адрес (PA). Трансляция этапа 2 может использоваться гипервизорами для управления и преобразования доступа к памяти со всех гостевых виртуальных машин. Как показано на рисунке 2, когда включены оба этапа трансляции, выходной адрес этапа 1 называется промежуточным физическим адресом (IPA). Примечание. Виртуальный адрес (VA) транслируется в IPA, а затем в PA.
Исторически сложилось так, что KVM работает с включенной трансляцией этапа 2 при работе гостей и с отключением этапа 2 при работе ядра Linux хоста. Эта архитектура позволяет доступу к памяти из MMU этапа 1 хоста проходить через MMU этапа 2, что обеспечивает неограниченный доступ хоста к страницам гостевой памяти. С другой стороны, pKVM обеспечивает защиту второго уровня даже в контексте хоста и возлагает ответственность за защиту страниц гостевой памяти на гипервизор, а не на хост.
KVM в полной мере использует трансляцию адресов на этапе 2 для реализации сложных сопоставлений IPA/PA для гостей, что создает для гостей иллюзию непрерывной памяти, несмотря на физическую фрагментацию. Однако использование MMU этапа 2 для хоста ограничивается только контролем доступа. Стадия хоста 2 сопоставлена с идентификаторами, гарантируя, что непрерывная память в пространстве IPA хоста является смежной в пространстве PA. Эта архитектура позволяет использовать большие сопоставления в таблице страниц и, следовательно, снижает нагрузку на резервный буфер трансляции (TLB). Поскольку сопоставление идентификаторов может быть проиндексировано PA, этап хоста 2 также используется для отслеживания владения страницей непосредственно в таблице страниц.
Защита прямого доступа к памяти (DMA)
Как описано ранее, отключение гостевых страниц хоста Linux в таблицах страниц ЦП является необходимым, но недостаточным шагом для защиты гостевой памяти. pKVM также необходимо защищать от доступа к памяти со стороны устройств с поддержкой DMA, находящихся под контролем ядра хоста, а также от возможности атаки DMA, инициированной злонамеренным хостом. Чтобы предотвратить доступ такого устройства к гостевой памяти, pKVM требует аппаратного обеспечения блока управления памятью ввода-вывода (IOMMU) для каждого устройства с поддержкой DMA в системе, как показано на рисунке 3.
Как минимум, аппаратное обеспечение IOMMU предоставляет средства предоставления и отзыва доступа устройства к физической памяти на чтение/запись на уровне детализации страниц. Однако это оборудование IOMMU ограничивает использование устройств в pVM, поскольку они предполагают этап 2 с сопоставлением идентификаторов.
Чтобы обеспечить изоляцию между виртуальными машинами, транзакции памяти, генерируемые от имени разных объектов, должны быть различимы IOMMU, чтобы для трансляции можно было использовать соответствующий набор таблиц страниц.
Кроме того, сокращение объема кода, специфичного для SoC, на EL2 является ключевой стратегией сокращения общей базы доверенных вычислений (TCB) pKVM и противоречит включению драйверов IOMMU в гипервизор. Чтобы смягчить эту проблему, хост EL1 отвечает за вспомогательные задачи управления IOMMU, такие как управление питанием, инициализация и, при необходимости, обработка прерываний.
Однако предоставление хосту контроля над состоянием устройства предъявляет дополнительные требования к программному интерфейсу оборудования IOMMU, чтобы гарантировать, что проверки разрешений нельзя будет обойти другими способами, например, после перезагрузки устройства.
Стандартным и хорошо поддерживаемым IOMMU для устройств Arm, который делает возможной как изоляцию, так и прямое назначение, является архитектура Arm System Memory Management Unit (SMMU). Эта архитектура является рекомендуемым эталонным решением.
Владение памятью
Во время загрузки предполагается, что вся память, не относящаяся к гипервизору, принадлежит хосту и отслеживается как таковая гипервизором. Когда создается pVM, хост жертвует страницы памяти, чтобы позволить ему загружаться, а гипервизор передает право владения этими страницами от хоста к pVM. Таким образом, гипервизор вводит ограничения контроля доступа в таблицу страниц второго уровня хоста, чтобы предотвратить повторный доступ к страницам, обеспечивая конфиденциальность гостю.
Общение между хозяином и гостями становится возможным благодаря контролируемому совместному использованию памяти между ними. Гостям разрешено делиться некоторыми из своих страниц с хостом с помощью гипервызова, который инструктирует гипервизор пересопоставить эти страницы в таблице страниц второго этапа хоста. Аналогично, связь хоста с TrustZone становится возможной благодаря операциям совместного использования и/или предоставления памяти, все из которых тщательно отслеживаются и контролируются pKVM с использованием спецификации Firmware Framework for Arm (FF-A) .
Поскольку требования к памяти pVM могут со временем меняться, предоставляется гипервызов, который позволяет вернуть право владения указанными страницами, принадлежащими вызывающей стороне, обратно хосту. На практике этот гипервызов используется с протоколом воздушного шара virtio, чтобы позволить VMM запрашивать обратно память у pVM, а также чтобы pVM уведомляла VMM об освобожденных страницах контролируемым образом.
Гипервизор отвечает за отслеживание владения всеми страницами памяти в системе и за то, используются ли они совместно или предоставляются в аренду другим объектам. Большая часть этого отслеживания состояния выполняется с использованием метаданных, прикрепленных к таблицам страниц узла и гостя на этапе 2, с использованием зарезервированных битов в записях таблицы страниц (PTE), которые, как следует из их названия, зарезервированы для использования программным обеспечением.
Хост должен гарантировать, что он не пытается получить доступ к страницам, которые гипервизор сделал недоступными. Незаконный доступ к хосту приводит к тому, что гипервизор вводит синхронное исключение в хост, что может привести либо к тому, что ответственная задача пользовательского пространства получит сигнал SEGV, либо к сбою ядра хоста. Чтобы предотвратить случайный доступ, страницы, переданные гостям, не подлежат обмену или объединению ядром хоста.
Обработка прерываний и таймеры
Прерывания являются важной частью взаимодействия гостя с устройствами и связи между процессорами, где межпроцессорные прерывания (IPI) являются основным механизмом связи. Модель KVM заключается в делегировании всего управления виртуальными прерываниями хосту в EL1, который для этой цели ведет себя как недоверенная часть гипервизора.
pKVM предлагает полную эмуляцию универсального контроллера прерываний версии 3 (GICv3), основанную на существующем коде KVM. Таймер и IPI обрабатываются как часть этого ненадежного кода эмуляции.
Поддержка GICv3
Интерфейс между EL1 и EL2 должен обеспечивать видимость полного состояния прерывания для хоста EL1, включая копии регистров гипервизора, связанных с прерываниями. Эта видимость обычно достигается с помощью областей общей памяти, по одной на каждый виртуальный ЦП (vCPU).
Код поддержки системного регистра во время выполнения можно упростить, чтобы поддерживать только перехват регистра программно-генерируемых прерываний (SGIR) и регистра деактивации прерываний (DIR). Архитектура требует, чтобы эти регистры всегда перехватывали EL2, в то время как другие ловушки до сих пор были полезны только для устранения ошибок. Все остальное решается аппаратно.
Со стороны MMIO все эмулируется на EL1, повторно используя всю текущую инфраструктуру KVM. Наконец, ожидание прерывания (WFI) всегда передается на EL1, поскольку это один из основных примитивов планирования, используемых KVM.
Поддержка таймера
Значение компаратора для виртуального таймера должно быть предоставлено EL1 на каждом перехватывающем WFI, чтобы EL1 мог вводить прерывания таймера, пока виртуальный ЦП заблокирован. Физический таймер полностью эмулируется, и все прерывания передаются на EL1.
Обработка MMIO
Для связи с монитором виртуальной машины (VMM) и выполнения эмуляции GIC ловушки MMIO должны быть ретранслированы обратно на хост в EL1 для дальнейшей сортировки. pKVM требует следующего:
- IPA и размер доступа
- Данные в случае записи
- Порядок байтов процессора в момент захвата
Кроме того, ловушки с регистром общего назначения (GPR) в качестве источника/назначения передаются с использованием абстрактного псевдорегистра передачи.
Гостевые интерфейсы
Гость может общаться с защищенным гостем, используя комбинацию гипервызовов и доступа к памяти к захваченным областям. Гипервызовы предоставляются в соответствии со стандартом SMCCC , при этом диапазон зарезервирован для распределения поставщиком KVM. Следующие гипервызовы имеют особое значение для гостей pKVM.
Общие гипервызовы
- PSCI предоставляет гостю стандартный механизм управления жизненным циклом своих виртуальных ЦП, включая подключение к сети, автономный режим и завершение работы системы.
- TRNG предоставляет гостю стандартный механизм запроса энтропии у pKVM, который передает вызов на EL3. Этот механизм особенно полезен, когда хосту нельзя доверить виртуализацию аппаратного генератора случайных чисел (ГСЧ).
Гипервызовы pKVM
- Разделение памяти с хостом. Вся гостевая память изначально недоступна хосту, но доступ к хосту необходим для связи с общей памятью и для паравиртуализированных устройств, которые полагаются на общие буферы. Гипервызовы для совместного использования и отмены совместного использования страниц с хостом позволяют гостю точно решать, какие части памяти доступны остальной части Android без необходимости рукопожатия.
- Освобождение памяти хосту. Вся гостевая память обычно принадлежит гостю, пока она не будет уничтожена. Это состояние может быть недостаточным для долгоживущих виртуальных машин, требования к памяти которых меняются со временем. Гипервызов
relinquish
позволяет гостю явно передать право собственности на страницы обратно хосту, не требуя завершения гостевого доступа. - Перехват доступа к памяти хосту. Традиционно, если гость KVM обращается к адресу, который не соответствует допустимой области памяти, поток виртуального ЦП выходит на хост, и доступ обычно используется для MMIO и эмулируется VMM в пространстве пользователя. Чтобы облегчить эту обработку, pKVM должен сообщать подробности о сбойной инструкции, такие как ее адрес, параметры регистра и, возможно, их содержимое, обратно на хост, что может непреднамеренно раскрыть конфиденциальные данные защищенного гостя, если ловушка не была ожидаема. pKVM решает эту проблему, рассматривая эти ошибки как фатальные, если только гость ранее не выполнил гипервызов для идентификации сбойного диапазона IPA как диапазона, для которого разрешено обращение к хосту. Это решение называется защитой MMIO .
Виртуальное устройство ввода-вывода (virtio)
Virtio — популярный, портативный и зрелый стандарт для реализации паравиртуализированных устройств и взаимодействия с ними. Большинство устройств, доступных для защищенных гостей, реализованы с использованием virtio. Virtio также лежит в основе реализации vsock, используемой для связи между защищенным гостем и остальной частью Android.
Устройства Virtio обычно реализуются в пользовательском пространстве хоста с помощью VMM, который перехватывает доступ к памяти от гостя к интерфейсу MMIO устройства Virtio и эмулирует ожидаемое поведение. Доступ к MMIO является относительно дорогим, поскольку каждый доступ к устройству требует обратного пути к VMM и обратно, поэтому большая часть фактической передачи данных между устройством и гостем происходит с использованием набора виртуальных очередей в памяти. Ключевое предположение virtio заключается в том, что хост может произвольно обращаться к гостевой памяти. Это предположение очевидно при проектировании очереди виртуализации, которая может содержать указатели на буферы в гостевой системе, к которым эмуляция устройства должна иметь прямой доступ.
Хотя ранее описанные гипервызовы совместного использования памяти могут использоваться для совместного использования буферов данных virtio от гостя к хосту, это совместное использование обязательно выполняется с детализацией страницы и может в конечном итоге раскрыть больше данных, чем требуется, если размер буфера меньше размера страницы. Вместо этого гость настраивается на выделение как виртуальных очередей, так и соответствующих им буферов данных из фиксированного окна общей памяти, при этом данные копируются (передаются) в окно и из него по мере необходимости.
Взаимодействие с TrustZone
Хотя гости не могут напрямую взаимодействовать с TrustZone, хост все равно должен иметь возможность отправлять вызовы SMC в безопасный мир. Эти вызовы могут указывать физически адресуемые буферы памяти, недоступные хосту. Поскольку защищенное программное обеспечение обычно не знает о доступности буфера, злонамеренный хост может использовать этот буфер для выполнения атаки с запутанным заместителем (аналогично атаке DMA). Чтобы предотвратить такие атаки, pKVM перехватывает все вызовы SMC хоста на EL2 и действует как прокси-сервер между хостом и безопасным монитором на EL3.
Вызовы PSCI от хоста перенаправляются на прошивку EL3 с минимальными изменениями. В частности, точка входа для ЦП, подключающегося к сети или возобновляющего режим ожидания, перезаписывается таким образом, что таблица страниц этапа 2 устанавливается в EL2 перед возвратом на хост в EL1. Во время загрузки эта защита обеспечивается pKVM.
Эта архитектура основана на SoC, поддерживающем PSCI, предпочтительно за счет использования актуальной версии TF-A в качестве прошивки EL3.
Firmware Framework for Arm (FF-A) стандартизирует взаимодействие между обычным и безопасным мирами, особенно при наличии безопасного гипервизора. Основная часть спецификации определяет механизм совместного использования памяти с безопасным миром, используя как общий формат сообщений, так и четко определенную модель разрешений для базовых страниц. pKVM проксирует сообщения FF-A, чтобы гарантировать, что хост не пытается использовать память совместно с защищенной стороной, для которой у него нет достаточных разрешений.
Эта архитектура опирается на программное обеспечение безопасного мира, обеспечивающее модель доступа к памяти, чтобы гарантировать, что доверенные приложения и любое другое программное обеспечение, работающее в безопасном мире, могут получить доступ к памяти, только если она либо принадлежит исключительно безопасному миру, либо была явно разделена с ним с помощью FF-A. В системе с S-EL2 обеспечение модели доступа к памяти должно выполняться ядром Secure Partition Manager (SPMC), таким как Hafnium , которое поддерживает таблицы страниц этапа 2 для безопасного мира. В системе без S-EL2 TEE может вместо этого реализовать модель доступа к памяти через свои таблицы страниц этапа 1.
Если вызов SMC на EL2 не является вызовом PSCI или сообщением, определенным FF-A, необработанные SMC перенаправляются на EL3. Предполагается, что защищенная прошивка (обязательно доверенная) может безопасно обрабатывать необработанные SMC, поскольку прошивка понимает меры предосторожности, необходимые для поддержания изоляции pVM.
Монитор виртуальной машины
crosvm — это монитор виртуальных машин (VMM), который запускает виртуальные машины через KVM-интерфейс Linux. Что делает crosvm уникальным, так это его внимание к безопасности за счет использования языка программирования Rust и «песочницы» вокруг виртуальных устройств для защиты ядра хоста. Подробнее о crosvm смотрите в его официальной документации здесь .
Дескрипторы файлов и ioctls
KVM предоставляет символьное устройство /dev/kvm
пользовательскому пространству с помощью ioctls, составляющих API KVM. ioctls относятся к следующим категориям:
- Системные ioctls запрашивают и устанавливают глобальные атрибуты, которые влияют на всю подсистему KVM, и создают pVM.
- ioctls виртуальной машины запрашивает и устанавливает атрибуты, которые создают виртуальные процессоры (vCPU) и устройства и влияют на всю pVM, например, включая структуру памяти и количество виртуальных процессоров (vCPU) и устройств.
- ioctls vCPU запрашивает и устанавливает атрибуты, которые управляют работой одного виртуального процессора.
- ioctls устройства запрашивает и устанавливает атрибуты, управляющие работой одного виртуального устройства.
Каждый процесс crosvm запускает ровно один экземпляр виртуальной машины. Этот процесс использует системный ioctl KVM_CREATE_VM
для создания дескриптора файла VM, который можно использовать для выдачи ioctl pVM. ioctl KVM_CREATE_VCPU
или KVM_CREATE_DEVICE
на VM FD создает виртуальный ЦП/устройство и возвращает дескриптор файла, указывающий на новый ресурс. ioctl на виртуальном ЦП или FD устройства можно использовать для управления устройством, созданным с помощью ioctl на FD VM. Для виртуальных ЦП это включает в себя важную задачу запуска гостевого кода.
Внутренне crosvm регистрирует файловые дескрипторы виртуальной машины в ядре, используя интерфейс epoll
, запускаемый по краям. Затем ядро уведомляет crosvm всякий раз, когда в любом из файловых дескрипторов ожидается новое событие.
pKVM добавляет новую возможность KVM_CAP_ARM_PROTECTED_VM
, которую можно использовать для получения информации о среде pVM и настройки защищенного режима для виртуальной машины. crosvm использует это во время создания pVM, если передан флаг --protected-vm
, для запроса и резервирования соответствующего объема памяти для прошивки pVM, а затем для включения защищенного режима.
Распределение памяти
Одной из основных обязанностей VMM является выделение памяти виртуальной машины и управление ее структурой. crosvm генерирует фиксированную структуру памяти, подробно описанную в таблице ниже.
ФДТ в обычном режиме | PHYS_MEMORY_END - 0x200000 |
Свободное место | ... |
Рамдиск | ALIGN_UP(KERNEL_END, 0x1000000) |
Ядро | 0x80080000 |
загрузчик | 0x80200000 |
FDT в режиме BIOS | 0x80000000 |
База физической памяти | 0x80000000 |
прошивка ПВМ | 0x7FE00000 |
Память устройства | 0x10000 - 0x40000000 |
Физическая память выделяется с помощью mmap
, и эта память передается виртуальной машине для заполнения ее областей памяти, называемых memslots , с помощью ioctl KVM_SET_USER_MEMORY_REGION
. Таким образом, вся гостевая память pVM приписывается экземпляру crosvm, который ею управляет, и может привести к остановке процесса (завершению виртуальной машины), если на хосте начинает заканчиваться свободная память. Когда виртуальная машина останавливается, память автоматически очищается гипервизором и возвращается в ядро хоста.
При использовании обычного KVM VMM сохраняет доступ ко всей гостевой памяти. При использовании pKVM гостевая память не отображается в физическом адресном пространстве хоста, когда она передается гостю. Единственным исключением является память, явно предоставленная гостем, например, для устройств Virtio.
Регионы MMIO в адресном пространстве гостя остаются несопоставленными. Доступ гостя к этим областям блокируется и приводит к событию ввода-вывода на VM FD. Этот механизм используется для реализации виртуальных устройств. В защищенном режиме гость должен подтвердить, что область его адресного пространства используется для MMIO с помощью гипервызова, чтобы снизить риск случайной утечки информации.
Планирование
Каждый виртуальный процессор представлен потоком POSIX и запланирован планировщиком хоста Linux. Поток вызывает ioctl KVM_RUN
на виртуальном ЦП FD, в результате чего гипервизор переключается на контекст гостевого виртуального ЦП. Планировщик хоста учитывает время, проведенное в гостевом контексте, как время, использованное соответствующим потоком виртуального ЦП. KVM_RUN
возвращается, когда происходит событие, которое должно быть обработано VMM, например ввод-вывод, завершение прерывания или остановка виртуального ЦП. VMM обрабатывает событие и снова вызывает KVM_RUN
.
Во время KVM_RUN
поток остается вытесняемым планировщиком узла, за исключением выполнения кода гипервизора EL2, который не является вытесняемым. Сама гостевая pVM не имеет механизма контроля такого поведения.
Поскольку все потоки виртуальных ЦП планируются так же, как и любые другие задачи пользовательского пространства, на них распространяются все стандартные механизмы QoS. В частности, каждый поток виртуальных ЦП можно привязать к физическим ЦП, поместить в наборы процессоров, увеличить или ограничить с помощью ограничения использования, изменить политику приоритетов/планирования и многое другое.
Виртуальные устройства
crosvm поддерживает ряд устройств, в том числе следующие:
- virtio-blk для составных образов дисков, только чтение или чтение-запись
- vhost-vsock для связи с хостом
- virtio-pci как транспорт virtio
- pl030 часы реального времени (RTC)
- 16550a UART для последовательной связи
прошивка ПВМ
Прошивка pVM (pvmfw) — это первый код, выполняемый pVM, аналогичный загрузочному ПЗУ физического устройства. Основная цель pvmfw — обеспечить безопасную загрузку и получить уникальный секрет pVM. pvmfw не ограничивается использованием какой-либо конкретной ОС, например Microdroid , если ОС поддерживается crosvm и правильно подписана.
Бинарный файл pvmfw хранится в одноименном флэш-разделе и обновляется с помощью OTA .
Загрузка устройства
В процедуру загрузки устройства с поддержкой pKVM добавляется следующая последовательность шагов:
- Загрузчик Android (ABL) загружает pvmfw из своего раздела в память и проверяет образ.
- ABL получает секреты механизма композиции идентификаторов устройств (DICE) (составные идентификаторы устройств (CDI) и цепочку сертификатов DICE) из корня доверия.
- ABL получает необходимые CDI для pvmfw и добавляет их в двоичный файл pvmfw.
- ABL добавляет в DT узел зарезервированной области памяти
linux,pkvm-guest-firmware-memory
, описывающий расположение и размер двоичного файла pvmfw, а также секреты, полученные им на предыдущем шаге. - ABL передает управление Linux, а Linux инициализирует pKVM.
- pKVM отменяет отображение области памяти pvmfw из таблиц страниц второго уровня хоста и защищает ее от хоста (и гостей) на протяжении всего времени безотказной работы устройства.
После загрузки устройства Microdroid загружается в соответствии с инструкциями, описанными в разделе «Последовательность загрузки» документа Microdroid .
пВМ-загрузка
При создании pVM crosvm (или другой VMM) должен создать достаточно большой слот памяти, чтобы гипервизор мог заполнить его образом pvmfw. VMM также ограничен списком регистров, начальное значение которых он может установить (x0–x14 для основного виртуального ЦП, ни одного для вторичного виртуального ЦП). Остальные регистры зарезервированы и являются частью ABI гипервизора-pvmfw.
При запуске pVM гипервизор сначала передает управление основным виртуальным ЦП pvmfw. Прошивка ожидает, что crosvm загрузит подписанное AVB ядро, которое может быть загрузчиком или любым другим образом, а также беззнаковый FDT в память по известным смещениям. pvmfw проверяет подпись AVB и в случае успеха генерирует дерево доверенных устройств на основе полученного FDT, стирает его секреты из памяти и переходит к точке входа полезной нагрузки. Если один из шагов проверки не удался, встроенное ПО выдает гипервызов PSCI SYSTEM_RESET
.
Между загрузками информация об экземпляре pVM сохраняется в разделе (устройство virtio-blk) и шифруется с помощью секрета pvmfw, чтобы гарантировать, что после перезагрузки секрет будет передан правильному экземпляру.
,Android предоставляет эталонную реализацию всех компонентов, необходимых для реализации платформы виртуализации Android. В настоящее время эта реализация ограничена ARM64. На этой странице объясняется архитектура платформы.
Фон
Архитектура Arm допускает до четырех уровней исключений, при этом уровень исключения 0 (EL0) является наименее привилегированным, а уровень исключения 3 (EL3) — максимальным. Большая часть кодовой базы Android (все компоненты пользовательского пространства) работает на EL0. Остальная часть того, что обычно называют «Android», — это ядро Linux, работающее на уровне EL1.
Уровень EL2 позволяет внедрить гипервизор, который позволяет изолировать память и устройства в отдельных pVM на уровне EL1/EL0 с строгими гарантиями конфиденциальности и целостности.
Гипервизор
Защищенная виртуальная машина на основе ядра (pKVM) построена на основе гипервизора Linux KVM , который был расширен возможностью ограничения доступа к полезным нагрузкам, выполняемым на гостевых виртуальных машинах, помеченных как «защищенные» во время создания.
KVM/arm64 поддерживает различные режимы выполнения в зависимости от доступности определенных функций ЦП, а именно расширений хоста виртуализации (VHE) (ARMv8.1 и более поздних версий). В одном из этих режимов, широко известном как режим без VHE, код гипервизора отделяется от образа ядра во время загрузки и устанавливается в EL2, тогда как само ядро работает в EL1. Компонент EL2 KVM является частью кодовой базы Linux, но представляет собой небольшой компонент, отвечающий за переключение между несколькими EL1. Компонент гипервизора скомпилирован с Linux, но находится в отдельном выделенном разделе памяти образа vmlinux
. pKVM использует эту конструкцию, расширяя код гипервизора новыми функциями, позволяющими налагать ограничения на ядро хоста Android и пользовательское пространство, а также ограничивать доступ хоста к гостевой памяти и гипервизору.
Модули поставщиков pKVM
Модуль поставщика pKVM — это аппаратный модуль, содержащий специфичные для устройства функции, такие как драйверы блока управления памятью ввода-вывода (IOMMU). Эти модули позволяют переносить функции безопасности, требующие доступа уровня исключения 2 (EL2), в pKVM.
Чтобы узнать, как реализовать и загрузить модуль поставщика pKVM, обратитесь к разделу «Реализация модуля поставщика pKVM» .
Процедура загрузки
На следующем рисунке показана процедура загрузки pKVM:
- Загрузчик входит в общее ядро на уровне EL2.
- Общее ядро обнаруживает, что оно работает на уровне EL2, и лишает себя права доступа к EL1, в то время как pKVM и его модули продолжают работать на уровне EL2. Кроме того, в это время загружаются модули поставщика pKVM.
- Обычное ядро загружается нормально, загружая все необходимые драйверы устройств, пока не достигнет пользовательского пространства. На данный момент pKVM уже установлен и обрабатывает таблицы страниц этапа 2.
Процедура загрузки доверяет загрузчику поддерживать целостность образа ядра только во время ранней загрузки. Когда ядро лишено привилегий, оно больше не считается доверенным гипервизором, который затем несет ответственность за свою защиту, даже если ядро скомпрометировано.
Наличие ядра Android и гипервизора в одном двоичном образе обеспечивает очень тесно связанный интерфейс связи между ними. Такая тесная связь гарантирует атомарные обновления двух компонентов, что позволяет избежать необходимости поддерживать стабильность интерфейса между ними и обеспечивает большую гибкость без ущерба для долговременной ремонтопригодности. Тесная связь также позволяет оптимизировать производительность, когда оба компонента могут взаимодействовать, не влияя на гарантии безопасности, предоставляемые гипервизором.
Более того, внедрение GKI в экосистему Android автоматически позволяет развертывать гипервизор pKVM на устройствах Android в том же двоичном виде, что и ядро.
Защита доступа к памяти процессора
Архитектура Arm определяет блок управления памятью (MMU), разделенный на два независимых этапа, оба из которых могут использоваться для реализации трансляции адресов и контроля доступа к различным частям памяти. MMU этапа 1 управляется EL1 и обеспечивает первый уровень трансляции адресов. MMU этапа 1 используется Linux для управления виртуальным адресным пространством, предоставляемым каждому процессу пользовательского пространства, а также его собственному виртуальному адресному пространству.
MMU этапа 2 управляется EL2 и позволяет применить вторую трансляцию адреса к выходному адресу MMU этапа 1, в результате чего получается физический адрес (PA). Трансляция этапа 2 может использоваться гипервизорами для управления и преобразования доступа к памяти со всех гостевых виртуальных машин. Как показано на рисунке 2, когда включены оба этапа трансляции, выходной адрес этапа 1 называется промежуточным физическим адресом (IPA). Примечание. Виртуальный адрес (VA) транслируется в IPA, а затем в PA.
Исторически сложилось так, что KVM работает с включенной трансляцией этапа 2 при работе гостей и с отключением этапа 2 при работе ядра Linux хоста. Эта архитектура позволяет доступу к памяти из MMU этапа 1 хоста проходить через MMU этапа 2, что обеспечивает неограниченный доступ хоста к страницам гостевой памяти. С другой стороны, pKVM обеспечивает защиту второго уровня даже в контексте хоста и возлагает ответственность за защиту страниц гостевой памяти на гипервизор, а не на хост.
KVM в полной мере использует перевод адреса на этапе 2 для реализации сложных отображений IPA/PA для гостей, что создает иллюзию смежной памяти для гостей, несмотря на физическую фрагментацию. Тем не менее, использование MMU Stage 2 для хоста ограничено только для контроля доступа. Стадия 2-го хоста отображается на идентификаторе, что обеспечивает смежную память в пространстве IPA хоста в пространстве PA. Эта архитектура позволяет использовать большие отображения в таблице страниц и, следовательно, снижает давление на буфер Translation LookAside (TLB). Поскольку сопоставление идентификации может быть проиндексировано PA, этап хоста 2 также используется для отслеживания владения страницей непосредственно в таблице страниц.
Защита прямого доступа к памяти (DMA)
Как описано ранее, невозможные гостевые страницы от хоста Linux в таблицах страниц CPU являются необходимым, но недостаточным шагом для защиты памяти гостей. PKVM также необходимо защитить от доступа к памяти, сделанных устройствами, способствующими DMA, под контролем ядра хоста, и возможность атаки DMA, инициированной злонамеренным хозяином. Чтобы предотвратить доступ к такому устройству, PKVM требует аппаратного обеспечения блока управления памятью (IOMMU) ввода-вывода (IOMMU) для каждого устройства, способного способным к DMA, в системе, как показано на рисунке 3.
Как минимум, Iommu Adware предоставляет средства предоставления и отмены доступа к чтению/записи для устройства для физической памяти на гранулярности Page. Тем не менее, это оборудование IOMMU ограничивает использование устройств в PVMS, поскольку они предполагают этап 2, отображаемая на личности.
Чтобы обеспечить изоляцию между виртуальными машинами, транзакции памяти, генерируемые от имени различных объектов, должны быть различны от iommu, чтобы для перевода можно было использовать соответствующий набор таблиц страниц.
Кроме того, сокращение объема SOC-специфического кода в EL2 является ключевой стратегией для снижения общей надежной вычислительной базы (TCB) PKVM и выполняет противодействие включению драйверов IOMMU в гипервизор. Чтобы смягчить эту проблему, хозяин в EL1 отвечает за вспомогательные задачи управления IOMMU, такие как управление питанием, инициализация и, при необходимости, обработка прерываний.
Тем не менее, размещение хоста в управление состоянием устройства ставит дополнительные требования к интерфейсу программирования аппаратного обеспечения iommu, чтобы гарантировать, что проверки разрешений не могут быть обойдены другими способами, например, после сброса устройства.
Стандартным и хорошо поддерживаемым iOmmu для устройств ARM, который делает возможным как изоляцию, так и прямое назначение, является архитектура управления памятью системы ARM (SMMU). Эта архитектура является рекомендуемым справочным решением.
Владение памятью
Во время загрузки все не-гипервизорная память предполагается, что она принадлежит хосту и отслеживается как таковая гипервизор. Когда PVM появляется, хост жертвует страницы памяти, чтобы позволить ему загружаться, а гипервизор переходит владение этими страницами от хоста на PVM. Таким образом, гипервизор устанавливает ограничения на контроль доступа в таблице страниц на стадии хоста, чтобы предотвратить его снова получить доступ к страницам, обеспечивая конфиденциальность гостю.
Связь между хостом и гостями стала возможной благодаря контролируемому обмену памятью между ними. Гостям разрешено поделиться некоторыми из своих страниц с хостом, используя гиперкал, который инструктирует гипервизора пережить эти страницы в таблице этапа 2 -го этапа. Аналогичным образом, связь хоста с TrustZone стала возможной благодаря операциям обмена памятью и/или кредитования, которые тщательно контролируются и контролируются PKVM с использованием спецификации прошивки для ARM (FF-A) .
Поскольку требования к памяти PVM могут измениться с течением времени, предоставляется гиперкал, который позволяет вернуть хост -владение указанными страницами, принадлежащими абоненту. На практике этот гиперкал используется с протоколом воздушного шара Virtio, чтобы позволить VMM запросить память обратно от PVM, и для того, чтобы PVM уведомлял VMM о отказе от страниц контролируемым образом.
Гипервизор отвечает за отслеживание владения всеми страницами памяти в системе и о том, обмениваются ли они или одарены другим организациям. Большая часть этого отслеживания состояния выполняется с использованием метаданных, прикрепленных к таблицам хоста и гостей на этапе 2, используя зарезервированные биты в записях таблицы страниц (PTE), которые, как следует из их названия, зарезервированы для использования программного обеспечения.
Хозяин должен убедиться, что он не пытается получить доступ к страницам, которые были сделаны недоступными гипервизором. Нелегальный доступ хоста приводит к тому, что синхронное исключение будет введено в хост гипервизором, что может привести к тому, что ответственная задача пользователя получает сигнал SEGV, либо сбои ядра хоста. Чтобы предотвратить случайный доступ, страницы, пожертвованные гостям, не имеют права на обмен или слияние ядром хоста.
Управление прерыванием и таймеры
Прерывания являются неотъемлемой частью того, как гость взаимодействует с устройствами и для связи между процессорами, где межпроцессорные прерывания (IPIS) являются основным механизмом связи. Модель KVM предназначена для того, чтобы делегировать все виртуальное управление прерыванием хозяину в EL1, которая для этой цели ведет себя как ненадежная часть гипервизора.
PKVM предлагает полную эмуляцию общего контроллера прерываний версии 3 (GICV3) на основе существующего кода KVM. Таймер и IPI обрабатываются как часть этого ненадежного кода эмуляции.
Поддержка GICV3
Интерфейс между EL1 и EL2 должен гарантировать, что полное состояние прерывания видно для хозяина EL1, включая копии регистров гипервизора, связанных с прерывами. Эта видимость обычно выполняется с использованием общих областей памяти, по одному на виртуальный процессор (VCPU).
Код поддержки System Register Runtime может быть упрощен для поддержки только программного обеспечения, сгенерированного регистром прерываний (SGIR) и деактивирования регистра прерываний (DIR). Архитектура обязывает, что эти регистры всегда ловят EL2, в то время как другие ловушки до сих пор были полезны только для смягчения ошибок. Все остальное обрабатывается в оборудовании.
Со стороны MMIO все эмулировано в EL1, повторно используя всю текущую инфраструктуру в KVM. Наконец, подождите, пока прерывание (WFI) всегда передается на EL1, потому что это один из основных примитивов планирования KVM.
Поддержка таймера
Значение компаратора для виртуального таймера должно подвергаться воздействию EL1 на каждом ловушке WFI, чтобы EL1 мог вводить прерывания таймера, пока VCPU блокируется. Физический таймер полностью эмулируется, и все ловушки передаются на EL1.
Обработка MMIO
Чтобы общаться с монитором виртуальной машины (VMM) и выполнить GIC -эмуляцию, ловушки MMIO должны быть переданы обратно на хост в EL1 для дальнейшего тридю. PKVM требует следующего:
- IPA и размер доступа
- Данные в случае записи
- Эндианство ЦП в точке захвата
Кроме того, ловушки с реестрой общего назначения (GPR) в качестве источника/назначения передаются с использованием абстрактного псевдо-регистра.
Гостевые интерфейсы
Гость может общаться с защищенным гостем, используя комбинацию гиперкаллов и доступа к памяти в пойманные регионы. Гиперкаллы выставлены в соответствии с стандартом SMCCC , с диапазоном, зарезервированным для распределения поставщиков по KVM. Следующие гиперкаллы имеют особое значение для гостей PKVM.
Общие гиперкаллы
- PSCI предоставляет для гостя стандартный механизм, чтобы контролировать жизненный цикл своих VCPU, включая Onlining, Offlining и отключение системы.
- TRNG предоставляет стандартный механизм для гостя для запроса энтропии у PKVM, который передает вызов EL3. Этот механизм особенно полезен, если хост нельзя доверять для виртуализации аппаратного генератора случайных чисел (RNG).
PKVM Hypercalls
- Обмен памятью с хостом. Вся память гостей изначально недоступна для хоста, но доступ к хосту необходим для общего коммуникации и для паравритуализированных устройств, которые полагаются на общие буферы. Гиперкаллы для обмена и неразличимыми страницами с хостом позволяют гостю решать, какие части памяти становятся доступными для остальной части Android без необходимости рукопожатия.
- Память отказаться от хоста. Вся память гостя обычно принадлежит гостю, пока она не будет уничтожена. Это состояние может быть неадекватным для долгоживущих виртуальных машин с требованиями к памяти, которые со временем варьируются.
relinquish
гиперкалл позволяет гостю явно перенести владение страницами обратно на хост, не требуя завершения гостя. - Получение доступа к памяти к хосту. Традиционно, если гость KVM обращается к адресу, который не соответствует действительной области памяти, то поток VCPU выходит на хост, а доступ обычно используется для MMIO и эмулируется VMM в пространстве пользователя. Чтобы облегчить эту обработку, PKVM должен рекламировать подробную информацию о неправильной инструкции, таких как его адрес, параметры регистрации и потенциально их содержимое обратно на хост, что может непреднамеренно разоблачить конфиденциальные данные от защищенного гостя, если ловушка не была ожидалась. PKVM решает эту проблему, рассматривая эти неисправности как смертельные, если только гость ранее не выпустил гиперкал, чтобы определить разлому диапазон IPA как тот, для которого доступны доступ к хосту. Это решение называется охранником MMIO .
Виртуальное устройство ввода/вывода (Virtio)
Virtio является популярным, портативным и зрелым стандартом для реализации и взаимодействия с паравритуализированными устройствами. Большинство устройств, подвергшихся воздействию защищенных гостей, реализованы с использованием Virtio. Virtio также лежит в основе реализации VSock, используемой для связи между защищенным гостем и остальной частью Android.
Устройства Virtio обычно реализуются в пользовательском пространстве хоста VMM, который перехватывает захваченные захваченные памяти от гостя к интерфейсу MMIO устройства Virtio и эмулирует ожидаемое поведение. MMIO Access является относительно дорогим, потому что каждый доступ к устройству требует обратной поездки к VMM и обратно, поэтому большая часть фактической передачи данных между устройством и гостями происходит с использованием набора виркетов в памяти. Ключевым предположением Virtio является то, что хост может произвольно получить доступ к памяти гостя. Это предположение очевидно при проектировании Virtqueue, которое может содержать указатели на буферы в госте, что эмуляция устройства предназначена для непосредственного доступа.
Хотя ранее описанные гиперкаллы, описанные памятью, могут использоваться для обмена буферами данных Virtio от гостя на хост, этот обмен обязательно выполняется на гранулярности Page и может в конечном итоге обнародовать больше данных, чем требуется, если размер буфера меньше, чем на странице. Вместо этого гость настроен для выделения как виркетов, так и соответствующих их буферов данных из фиксированного окна общей памяти, при этом данные копируются (отступаны) в окно и обратно по мере необходимости.
Взаимодействие с Trustzone
Хотя гости не могут напрямую взаимодействовать с TrustZone, хозяин все равно должен быть в состоянии выпустить вызовы SMC в защищенный мир. Эти вызовы могут указывать физически адресованные буферы памяти, которые недоступны для хоста. Поскольку безопасное программное обеспечение, как правило, не знает о доступности буфера, вредоносный хост может использовать этот буфер для выполнения запутанного заместителя атаки (аналогично атаке DMA). Чтобы предотвратить такие атаки, PKVM ловит все вызовы SMC для EL2 и выступает в качестве прокси между хостом и безопасным монитором на EL3.
Вызовы PSCI от хоста перенаправляются в прошивку EL3 с минимальными модификациями. В частности, точка входа для процессора, поступающего в Интернете или возобновления от приостановки, переписана, так что таблица страницы 2 этапа установлена на EL2, прежде чем вернуться на хост на EL1. Во время загрузки эта защита применяется PKVM.
Эта архитектура зависит от SOC, поддерживающего PSCI, предпочтительно благодаря использованию современной версии TF-A в качестве прошивки EL3.
Структура прошивки для ARM (FF-A) стандартизирует взаимодействия между нормальными и безопасными мирами, особенно в присутствии безопасного гипервизора. Основная часть спецификации определяет механизм обмена памятью с безопасным миром, используя как общий формат сообщений, так и четко определенную модель разрешений для базовых страниц. PKVM Proxies FF-A Сообщения, чтобы убедиться, что хост не пытается делиться памятью с безопасной стороной, для которой он не имеет достаточных разрешений.
Эта архитектура зависит от программного обеспечения Secure World, обеспечивающего соблюдение модели доступа к памяти, чтобы гарантировать, что доверенные приложения и любое другое программное обеспечение, работающее в безопасном мире, могут получить доступ к памяти, только если она либо принадлежит исключительно в Secure World, либо явно поделится с ним с помощью FF-A. В системе с S-EL2, обеспечение модели доступа к памяти должно выполняться безопасным ядром менеджера разделов (SPMC), такого как Hafnium , который поддерживает таблицы на этапе 2 для безопасного мира. В системе без S-EL2 TEE может вместо этого обеспечить соблюдение модели доступа к памяти через таблицы страниц 1 этапа.
Если вызов SMC в EL2 не является вызовом PSCI или FF-A, определенным, невозможные SMC перенаправляются в EL3. Предполагается, что (обязательно доверившая) безопасная прошивка может безопасно обрабатывать безмолвные SMC, потому что прошивка понимает меры предосторожности, необходимые для поддержания изоляции PVM.
Монитор виртуальной машины
CroSVM - это монитор виртуальной машины (VMM), который запускает виртуальные машины через интерфейс KVM от Linux. Что делает CroSVM уникальным, так это то, что он сосредоточен на безопасности с использованием языка программирования Rust и песочницы вокруг виртуальных устройств для защиты ядра хоста. Для получения дополнительной информации о CroSVM, см. Ее официальную документацию здесь .
Файл -дескрипторы и ioctls
KVM разоблачает устройство символа /dev/kvm
для пользователей с ioctls, которые составляют API KVM. Ioctls принадлежат к следующим категориям:
- Система IOCTLS запроса и устанавливает глобальные атрибуты, которые влияют на всю подсистему KVM, и создают PVMS.
- VM IOCTLS Запросы и устанавливают атрибуты, которые создают виртуальные процессоры (VCPU) и устройства, и влияют на целый PVM, такие как включение макета памяти и количество виртуальных процессоров (VCPU) и устройств.
- VCPU IOCTLS Запрос и устанавливает атрибуты, которые управляют операцией одного виртуального процессора.
- Устройство IOCTLS Запрос и устанавливает атрибуты, которые управляют работой одного виртуального устройства.
Каждый процесс CroSVM запускает ровно один экземпляр виртуальной машины. В этом процессе используется система KVM_CREATE_VM
IOCTL для создания дескриптора файла VM, который можно использовать для выпуска PVM IOCTLS. KVM_CREATE_VCPU
или KVM_CREATE_DEVICE
ioctl на VM FD создает VCPU/устройство и возвращает дескриптор файла, указывающий на новый ресурс. IOCTLS на VCPU или устройстве FD можно использовать для управления устройством, которое было создано с использованием IOCTL на VM FD. Для VCPU это включает в себя важную задачу запуска гостевого кода.
Внутренне CroSVM регистрирует дескрипторы файлов виртуальной машины с помощью ядра, используя интерфейс epoll
, вызванный Epoll. Затем ядро уведомляет CroSVM всякий раз, когда в любом из дескрипторов файлов ожидается новое событие.
PKVM добавляет новую возможность, KVM_CAP_ARM_PROTECTED_VM
, которую можно использовать для получения информации о среде PVM и настройки защищенного режима для виртуальной машины. CroSVM использует это во время создания PVM, если проходит флаг --protected-vm
, чтобы запросить и зарезервировать соответствующее количество памяти для прошивки PVM, а затем для включения защищенного режима.
Распределение памяти
Одной из основных обязанностей VMM является выделение памяти виртуальной машины и управление макетом памяти. CroSVM генерирует фиксированную компоновку памяти, свободно описанную в таблице ниже.
FDT в нормальном режиме | PHYS_MEMORY_END - 0x200000 |
Свободное место | ... |
Рамдиск | ALIGN_UP(KERNEL_END, 0x1000000) |
Ядро | 0x80080000 |
загрузчик | 0x80200000 |
FDT в режиме BIOS | 0x80000000 |
База физической памяти | 0x80000000 |
Прошивка PVM | 0x7FE00000 |
Память устройства | 0x10000 - 0x40000000 |
Физическая память выделяется с помощью mmap
, и память пожертвована виртуальной машине для заполнения ее областей памяти, называемых MEMSLOTS , с помощью KVM_SET_USER_MEMORY_REGION
IOCTL. Таким образом, вся приглашенная PVM -память приписывается экземпляру CroSVM, который управляет им и может привести к убийству процесса (завершает виртуальную машину), если хост начинает заканчивать свободную память. Когда виртуальная машина останавливается, память автоматически уничтожается гипервизором и возвращается в ядро хоста.
При обычном KVM VMM сохраняет доступ ко всей памяти гостей. С PKVM память гостя не наносится из физического адреса хоста, когда она пожертвована гостю. Единственное исключение - это память, явно разделенная гостем, например, для устройств Virtio.
Регионы MMIO в адресном пространстве гостя остались без зарезов. Доступ к этим регионам гостем пойман в ловушку и приводит к событию ввода/вывода на VM FD. Этот механизм используется для реализации виртуальных устройств. В защищенном режиме гость должен признать, что для MMIO используется область его адресного пространства с использованием гиперкала, чтобы снизить риск случайной утечки информации.
Планирование
Каждый виртуальный процессор представлен потоком POSIX и запланирован Host Linux Scheduler. Поток вызывает KVM_RUN
IOCTL на VCPU FD, что приводит к тому, что гипервизор перешел на контекст VCPU Guest. Планировщик хоста учитывает время, проведенное в гостевом контексте как время, используемое соответствующим потоком VCPU. KVM_RUN
возвращается, когда есть событие, которое должно быть обработано VMM, таким как ввод -ввод, конец прерывания или VCPU. VMM обрабатывает событие и снова вызывает KVM_RUN
.
Во время KVM_RUN
поток остается предотвращаемым планировщиком хоста, за исключением выполнения кода гипервизора EL2, который не является превентивным. Сам гостевой PVM не имеет механизма для контроля такого поведения.
Поскольку все потоки VCPU запланированы, как и любые другие задачи пользователя, они подчиняются всем стандартным механизмам QoS. В частности, каждый поток VCPU может быть связан с физическими процессорами, размещенных в процессорах, повышенной или ограниченной с использованием зажима использования, изменить их политику приоритета/планирования.
Виртуальные устройства
CroSVM поддерживает ряд устройств, в том числе следующие:
- Virtio-Blk для составных дисковых изображений, только для чтения или чтения
- vhost-vsock для общения с хозяином
- Virtio-PCI как Virtio Transport
- PL030 Часы в реальном времени (RTC)
- 16550A UART для последовательной связи
Прошивка PVM
Прошивка PVM (PVMFW) является первым кодом, выполненным PVM, похожим на загрузочный ПЗУ физического устройства. Основная цель PVMFW - Bootstrap Secure Boot и вывести уникальный секрет PVM. PVMFW не ограничивается использование с какой -либо конкретной ОС, такой как микродоида , при условии, что ОС поддерживается CroSVM и была должным образом подписана.
Двоичный файл PVMFW хранится в флэш -разделе с тем же именем и обновляется с использованием OTA .
Device Boot
Следующая последовательность шагов добавляется в процедуру загрузки устройства с поддержкой PKVM:
- Android Bootloader (ABL) загружает PVMFW из своего разделения в память и проверяет изображение.
- ABL получает секреты композитора идентификатора устройства (DICE) Секреты (идентификаторы комплексного устройства (CDI) и цепочку сертификатов костей) из корня доверия.
- ABL получает необходимые CDI для PVMFW и добавляет их в двоичный файл PVMFW.
- ABL добавляет в DT
linux,pkvm-guest-firmware-memory
, описывая местоположение и размер бинарного файла PVMFW и секреты, которые он получил на предыдущем этапе. - ABL рук контролирует Linux, а Linux инициализирует PKVM.
- PKVM Unmans Pvmfw Remeriem Precation из таблиц Stage 2 -й страницы Host 2 и защищает его от хоста (и гостей) в течение всего времени безотказной работы.
После загрузки устройства Microdroid загружается в соответствии с шагами в разделе последовательности загрузки микродоидного документа.
PVM Boot
При создании PVM CroSVM (или другой VMM) должен создать достаточно большой мемслот, который будет заполнен изображением PVMFW гипервизором. VMM также ограничен в списке регистров, начальное значение которого он может установить (x0-x14 для первичного VCPU, нет для вторичных VCPU). Остальные регистры зарезервированы и являются частью гипервизора-PVMFW ABI.
Когда PVM запускается, гипервизор сначала рука управляет первичным VCPU до PVMFW. Прошивка ожидает, что CroSVM загрузит ядро, подписанное AVB, которое может быть загрузчиком или любое другое изображение, а также Unsigned FDT в память в известных смещениях. PVMFW проверяет подпись AVB и, в случае успеха, генерирует доверенное дерево устройств из полученного FDT, вытирает свои секреты от памяти и ветви до точки входа полезной нагрузки. Если один из шагов проверки не выполняется, прошивка выпускает гиперколл PSCI SYSTEM_RESET
.
Между ботинками информация о экземпляре PVM сохраняется в разделе (устройство Virtio-BLK) и зашифрована с секретом PVMFW, чтобы гарантировать, что после перезагрузки секрет предоставляется к правильному экземпляру.
,Android предоставляет эталонную реализацию всех компонентов, необходимых для реализации структуры виртуализации Android. В настоящее время эта реализация ограничена ARM64. Эта страница объясняет фреймворк -архитектуру.
Фон
Архитектура ARM допускает до четырех уровней исключений, с уровнем исключения 0 (EL0) является наименее привилегированным, а уровень 3 (EL3) больше всего (EL3). Самая большая часть кодовой базы Android (все компоненты пользователя) работает на EL0. Остальная часть того, что обычно называют «Android», - это ядро Linux, которое работает на EL1.
Слой EL2 позволяет вводить гипервизора, который позволяет изолировать память и устройства в отдельные PVMS на EL1/EL0, с сильной конфиденциальностью и целостностью гарантий.
Гипервизор
Защищенная виртуальная машина на основе ядра (PKVM) построена на гипервизоре Linux KVM , который был расширен с возможностью ограничения доступа к полезным нагрузкам, работающим в виртуальных машинах для гостей, отмеченными «защищенными» во время создания.
KVM/ARM64 поддерживает различные режимы выполнения в зависимости от наличия определенных функций процессора, а именно, расширения хоста виртуализации (VHE) (ARMV8.1 и более позднее). В одном из этих режимов, широко известных как режим не-VHE, код гипервизора разделен из изображения ядра во время загрузки и устанавливается на EL2, тогда как само ядро работает на EL1. Хотя часть кодовой базы Linux, компонент EL2 KVM является небольшим компонентом, отвечающим за переключатель между несколькими EL1. Компонент гипервизора составлен с Linux, но находится в отдельном выделенном разделе памяти изображения vmlinux
. PKVM использует этот дизайн, расширяя код гипервизора новыми функциями, позволяя ему наложить ограничения на ядро и пользовательское пространство Android, а также ограничивая доступ к хосту к памяти гостя и гипервизор.
модули поставщиков PKVM
Модуль поставщика PKVM -это аппаратный модуль, содержащий функциональность, специфичные для устройства, такие как драйверы управления памятью (IOMMU) ввода-вывода (IOMMU). Эти модули позволяют вам доступа к доступу в PKVM, требующих доступа к PKVM, функциям безопасности, требующих доступа к уровню исключения.
Чтобы узнать, как реализовать и загрузить модуль поставщика PKVM, см. Внедрение модуля поставщика PKVM .
Процедура загрузки
На следующем рисунке изображена процедура загрузки PKVM:
- Bootloader входит в общее ядро на EL2.
- Общее ядро обнаруживает, что он работает в EL2 и депривоивает себя до EL1, в то время как PKVM и его модули продолжают работать на EL2. Кроме того, в это время загружаются модули поставщиков PKVM.
- Общее ядро переходит к нормальному загрузке, загружая все необходимые драйверы устройства до достижения пространства пользователя. На этом этапе PKVM находится на месте и обрабатывает таблицы страниц Stage-2.
Процедура загрузки доверяет загрузчику для поддержания целостности изображения ядра только во время ранней загрузки. Когда ядро дезистировано, им больше не считается доверительным гипервизором, который затем отвечает за защиту себя, даже если ядро скомпрометировано.
Наличие ядра Android и гипервизора на одном бинарном изображении позволяет между ними очень плотно связанным взаимодействием связи. Эта плотная связь гарантирует атомные обновления двух компонентов, что позволяет избежать необходимости поддерживать стабильный интерфейс между ними и обеспечивает большую гибкость без ущерба для долгосрочной обслуживаемости. Тесная связь также позволяет оптимизировать производительность, когда оба компонента могут сотрудничать, не влияя на гарантии безопасности, предоставленные гипервизором.
Более того, принятие GKI в экосистеме Android автоматически позволяет развернуть гипервизор PKVM на устройства Android в том же двоичном файле, что и ядро.
Защита доступа к памяти процессора
Архитектура ARM определяет блок управления памятью (MMU) на двух независимых этапах, которые могут использоваться для реализации перевода адреса и управления доступа к различным частям памяти. ММУ этапа 1 контролируется EL1 и позволяет первым уровнем перевода адреса. MMU Stage 1 используется Linux для управления виртуальным адресным пространством, предоставленным для каждого процесса пользователя, и для своего собственного виртуального адресного пространства.
MMU Stage 2 управляется EL2 и позволяет применять перевод второго адреса на выходном адресе MMU этапа 1, что приводит к физическому адресу (PA). Перевод на стадии 2 может использоваться гипервизорами для управления и перевода доступа к памяти со всех гостевых виртуальных машин. Как показано на рисунке 2, когда оба этапа перевода включены, выходной адрес стадии 1 называется промежуточным физическим адресом (IPA) Примечание. Виртуальный адрес (VA) переводится в IPA, а затем в PA.
Исторически, KVM работает с включенным переводом на этапе 2 во время управления гостями и с отключенным этапом во время запуска ядра Host Linux. Эта архитектура позволяет получить доступ к памяти из MMU Stage 1 -го этапа проходить через MMU на этапе 2, что позволяет неограниченный доступ от хоста к страницам памяти гостей. С другой стороны, PKVM обеспечивает защиту 2 этапа даже в контексте хоста и ставит гипервизор ответственным за защиту страниц памяти гостей вместо хоста.
KVM в полной мере использует перевод адреса на этапе 2 для реализации сложных отображений IPA/PA для гостей, что создает иллюзию смежной памяти для гостей, несмотря на физическую фрагментацию. Тем не менее, использование MMU Stage 2 для хоста ограничено только для контроля доступа. Стадия 2-го хоста отображается на идентификаторе, что обеспечивает смежную память в пространстве IPA хоста в пространстве PA. Эта архитектура позволяет использовать большие отображения в таблице страниц и, следовательно, снижает давление на буфер Translation LookAside (TLB). Поскольку сопоставление идентификации может быть проиндексировано PA, этап хоста 2 также используется для отслеживания владения страницей непосредственно в таблице страниц.
Защита прямого доступа к памяти (DMA)
Как описано ранее, невозможные гостевые страницы от хоста Linux в таблицах страниц CPU являются необходимым, но недостаточным шагом для защиты памяти гостей. PKVM также необходимо защитить от доступа к памяти, сделанных устройствами, способствующими DMA, под контролем ядра хоста, и возможность атаки DMA, инициированной злонамеренным хозяином. Чтобы предотвратить доступ к такому устройству, PKVM требует аппаратного обеспечения блока управления памятью (IOMMU) ввода-вывода (IOMMU) для каждого устройства, способного способным к DMA, в системе, как показано на рисунке 3.
Как минимум, Iommu Adware предоставляет средства предоставления и отмены доступа к чтению/записи для устройства для физической памяти на гранулярности Page. Тем не менее, это оборудование IOMMU ограничивает использование устройств в PVMS, поскольку они предполагают этап 2, отображаемая на личности.
Чтобы обеспечить изоляцию между виртуальными машинами, транзакции памяти, генерируемые от имени различных объектов, должны быть различны от iommu, чтобы для перевода можно было использовать соответствующий набор таблиц страниц.
Кроме того, сокращение объема SOC-специфического кода в EL2 является ключевой стратегией для снижения общей надежной вычислительной базы (TCB) PKVM и выполняет противодействие включению драйверов IOMMU в гипервизор. Чтобы смягчить эту проблему, хозяин в EL1 отвечает за вспомогательные задачи управления IOMMU, такие как управление питанием, инициализация и, при необходимости, обработка прерываний.
Тем не менее, размещение хоста в управление состоянием устройства ставит дополнительные требования к интерфейсу программирования аппаратного обеспечения iommu, чтобы гарантировать, что проверки разрешений не могут быть обойдены другими способами, например, после сброса устройства.
Стандартным и хорошо поддерживаемым iOmmu для устройств ARM, который делает возможным как изоляцию, так и прямое назначение, является архитектура управления памятью системы ARM (SMMU). Эта архитектура является рекомендуемым справочным решением.
Владение памятью
Во время загрузки все не-гипервизорная память предполагается, что она принадлежит хосту и отслеживается как таковая гипервизор. Когда PVM появляется, хост жертвует страницы памяти, чтобы позволить ему загружаться, а гипервизор переходит владение этими страницами от хоста на PVM. Таким образом, гипервизор устанавливает ограничения на контроль доступа в таблице страниц на стадии хоста, чтобы предотвратить его снова получить доступ к страницам, обеспечивая конфиденциальность гостю.
Связь между хостом и гостями стала возможной благодаря контролируемому обмену памятью между ними. Гостям разрешено поделиться некоторыми из своих страниц с хостом, используя гиперкал, который инструктирует гипервизора пережить эти страницы в таблице этапа 2 -го этапа. Аналогичным образом, связь хоста с TrustZone стала возможной благодаря операциям обмена памятью и/или кредитования, которые тщательно контролируются и контролируются PKVM с использованием спецификации прошивки для ARM (FF-A) .
Поскольку требования к памяти PVM могут измениться с течением времени, предоставляется гиперкал, который позволяет вернуть хост -владение указанными страницами, принадлежащими абоненту. На практике этот гиперкал используется с протоколом воздушного шара Virtio, чтобы позволить VMM запросить память обратно от PVM, и для того, чтобы PVM уведомлял VMM о отказе от страниц контролируемым образом.
Гипервизор отвечает за отслеживание владения всеми страницами памяти в системе и о том, обмениваются ли они или одарены другим организациям. Большая часть этого отслеживания состояния выполняется с использованием метаданных, прикрепленных к таблицам хоста и гостей на этапе 2, используя зарезервированные биты в записях таблицы страниц (PTE), которые, как следует из их названия, зарезервированы для использования программного обеспечения.
Хозяин должен убедиться, что он не пытается получить доступ к страницам, которые были сделаны недоступными гипервизором. Нелегальный доступ хоста приводит к тому, что синхронное исключение будет введено в хост гипервизором, что может привести к тому, что ответственная задача пользователя получает сигнал SEGV, либо сбои ядра хоста. Чтобы предотвратить случайный доступ, страницы, пожертвованные гостям, не имеют права на обмен или слияние ядром хоста.
Управление прерыванием и таймеры
Прерывания являются неотъемлемой частью того, как гость взаимодействует с устройствами и для связи между процессорами, где межпроцессорные прерывания (IPIS) являются основным механизмом связи. Модель KVM предназначена для того, чтобы делегировать все виртуальное управление прерыванием хозяину в EL1, которая для этой цели ведет себя как ненадежная часть гипервизора.
PKVM предлагает полную эмуляцию общего контроллера прерываний версии 3 (GICV3) на основе существующего кода KVM. Timer and IPIs are handled as part of this untrusted emulation code.
GICv3 support
The interface between EL1 and EL2 must ensure that the full interrupt state is visible to the EL1 host, including copies of the hypervisor registers related to interrupts. This visibility is typically accomplished using shared memory regions, one per virtual CPU (vCPU).
The system register runtime support code can be simplified to support only the Software Generated Interrupt Register (SGIR) and Deactivate Interrupt Register (DIR) register trapping. The architecture mandates that these registers always trap to EL2, while the other traps have so far only been useful to mitigate errata. Everything else is being handled in hardware.
On the MMIO side, everything is emulated at EL1, reusing all the current infrastructure in KVM. Finally, Wait for Interrupt (WFI) is always relayed to EL1, because this is one of the basic scheduling primitives KVM uses.
Timer support
The comparator value for the virtual timer must be exposed to EL1 on each trapping WFI so that EL1 can inject timer interrupts while the vCPU is blocked. The physical timer is entirely emulated, and all traps relayed to EL1.
MMIO handling
To communicate with the virtual machine monitor (VMM) and perform GIC emulation, MMIO traps must be relayed back to the host in EL1 for further triaging. pKVM requires the following:
- IPA and size of the access
- Data in case of a write
- Endianness of the CPU at the point of trapping
Additionally, traps with a general purpose register (GPR) as a source/destination are relayed using an abstract transfer pseudo-register.
Guest interfaces
A guest can communicate with a protected guest using a combination of hypercalls and memory access to trapped regions. Hypercalls are exposed according to the SMCCC standard , with a range reserved for a vendor allocation by KVM. The following hypercalls are of particular importance to pKVM guests.
Generic hypercalls
- PSCI provides a standard mechanism for the guest to control the lifecycle of its vCPUs including onlining, offlining, and system shutdown.
- TRNG provides a standard mechanism for the guest to request entropy from the pKVM which relays the call to EL3. This mechanism is particularly useful where the host can't be trusted to virtualize a hardware random number generator (RNG).
pKVM hypercalls
- Memory sharing with the host. All guest memory is initially inaccessible to the host, but host access is necessary for shared-memory communication and for paravirtualized devices that rely on shared buffers. Hypercalls for sharing and unsharing pages with the host allow the guest to decide exactly what parts of memory are made accessible to the rest of Android without the need for a handshake.
- Memory relinquishment to the host. All guest memory usually belongs to the guest until it is destroyed. This state can be inadequate for long-lived VMs with memory requirements which vary over time. The
relinquish
hypercall allows a guest to explicitly transfer ownership of pages back to the host without requiring guest termination. - Memory access trapping to the host. Traditionally, if a KVM guest accesses an address that doesn't correspond to a valid memory region, then the vCPU thread exits to the host and the access is typically used for MMIO and emulated by the VMM in user space. To facilitate this handling, pKVM is required to advertise details about the faulting instruction such as its address, register parameters and potentially their contents back to the host, which could unintentionally expose sensitive data from a protected guest if the trap was not anticipated. pKVM solves this problem by treating these faults as fatal unless the guest has previously issued a hypercall to identify the faulting IPA range as one for which accesses are permitted to trap back to the host. This solution is referred to as the MMIO guard .
Virtual I/O device (virtio)
Virtio is a popular, portable, and mature standard for implementing and interacting with paravirtualized devices. The majority of devices exposed to protected guests are implemented using virtio. Virtio also underpins the vsock implementation used for communication between a protected guest and the rest of Android.
Virtio devices are typically implemented in the host's user space by the VMM, which intercepts trapped memory accesses from the guest to the MMIO interface of the virtio device and emulates the expected behavior. MMIO access is relatively expensive because each access to the device requires a round-trip to the VMM and back, so most of the actual data transfer between the device and guest occurs using a set of virtqueues in memory. A key assumption of virtio is that the host can access guest memory arbitrarily. This assumption is evident in the design of the virtqueue, which might contain pointers to buffers in the guest that the device emulation is intended to access directly.
Although the previously described memory sharing hypercalls could be used to share virtio data buffers from the guest to the host, this sharing is necessarily performed at page granularity and could end up exposing more data than required if the buffer size is less than that of a page. Instead, the guest is configured to allocate both the virtqueues and their corresponding data buffers from a fixed window of shared memory, with data being copied (bounced) to and from the window as required.
Interaction with TrustZone
Although guests aren't able to interact directly with TrustZone, the host must still be able to issue SMC calls into the secure world. These calls can specify physically addressed memory buffers that are inaccessible to the host. Because the secure software is generally unaware of the accessibility of the buffer, a malicious host could use this buffer to perform a confused deputy attack (analogous to a DMA attack). To prevent such attacks, pKVM traps all host SMC calls to EL2 and acts as a proxy between the host and the secure monitor at EL3.
PSCI calls from the host are forwarded to the EL3 firmware with minimal modifications. Specifically, the entry point for a CPU coming online or resuming from suspend is rewritten so that the stage 2 page table is installed at EL2 before returning to the host at EL1. During boot, this protection is enforced by pKVM.
This architecture relies on the SoC supporting PSCI, preferably through the use of an up-to-date version of TF-A as its EL3 firmware.
Firmware Framework for Arm (FF-A) standardizes interactions between the normal and secure worlds, particularly in the presence of a secure hypervisor. A major part of the specification defines a mechanism for sharing memory with the secure world, using both a common message format and a well-defined permissions model for the underlying pages. pKVM proxies FF-A messages to ensure that the host isn't attempting to share memory with the secure-side for which it doesn't have sufficient permissions.
This architecture relies on the secure world software enforcing the memory access model, to ensure that trusted apps and any other software running in the secure world can access memory only if it's either exclusively owned by the secure world or has been explicitly shared with it using FF-A. On a system with S-EL2, enforcing the memory access model should be done by a Secure Partition Manager Core (SPMC), such as Hafnium , which maintains stage 2 page tables for the secure world. On a system without S-EL2, the TEE can instead enforce a memory access model through its stage 1 page tables.
If the SMC call to EL2 isn't a PSCI call or FF-A defined message, unhandled SMCs are forwarded to EL3. The assumption is that the (necessarily trusted) secure firmware can handle unhandled SMCs safely because the firmware understands the precautions needed to maintain pVM isolation.
Virtual machine monitor
crosvm is a virtual machine monitor (VMM) which runs virtual machines through Linux's KVM interface. What makes crosvm unique is its focus on safety with the use of the Rust programming language and a sandbox around virtual devices to protect the host kernel. For more about crosvm, see its official documentation here .
File descriptors and ioctls
KVM exposes the /dev/kvm
character device to userspace with ioctls that make up the KVM API. The ioctls belong to the following categories:
- System ioctls query and set global attributes that affect the whole KVM subsystem, and create pVMs.
- VM ioctls query and set attributes that create virtual CPUs (vCPUs) and devices, and affect an entire pVM, such as including memory layout and the number of virtual CPUs (vCPUs) and devices.
- vCPU ioctls query and set attributes that control the operation of a single virtual CPU.
- Device ioctls query and set attributes that control the operation of a single virtual device.
Each crosvm process runs exactly one instance of a virtual machine. This process uses the KVM_CREATE_VM
system ioctl to create a VM file descriptor that can be used to issue pVM ioctls. A KVM_CREATE_VCPU
or KVM_CREATE_DEVICE
ioctl on a VM FD creates a vCPU/device and returns a file descriptor pointing to the new resource. ioctls on a vCPU or device FD can be used to control the device that was created using the ioctl on a VM FD. For vCPUs, this includes the important task of running guest code.
Internally, crosvm registers the VM's file descriptors with the kernel using the edge-triggered epoll
interface. The kernel then notifies crosvm whenever there's a new event pending in any of the file descriptors.
pKVM adds a new capability, KVM_CAP_ARM_PROTECTED_VM
, which can be used to get information about the pVM environment and set up protected mode for a VM. crosvm uses this during pVM creation if the --protected-vm
flag is passed, to query and reserve the appropriate amount of memory for pVM firmware, and then to enable protected mode.
Memory allocation
One of the main responsibilities of a VMM is allocating the VM's memory and managing its memory layout. crosvm generates a fixed memory layout loosely described in the table below.
FDT in normal mode | PHYS_MEMORY_END - 0x200000 |
Свободное место | ... |
Ramdisk | ALIGN_UP(KERNEL_END, 0x1000000) |
Ядро | 0x80080000 |
загрузчик | 0x80200000 |
FDT in BIOS mode | 0x80000000 |
Physical memory base | 0x80000000 |
pVM firmware | 0x7FE00000 |
Device memory | 0x10000 - 0x40000000 |
Physical memory is allocated with mmap
and the memory is donated to the VM to populate its memory regions, called memslots , with the KVM_SET_USER_MEMORY_REGION
ioctl. All guest pVM memory is therefore attributed to the crosvm instance that manages it and can result in the process being killed (terminating the VM) if the host starts running out of free memory. When a VM is stopped, the memory is automatically wiped by the hypervisor and returned to the host kernel.
Under regular KVM, the VMM retains access to all guest memory. With pKVM, guest memory is unmapped from the host physical address space when it's donated to the guest. The only exception is memory explicitly shared back by the guest, such as for virtio devices.
MMIO regions in the guest's address space are left unmapped. Access to these regions by the guest is trapped and results in an I/O event on the VM FD. This mechanism is used to implement virtual devices. In protected mode, the guest must acknowledge that a region of its address space is be used for MMIO using a hypercall, to reduce risk of accidental information leakage.
Scheduling
Each virtual CPU is represented by a POSIX thread and scheduled by the host Linux scheduler. The thread calls the KVM_RUN
ioctl on the vCPU FD, resulting in the hypervisor switching to the guest vCPU context. The host scheduler accounts for the time spent in a guest context as time used by the corresponding vCPU thread. KVM_RUN
returns when there's an event that must be handled by the VMM, such as I/O, end of interrupt, or the vCPU halted. The VMM handles the event and calls KVM_RUN
again.
During KVM_RUN
, the thread remains preemptible by the host scheduler, except for execution of the EL2 hypervisor code, which isn't preemptible. The guest pVM itself has no mechanism for controlling this behavior.
Because all vCPU threads are scheduled like any other userspace tasks, they're subject to all standard QoS mechanisms. Specifically, each vCPU thread can be affined to physical CPUs, placed in cpusets, boosted or capped using utilization clamping, have their priority/scheduling policy changed, and more.
Virtual devices
crosvm supports a number of devices, including the following:
- virtio-blk for composite disk images, read-only or read-write
- vhost-vsock for communication with the host
- virtio-pci as virtio transport
- pl030 real time clock (RTC)
- 16550a UART for serial communication
pVM firmware
The pVM firmware (pvmfw) is the first code executed by a pVM, similar to the boot ROM of a physical device. pvmfw's primary goal is to bootstrap secure boot and derive the pVM's unique secret. pvmfw isn't limited to use with any specific OS, such as Microdroid , as long as the OS is supported by crosvm and has been properly signed.
The pvmfw binary is stored in a flash partition of the same name and is updated using OTA .
Device boot
The following sequence of steps is added to the boot procedure of a pKVM-enabled device:
- The Android Bootloader (ABL) loads pvmfw from its partition into memory and verifies the image.
- The ABL obtains its Device Identifier Composition Engine (DICE) secrets (Compound Device Identifiers (CDIs) and DICE certificate chain) from a Root of Trust.
- The ABL derives the necessary CDIs for pvmfw, and appends them to the pvmfw binary.
- The ABL adds a
linux,pkvm-guest-firmware-memory
reserved memory region node to the DT, describing the location and size of the pvmfw binary and the secrets it derived in the previous step. - The ABL hands control over to Linux and Linux initializes pKVM.
- pKVM unmaps pvmfw memory region from host's stage 2 page tables and protects it from the host (and guests) throughout device uptime.
After device boot, Microdroid is booted per the steps in the Boot sequence section of the Microdroid document.
pVM boot
When creating a pVM, crosvm (or another VMM) must create a sufficiently large memslot to be populated with the pvmfw image by the hypervisor. The VMM is also restricted in the list of registers whose initial value it can set (x0-x14 for the primary vCPU, none for secondary vCPUs). The remaining registers are reserved and are part of the hypervisor-pvmfw ABI.
When the pVM is run, the hypervisor first hands control of the primary vCPU over to pvmfw. The firmware expects crosvm to have loaded an AVB-signed kernel, which can be a bootloader or any other image, and an unsigned FDT to memory at known offsets. pvmfw validates the AVB signature and, if successful, generates a trusted device tree from the received FDT, wipes its secrets from memory, and branches to the entry point of the payload. If one of the verification steps fails, the firmware issues a PSCI SYSTEM_RESET
hypercall.
Between boots, information about the pVM instance is stored in a partition (virtio-blk device) and encrypted with pvmfw's secret to ensure that, following a reboot, the secret is being provisioned to the correct instance.
,Android provides a reference implementation of all the components needed to implement the Android Virtualization Framework. Currently this implementation is limited to ARM64. This page explains the framework architecture.
Фон
The Arm architecture allows up to four exception levels, with exception level 0 (EL0) being the least privileged, and exception level 3 (EL3) the most. The largest portion of the Android codebase (all userspace components) runs at EL0. The rest of what is commonly called "Android" is the Linux kernel, which runs at EL1.
The EL2 layer allows the introduction of a hypervisor that enables isolating memory and devices into individual pVMs at EL1/EL0, with strong confidentiality and integrity guarantees.
Hypervisor
The protected kernel-based virtual machine (pKVM) is built upon the Linux KVM hypervisor , which has been extended with the ability to restrict access to the payloads running in guest virtual machines marked 'protected' at the time of creation.
KVM/arm64 supports different execution modes depending on the availability of certain CPU features, namely, the Virtualization Host Extensions (VHE) (ARMv8.1 and later). In one of those modes, commonly known as the non-VHE mode, the hypervisor code is split out of the kernel image during boot and installed at EL2, whereas the kernel itself runs at EL1. Although part of the Linux codebase, the EL2 component of KVM is a small component in charge of the switch between multiple EL1s. The hypervisor component is compiled with Linux, but resides in a separate, dedicated memory section of the vmlinux
image. pKVM leverages this design by extending the hypervisor code with new features allowing it to put restrictions on the Android host kernel and user space, and limiting host access to guest memory and the hypervisor.
pKVM vendor modules
A pKVM vendor module is a hardware-specific module containing device-specific functionality, such as input-output memory management unit (IOMMU) drivers. These modules let you port security features requiring exception level 2 (EL2) access to pKVM.
To learn how to implement and load a pKVM vendor module, refer to Implement a pKVM vendor module .
Boot procedure
The following figure depicts the pKVM boot procedure:
- The bootloader enters the generic kernel at EL2.
- The generic kernel detects that it's running at EL2 and deprivileges itself to EL1 while pKVM and its modules continue to run at EL2. Additionally, pKVM vendor modules are loaded at this time.
- The generic kernel proceeds to boot normally, loading all necessary device drivers until reaching user space. At this point, pKVM is in place and handles the stage-2 page tables.
The boot procedure trusts the bootloader to maintain the integrity of the kernel image only during early boot. When the kernel is deprivileged, it's no longer considered trusted by the hypervisor, which is then responsible for protecting itself even if the kernel is compromised.
Having the Android kernel and the hypervisor in the same binary image allows for a very tightly coupled communication interface between them. This tight coupling guarantees atomic updates of the two components, which avoids the need to keep the interface between them stable, and offers a great deal of flexibility without compromising long-term maintainability. The tight coupling also allows performance optimizations when both components can cooperate without impacting the security guarantees provided by the hypervisor.
Moreover, the adoption of GKI in the Android ecosystem automatically allows the pKVM hypervisor to be deployed to Android devices in the same binary as the kernel.
CPU memory access protection
The Arm architecture specifies a memory management unit (MMU) split in two independent stages, both of which can be used to implement address translation and access control to different parts of memory. The stage 1 MMU is controlled by EL1 and allows a first level of address translation. The stage 1 MMU is used by Linux to manage the virtual address space provided to each userspace process and to its own virtual address space.
The stage 2 MMU is controlled by EL2 and enables the application of a second address translation on the output address of the stage 1 MMU, resulting in a physical address (PA). The stage 2 translation can be used by hypervisors to control and translate memory accesses from all guest VMs. As shown in figure 2, when both stages of translation are enabled, the output address of the stage 1 is called an intermediate physical address (IPA) Note: The virtual address (VA) is translated into an IPA and then to a PA.
Historically, KVM runs with stage 2 translation enabled while running guests and with stage 2 disabled while running the host Linux kernel. This architecture allows memory accesses from the host stage 1 MMU to pass through the stage 2 MMU, hence allowing unrestricted access from the host to guest memory pages. On the other hand, pKVM enables stage 2 protection even in host context, and puts the hypervisor in charge of protecting guest memory pages instead of the host.
KVM makes full use of address translation at stage 2 to implement complex IPA/PA mappings for guests, which creates the illusion of contiguous memory for guests despite physical fragmentation. However, the usage of the stage 2 MMU for the host is restricted to access control only. The host stage 2 is identity-mapped, ensuring that contiguous memory in the host IPA space is contiguous in the PA space. This architecture allows the use of large mappings in the page table and consequently reduces pressure on the translation lookaside buffer (TLB). Because an identity mapping can be indexed by PA, the host stage 2 is also used to track page ownership directly in the page table.
Direct memory access (DMA) protection
As described previously, unmapping guest pages from the Linux host in the CPU page tables is a necessary but insufficient step for protecting guest memory. pKVM also needs to protect against memory accesses made by DMA-capable devices under the host kernel's control, and the possibility of a DMA attack initiated by a malicious host. To prevent such a device from accessing guest memory, pKVM requires input-output memory management unit (IOMMU) hardware for every DMA-capable device in the system, as shown in figure 3.
At a minimum, IOMMU hardware provides the means of granting and revoking read/write access for a device to physical memory at page granularity. However, this IOMMU hardware limits the use of devices in pVMs as they assume an identity-mapped stage 2.
To ensure isolation between virtual machines, memory transactions generated on behalf of different entities must be distinguishable by the IOMMU so that the appropriate set of page tables can be used for the translation.
In addition, reducing the amount of SoC-specific code at EL2 is a key strategy to reduce the overall trusted computing base (TCB) of pKVM and runs counter to the inclusion of IOMMU drivers in the hypervisor. To mitigate this issue, the host at EL1 is responsible for auxiliary IOMMU management tasks, such as power management, initialization and, where appropriate, interrupt handling.
However, putting the host in control of the device state places additional requirements on the programming interface of the IOMMU hardware to ensure that permission checks can't be bypassed by other means, for example, following a device reset.
A standard and well supported IOMMU for Arm devices that makes both isolation and direct assignment possible is the Arm System Memory Management Unit (SMMU) architecture. This architecture is the recommended reference solution.
Memory ownership
At boot time, all non-hypervisor memory is assumed to be owned by the host, and is tracked as such by the hypervisor. When a pVM is spawned, the host donates memory pages to allow it to boot and the hypervisor transitions the ownership of those pages from the host to the pVM. Thus, the hypervisor puts access-control restrictions in place in the host's stage 2 page table to prevent it from accessing the pages again, providing confidentiality to the guest.
Communication between the host and guests is made possible by controlled memory sharing between them. Guests are allowed to share some of their pages back with the host using a hypercall, which instructs the hypervisor to remap those pages in the host stage 2 page table. Similarly, the host's communication with TrustZone is made possible by memory sharing and/or lending operations, all of which are closely monitored and controlled by pKVM using the Firmware Framework for Arm (FF-A) specification .
Since a pVM's memory requirements can change over time, a hypercall is provided which allows ownership of specified pages belonging to the caller to be relinquished back to the host. In practice this hypercall is used with the virtio balloon protocol to allow the VMM to request memory back from the pVM, and for the pVM to notify the VMM of relinquished pages, in a controlled manner.
The hypervisor is responsible for tracking ownership of all memory pages in the system and whether they're being shared or lent to other entities. Most of this state tracking is done using metadata attached to the host and guests' stage 2 page tables, using reserved bits in the page table entries (PTEs) which, as their name suggests, are reserved for software use.
The host must ensure that it doesn't attempt to access pages that have been made inaccessible by the hypervisor. An illegal host access causes a synchronous exception to be injected into the host by the hypervisor, which can either result in the responsible userspace task receiving a SEGV signal, or the host kernel crashing. To prevent accidental accesses, pages donated to guests are made ineligible for swapping or merging by the host kernel.
Interrupt handling and timers
Interrupts are an essential part of the way a guest interacts with devices and for communication between CPUs, where interprocessor interrupts (IPIs) are the main communication mechanism. The KVM model is to delegate all the virtual interrupt management to the host in EL1, which for that purpose behaves as an untrusted part of the hypervisor.
pKVM offers a full Generic Interrupt Controller version 3 (GICv3) emulation based on the existing KVM code. Timer and IPIs are handled as part of this untrusted emulation code.
GICv3 support
The interface between EL1 and EL2 must ensure that the full interrupt state is visible to the EL1 host, including copies of the hypervisor registers related to interrupts. This visibility is typically accomplished using shared memory regions, one per virtual CPU (vCPU).
The system register runtime support code can be simplified to support only the Software Generated Interrupt Register (SGIR) and Deactivate Interrupt Register (DIR) register trapping. The architecture mandates that these registers always trap to EL2, while the other traps have so far only been useful to mitigate errata. Everything else is being handled in hardware.
On the MMIO side, everything is emulated at EL1, reusing all the current infrastructure in KVM. Finally, Wait for Interrupt (WFI) is always relayed to EL1, because this is one of the basic scheduling primitives KVM uses.
Timer support
The comparator value for the virtual timer must be exposed to EL1 on each trapping WFI so that EL1 can inject timer interrupts while the vCPU is blocked. The physical timer is entirely emulated, and all traps relayed to EL1.
MMIO handling
To communicate with the virtual machine monitor (VMM) and perform GIC emulation, MMIO traps must be relayed back to the host in EL1 for further triaging. pKVM requires the following:
- IPA and size of the access
- Data in case of a write
- Endianness of the CPU at the point of trapping
Additionally, traps with a general purpose register (GPR) as a source/destination are relayed using an abstract transfer pseudo-register.
Guest interfaces
A guest can communicate with a protected guest using a combination of hypercalls and memory access to trapped regions. Hypercalls are exposed according to the SMCCC standard , with a range reserved for a vendor allocation by KVM. The following hypercalls are of particular importance to pKVM guests.
Generic hypercalls
- PSCI provides a standard mechanism for the guest to control the lifecycle of its vCPUs including onlining, offlining, and system shutdown.
- TRNG provides a standard mechanism for the guest to request entropy from the pKVM which relays the call to EL3. This mechanism is particularly useful where the host can't be trusted to virtualize a hardware random number generator (RNG).
pKVM hypercalls
- Memory sharing with the host. All guest memory is initially inaccessible to the host, but host access is necessary for shared-memory communication and for paravirtualized devices that rely on shared buffers. Hypercalls for sharing and unsharing pages with the host allow the guest to decide exactly what parts of memory are made accessible to the rest of Android without the need for a handshake.
- Memory relinquishment to the host. All guest memory usually belongs to the guest until it is destroyed. This state can be inadequate for long-lived VMs with memory requirements which vary over time. The
relinquish
hypercall allows a guest to explicitly transfer ownership of pages back to the host without requiring guest termination. - Memory access trapping to the host. Traditionally, if a KVM guest accesses an address that doesn't correspond to a valid memory region, then the vCPU thread exits to the host and the access is typically used for MMIO and emulated by the VMM in user space. To facilitate this handling, pKVM is required to advertise details about the faulting instruction such as its address, register parameters and potentially their contents back to the host, which could unintentionally expose sensitive data from a protected guest if the trap was not anticipated. pKVM solves this problem by treating these faults as fatal unless the guest has previously issued a hypercall to identify the faulting IPA range as one for which accesses are permitted to trap back to the host. This solution is referred to as the MMIO guard .
Virtual I/O device (virtio)
Virtio is a popular, portable, and mature standard for implementing and interacting with paravirtualized devices. The majority of devices exposed to protected guests are implemented using virtio. Virtio also underpins the vsock implementation used for communication between a protected guest and the rest of Android.
Virtio devices are typically implemented in the host's user space by the VMM, which intercepts trapped memory accesses from the guest to the MMIO interface of the virtio device and emulates the expected behavior. MMIO access is relatively expensive because each access to the device requires a round-trip to the VMM and back, so most of the actual data transfer between the device and guest occurs using a set of virtqueues in memory. A key assumption of virtio is that the host can access guest memory arbitrarily. This assumption is evident in the design of the virtqueue, which might contain pointers to buffers in the guest that the device emulation is intended to access directly.
Although the previously described memory sharing hypercalls could be used to share virtio data buffers from the guest to the host, this sharing is necessarily performed at page granularity and could end up exposing more data than required if the buffer size is less than that of a page. Instead, the guest is configured to allocate both the virtqueues and their corresponding data buffers from a fixed window of shared memory, with data being copied (bounced) to and from the window as required.
Interaction with TrustZone
Although guests aren't able to interact directly with TrustZone, the host must still be able to issue SMC calls into the secure world. These calls can specify physically addressed memory buffers that are inaccessible to the host. Because the secure software is generally unaware of the accessibility of the buffer, a malicious host could use this buffer to perform a confused deputy attack (analogous to a DMA attack). To prevent such attacks, pKVM traps all host SMC calls to EL2 and acts as a proxy between the host and the secure monitor at EL3.
PSCI calls from the host are forwarded to the EL3 firmware with minimal modifications. Specifically, the entry point for a CPU coming online or resuming from suspend is rewritten so that the stage 2 page table is installed at EL2 before returning to the host at EL1. During boot, this protection is enforced by pKVM.
This architecture relies on the SoC supporting PSCI, preferably through the use of an up-to-date version of TF-A as its EL3 firmware.
Firmware Framework for Arm (FF-A) standardizes interactions between the normal and secure worlds, particularly in the presence of a secure hypervisor. A major part of the specification defines a mechanism for sharing memory with the secure world, using both a common message format and a well-defined permissions model for the underlying pages. pKVM proxies FF-A messages to ensure that the host isn't attempting to share memory with the secure-side for which it doesn't have sufficient permissions.
This architecture relies on the secure world software enforcing the memory access model, to ensure that trusted apps and any other software running in the secure world can access memory only if it's either exclusively owned by the secure world or has been explicitly shared with it using FF-A. On a system with S-EL2, enforcing the memory access model should be done by a Secure Partition Manager Core (SPMC), such as Hafnium , which maintains stage 2 page tables for the secure world. On a system without S-EL2, the TEE can instead enforce a memory access model through its stage 1 page tables.
If the SMC call to EL2 isn't a PSCI call or FF-A defined message, unhandled SMCs are forwarded to EL3. The assumption is that the (necessarily trusted) secure firmware can handle unhandled SMCs safely because the firmware understands the precautions needed to maintain pVM isolation.
Virtual machine monitor
crosvm is a virtual machine monitor (VMM) which runs virtual machines through Linux's KVM interface. What makes crosvm unique is its focus on safety with the use of the Rust programming language and a sandbox around virtual devices to protect the host kernel. For more about crosvm, see its official documentation here .
File descriptors and ioctls
KVM exposes the /dev/kvm
character device to userspace with ioctls that make up the KVM API. The ioctls belong to the following categories:
- System ioctls query and set global attributes that affect the whole KVM subsystem, and create pVMs.
- VM ioctls query and set attributes that create virtual CPUs (vCPUs) and devices, and affect an entire pVM, such as including memory layout and the number of virtual CPUs (vCPUs) and devices.
- vCPU ioctls query and set attributes that control the operation of a single virtual CPU.
- Device ioctls query and set attributes that control the operation of a single virtual device.
Each crosvm process runs exactly one instance of a virtual machine. This process uses the KVM_CREATE_VM
system ioctl to create a VM file descriptor that can be used to issue pVM ioctls. A KVM_CREATE_VCPU
or KVM_CREATE_DEVICE
ioctl on a VM FD creates a vCPU/device and returns a file descriptor pointing to the new resource. ioctls on a vCPU or device FD can be used to control the device that was created using the ioctl on a VM FD. For vCPUs, this includes the important task of running guest code.
Internally, crosvm registers the VM's file descriptors with the kernel using the edge-triggered epoll
interface. The kernel then notifies crosvm whenever there's a new event pending in any of the file descriptors.
pKVM adds a new capability, KVM_CAP_ARM_PROTECTED_VM
, which can be used to get information about the pVM environment and set up protected mode for a VM. crosvm uses this during pVM creation if the --protected-vm
flag is passed, to query and reserve the appropriate amount of memory for pVM firmware, and then to enable protected mode.
Memory allocation
One of the main responsibilities of a VMM is allocating the VM's memory and managing its memory layout. crosvm generates a fixed memory layout loosely described in the table below.
FDT in normal mode | PHYS_MEMORY_END - 0x200000 |
Свободное место | ... |
Ramdisk | ALIGN_UP(KERNEL_END, 0x1000000) |
Ядро | 0x80080000 |
загрузчик | 0x80200000 |
FDT in BIOS mode | 0x80000000 |
Physical memory base | 0x80000000 |
pVM firmware | 0x7FE00000 |
Device memory | 0x10000 - 0x40000000 |
Physical memory is allocated with mmap
and the memory is donated to the VM to populate its memory regions, called memslots , with the KVM_SET_USER_MEMORY_REGION
ioctl. All guest pVM memory is therefore attributed to the crosvm instance that manages it and can result in the process being killed (terminating the VM) if the host starts running out of free memory. When a VM is stopped, the memory is automatically wiped by the hypervisor and returned to the host kernel.
Under regular KVM, the VMM retains access to all guest memory. With pKVM, guest memory is unmapped from the host physical address space when it's donated to the guest. The only exception is memory explicitly shared back by the guest, such as for virtio devices.
MMIO regions in the guest's address space are left unmapped. Access to these regions by the guest is trapped and results in an I/O event on the VM FD. This mechanism is used to implement virtual devices. In protected mode, the guest must acknowledge that a region of its address space is be used for MMIO using a hypercall, to reduce risk of accidental information leakage.
Scheduling
Each virtual CPU is represented by a POSIX thread and scheduled by the host Linux scheduler. The thread calls the KVM_RUN
ioctl on the vCPU FD, resulting in the hypervisor switching to the guest vCPU context. The host scheduler accounts for the time spent in a guest context as time used by the corresponding vCPU thread. KVM_RUN
returns when there's an event that must be handled by the VMM, such as I/O, end of interrupt, or the vCPU halted. The VMM handles the event and calls KVM_RUN
again.
During KVM_RUN
, the thread remains preemptible by the host scheduler, except for execution of the EL2 hypervisor code, which isn't preemptible. The guest pVM itself has no mechanism for controlling this behavior.
Because all vCPU threads are scheduled like any other userspace tasks, they're subject to all standard QoS mechanisms. Specifically, each vCPU thread can be affined to physical CPUs, placed in cpusets, boosted or capped using utilization clamping, have their priority/scheduling policy changed, and more.
Virtual devices
crosvm supports a number of devices, including the following:
- virtio-blk for composite disk images, read-only or read-write
- vhost-vsock for communication with the host
- virtio-pci as virtio transport
- pl030 real time clock (RTC)
- 16550a UART for serial communication
pVM firmware
The pVM firmware (pvmfw) is the first code executed by a pVM, similar to the boot ROM of a physical device. pvmfw's primary goal is to bootstrap secure boot and derive the pVM's unique secret. pvmfw isn't limited to use with any specific OS, such as Microdroid , as long as the OS is supported by crosvm and has been properly signed.
The pvmfw binary is stored in a flash partition of the same name and is updated using OTA .
Device boot
The following sequence of steps is added to the boot procedure of a pKVM-enabled device:
- The Android Bootloader (ABL) loads pvmfw from its partition into memory and verifies the image.
- The ABL obtains its Device Identifier Composition Engine (DICE) secrets (Compound Device Identifiers (CDIs) and DICE certificate chain) from a Root of Trust.
- The ABL derives the necessary CDIs for pvmfw, and appends them to the pvmfw binary.
- The ABL adds a
linux,pkvm-guest-firmware-memory
reserved memory region node to the DT, describing the location and size of the pvmfw binary and the secrets it derived in the previous step. - The ABL hands control over to Linux and Linux initializes pKVM.
- pKVM unmaps pvmfw memory region from host's stage 2 page tables and protects it from the host (and guests) throughout device uptime.
After device boot, Microdroid is booted per the steps in the Boot sequence section of the Microdroid document.
pVM boot
When creating a pVM, crosvm (or another VMM) must create a sufficiently large memslot to be populated with the pvmfw image by the hypervisor. The VMM is also restricted in the list of registers whose initial value it can set (x0-x14 for the primary vCPU, none for secondary vCPUs). The remaining registers are reserved and are part of the hypervisor-pvmfw ABI.
When the pVM is run, the hypervisor first hands control of the primary vCPU over to pvmfw. The firmware expects crosvm to have loaded an AVB-signed kernel, which can be a bootloader or any other image, and an unsigned FDT to memory at known offsets. pvmfw validates the AVB signature and, if successful, generates a trusted device tree from the received FDT, wipes its secrets from memory, and branches to the entry point of the payload. If one of the verification steps fails, the firmware issues a PSCI SYSTEM_RESET
hypercall.
Between boots, information about the pVM instance is stored in a partition (virtio-blk device) and encrypted with pvmfw's secret to ensure that, following a reboot, the secret is being provisioned to the correct instance.