Google Adapts Linux KVM to Shield Mobile Enclaves Without New Silicon

Protected KVM on Arm64: A Technical Deep Dive - Quentin Perret, Google — The Linux Foundation

The Linux Foundation

By enforcing Stage-2 translation on the host kernel via software at EL2, pKVM delivers Intel TDX-level memory isolation on standard Arm chips but faces upstream hurdles over unfixable linear map crashes.

AI-assisted summary of Protected KVM on Arm64: A Technical Deep Dive - Quentin Perret, Google — original source. Check the original for full context.

Source published:

Key ideas

  • Protected KVM (pKVM) extends KVM/arm64 nVHE mode to provide confidential computing on Arm v8.0+ SoCs without specialized hardware extensions like Intel TDX or AMD SEV, enforcing memory isolation via an enlightened EL2 software hypervisor. (0:00)
  • The architecture activates Stage-2 MMU translation on the EL1 host kernel itself, identity-mapping host memory while encoding ownership IDs in bits 1–63 of invalid Stage-2 PTEs with zero runtime metadata tree overhead. (12:36)
  • To prevent kernel panics when host tools like strace trigger remote user page lookups on donated memory, upstream kernel work is evaluating guest_memfd and secretmem extensions supporting zero-copy in-place memory conversion. (31:16)
  • Attestation and secure workloads like 4K DRM rely on a hypervisor-injected bootloader and hardware attestation keys, which Android intentionally revokes upon bootloader unlocking to prevent unauthorized access. (48:15)

Detailed notes

## Background This presentation was delivered by Quentin Perret of Google's Android Virtualization team and published by The Linux Foundation on YouTube on October 8, 2022. According to the page description, the talk is a technical deep dive into Protected KVM (pKVM), an extension of KVM/arm64 that brings confidential computing to Arm v8.0+ architectures without dedicated hardware extensions by managing the CPU's stage-2 MMU from the nVHE EL2 hypervisor.

## TL;DR Quentin Perret details the architecture and upstream Linux kernel implementation of Protected KVM (pKVM) on Arm64. He explains how pKVM elevates the lightweight nVHE EL2 context-switching stub into a hardened hypervisor that enforces stage-2 translation tables on both guests and the untrusted host Linux kernel. The talk walks through platform bootstrapping, memory ownership models tracked via unused software bits in stage-2 PTEs, dynamic memory donation, vCPU lifecycle management, and incremental page zeroing during teardown. Finally, Perret covers major technical hurdles around handling host linear map faults, proposing integrations with mechanisms like guest_memfd/private-fd to accommodate use cases requiring in-place zero-copy page transitions.

## Atmosphere & style The presentation carries the brisk, deeply pragmatic tone of an open-source systems engineer addressing a room of kernel peers, opening with the dry promise: 'here's the cake if you were waiting for it.' It feels rigorous, understated, and unpretentious, methodically navigating complex architectural layers, register bits, and exception vectors without marketing polish or high-level hand-waving. The technical tension peaks around the unsolved problem of host-side linear map crashes, transitioning smoothly into a highly collaborative, whiteboard-style Q&A where attendees and the speaker eagerly brainstorm kernel abstractions and low-level patch designs.

## Bulletin - [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) Protected KVM (pKVM) extends KVM/arm64 nVHE mode to provide confidential computing on Arm v8.0+ SoCs without specialized hardware extensions like Intel TDX or AMD SEV, enforcing memory isolation via an enlightened EL2 software hypervisor. - [12:36](https://www.youtube.com/watch?v=9npebeVFbFw&t=756s) The architecture activates Stage-2 MMU translation on the EL1 host kernel itself, identity-mapping host memory while encoding ownership IDs in bits 1–63 of invalid Stage-2 PTEs with zero runtime metadata tree overhead. - [31:16](https://www.youtube.com/watch?v=9npebeVFbFw&t=1876s) To prevent kernel panics when host tools like strace trigger remote user page lookups on donated memory, upstream kernel work is evaluating guest_memfd and secretmem extensions supporting zero-copy in-place memory conversion. - [48:15](https://www.youtube.com/watch?v=9npebeVFbFw&t=2895s) Attestation and secure workloads like 4K DRM rely on a hypervisor-injected bootloader and hardware attestation keys, which Android intentionally revokes upon bootloader unlocking to prevent unauthorized access.

## Key takeaways - [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) pKVM delivers guest confidentiality and integrity on standard Arm v8.0+ processors by interposing access-control restrictions via software at EL2 instead of relying on dedicated hardware extensions. - [02:44](https://www.youtube.com/watch?v=9npebeVFbFw&t=164s) Under standard KVM nVHE, the EL1 host kernel operates without Stage-2 translation, giving an untrusted host complete access to physical RAM, guest pages, and hypervisor code. - [05:04](https://www.youtube.com/watch?v=9npebeVFbFw&t=304s) Building the EL2 hypervisor directly into the Linux kernel image allows atomic updates through standard Android over-the-air pipelines and eliminates the need for a frozen, stable hypervisor ABI. - [06:56](https://www.youtube.com/watch?v=9npebeVFbFw&t=416s) During early boot, the host kernel uses the memblock allocator to carve out private physical memory frames scaled proportionally to total system RAM, which EL2 claims exclusively. - [10:21](https://www.youtube.com/watch?v=9npebeVFbFw&t=621s) pKVM manages memory access through a strict ownership model recognizing four distinct page states: Owned, Shared (Lender), Borrowed (Recipient), and Inaccessible. - [12:36](https://www.youtube.com/watch?v=9npebeVFbFw&t=756s) Because the host Stage-2 translation is identity-mapped purely for access control, pKVM repurposes ignored bits 1–63 of invalid PTEs to track owner IDs, assigning ID 0 to the host. - [15:25](https://www.youtube.com/watch?v=9npebeVFbFw&t=925s) To maintain a minimal footprint, the EL2 hypervisor relies on the EL1 host to dynamically donate physical pages for shadow VM metadata, vCPU tracking structures, and guest Stage-2 page tables. - [18:20](https://www.youtube.com/watch?v=9npebeVFbFw&t=1100s) Because EL2 execution is strictly non-preemptible, sleepable synchronization primitives like mutexes are prohibited, restricting hypervisor concurrency control entirely to custom spinlocks. - [23:23](https://www.youtube.com/watch?v=9npebeVFbFw&t=1403s) When a guest triggers a Stage-2 abort, the host takes a long-term page pin to prevent swapping and page migration before donating the backing memory and unmapping it from host Stage-2. - [25:38](https://www.youtube.com/watch?v=9npebeVFbFw&t=1538s) Guests explicitly register valid MMIO GPA ranges via hypercalls so the EL2 hypervisor can mediate host emulation while exposing only necessary transfer registers without leaking general-purpose state. - [31:16](https://www.youtube.com/watch?v=9npebeVFbFw&t=1876s) The primary upstream architectural challenge is host kernel MM: unexpected linear map faults caused by operations like process_vm_readv() cannot be fixed up cleanly and trigger fatal kernel panics. - [35:14](https://www.youtube.com/watch?v=9npebeVFbFw&t=2114s) Guest teardown leverages an incremental, loop-based page reclaim protocol where the host reschedules between individual EL2 hypercalls, avoiding prolonged non-preemptible stalls while zeroing guest memory. - [38:33](https://www.youtube.com/watch?v=9npebeVFbFw&t=2313s) Identity mapping host Stage-2 allows extensive use of large block mappings, reducing TLB miss penalties so that host translation overhead is practically undetectable in benchmarks. - [41:37](https://www.youtube.com/watch?v=9npebeVFbFw&t=2497s) pKVM strictly requires in-place memory conversion rather than fresh buffer copies to support legacy TrustZone workloads like 4K video DRM that pass hundreds of megabytes of media data. - [48:15](https://www.youtube.com/watch?v=9npebeVFbFw&t=2895s) Guest integrity relies on a device bootloader memory carveout that EL2 unmaps from the host; unlocking the device bootloader permanently purges attestation keys, preventing rooted environments from forging protected guest signatures.

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) Introduction to Protected KVM (pKVM) on arm64

Quentin Perret introduces himself as an engineer on Google's Android Virtualization team, presenting the ongoing work around Protected KVM (pKVM) on arm64 architectures. The core concept behind pKVM is extending standard KVM for arm64 to deliver confidential computing features even on application processors that lack specialized, proprietary hardware extensions. In terms of security objectives, Quentin explains that pKVM aims to achieve parity with technologies such as Intel TDX or AMD SEV by shielding guest private memory from a potentially compromised or untrusted host kernel. However, its implementation strategy diverges significantly from these hardware-rooted alternatives, relying on an enlightened software hypervisor layer instead.

Quentin notes that this presentation marks the first anniversary of the pKVM codebase, providing a natural milestone to reflect on architectural milestones achieved over the preceding year. Beyond showcasing internal Android work, he emphasizes that the concepts pioneered in pKVM are actively influencing confidential computing approaches across other CPU architectures. A primary motivation for the talk is walking through a substantial patch series posted upstream on the Linux kernel mailing list, demystifying its design to assist kernel maintainers and community reviewers with complex, open design decisions. Quentin establishes a distinctly technical tone for the session: “As opposed to the previous talk, this very much intends to be a technical talk, so here's the cake if you were waiting for it.”

- Target scope: Arm64 SoCs lacking dedicated confidential computing hardware extensions (Arm v8.0+). - Functional comparison: Delivers guest memory confidentiality and integrity protections conceptually equivalent to Intel TDX and AMD SEV. - Upstream status: A comprehensive patch series is actively undergoing public review on the `kvmarm` mailing list.

[02:44](https://www.youtube.com/watch?v=9npebeVFbFw&t=164s) Baseline Architecture: KVM in nVHE Mode

To establish how pKVM achieves host isolation, Quentin reviews the standard architecture of KVM on arm64 when running in non-Virtualization Host Extensions (nVHE) mode. The Arm architecture defines four primary Exception Levels: EL0 for user space, EL1 for operating system kernels, EL2 for hypervisors, and EL3 for low-level firmware and Secure Monitor routines. In nVHE mode, the host Linux kernel runs strictly at EL1 rather than at EL2. Because EL1 software cannot manipulate virtualization controls directly, KVM relies on an isolated execution stub running at EL2 known as the KVM world-switch code.

The world-switch code is compiled directly within the Linux source tree and linked into a dedicated section of the kernel ELF binary. During early system initialization, this code is mapped into EL2 memory and left resident to manage context switches between host and guest execution contexts whenever requested by EL1. Under standard nVHE KVM, when a guest execution context runs, the hardware Stage-2 Memory Management Unit (MMU) translates Guest Physical Addresses (GPA) to Host Physical Addresses (HPA). However, the host kernel executes with no Stage-2 translation enabled, granting it unrestricted access across physical memory—including memory pages assigned to active virtual machines and the private memory of the hypervisor itself.

```mermaid flowchart TD accTitle: Standard KVM nVHE Architecture accDescr: Diagram illustrating privilege levels EL0 through EL2 in standard nVHE mode showing unconstrained host memory access.

subgraph EL0["EL0: User Space"] HostApp["Host Applications"] VMM["Virtual Machine Manager / VMM"] GuestApp["Guest User Space"] end

subgraph EL1["EL1: Operating System Kernel"] HostKernel["Host Linux Kernel (KVM)"] GuestKernel["Guest Kernel"] end

subgraph EL2["EL2: Hypervisor"] WorldSwitch["KVM World-Switch Code"] end

HostApp --> HostKernel VMM --> HostKernel GuestApp --> GuestKernel HostKernel -->|"HVC call"| WorldSwitch WorldSwitch -->|"Stage-2 active"| GuestKernel HostKernel -.->|"Unrestricted Stage-1 direct access"| EL2 HostKernel -.->|"Unrestricted access to all RAM"| GuestKernel ```

- Exception hierarchy: EL0 (user space), EL1 (kernel), EL2 (hypervisor), EL3 (secure firmware). - Execution split: The main Linux kernel runs in EL1; minimal context-switching logic resides at EL2. - Memory privilege vulnerability: In vanilla nVHE, the host kernel operates with Stage-2 disabled, allowing it direct access to hypervisor structures and guest pages.

[05:04](https://www.youtube.com/watch?v=9npebeVFbFw&t=304s) The pKVM Philosophy and Architectural Advantages

Protected KVM subverts this conventional setup by transforming the lightweight EL2 world-switch component into an authoritative, security-enforcing hypervisor. Under pKVM, the EL2 software activates the Stage-2 MMU translation tables for the *host* kernel as well as the guest. By interposing Stage-2 address translation on the host's execution path, the hypervisor establishes deterministic access control over arbitrary physical pages throughout the system. EL2 strips host access permissions from any physical frames allocated to protected guest memory or reserved for the hypervisor's internal bookkeeping.

Quentin emphasizes that retaining the co-located nVHE deployment model delivers profound software lifecycle benefits over traditional, standalone hypervisor platforms. In particular, building EL2 directly into the primary kernel image solves the chronic Android problem of out-of-band updates, guaranteeing that hypervisor binaries deploy atomically alongside the platform kernel through established Android update channels. Furthermore, because both components are compiled from identical source commits, developers are not forced to freeze a rigid, backwards-compatible Application Binary Interface (ABI) between the EL1 host and the EL2 hypervisor: “Since the hypervisor and the kernel are part of the same image and they are by construction updated atomically, which means that we don't have to keep a stable API between the host kernel and the hypervisor.” Finally, running an open-source hypervisor at EL2 ensures complete visibility for audits, security hardening, and community-driven verification.

- Stage-2 protection for the host: EL2 configures host Stage-2 page tables to revoke EL1 access to guest private memory and hypervisor memory. - Updatability: Hypervisor binaries are bundled inside the kernel image, inheriting Android's existing kernel deployment and over-the-air update mechanisms. - ABI flexibility: Eliminates the requirement for a frozen ABI between the host kernel and hypervisor, accelerating feature iteration. - Open auditability: All code executing at the elevated EL2 privilege level remains open source in upstream Linux.

[06:56](https://www.youtube.com/watch?v=9npebeVFbFw&t=416s) Bootstrapping pKVM: Early Boot and Memory Reservation

To trace how pKVM establishes trust in practice, Quentin introduces an illustrated user character to anchor a step-by-step tour through the platform lifecycle, beginning at hardware cold boot. Trust begins with verified boot: the system bootloader verifies cryptographic signatures over the Linux kernel image using industry-standard secure boot standards before execution starts. Per architectural requirements of KVM on arm64, firmware branches into the kernel image while the processor is operating at EL2. Within its first twenty instructions, the kernel detects it is executing at EL2, installs primitive "stub vectors" (placeholder exception handlers that do nothing), and issues an `ERET` instruction to drop execution down to EL1 to execute early kernel initialization.

As Linux initializes its core memory management subsystems at EL1, pKVM must guarantee private memory reserves for the EL2 hypervisor. EL2 requires dedicated backing pages to track its private Stage-1 translation structures and the host Stage-2 page tables. To accomplish this, early boot code invokes the kernel's `memblock` allocator to carve out a dedicated physical memory pool to donate to EL2. The total footprint of this reserved pool scales dynamically with the physical memory density of the SoC.

Because KVM on arm64 cannot be loaded as a runtime kernel module, pKVM initialization completes before user space is spawned. During this initialization phase, the host prepares a temporary Stage-1 page table and allocates per-CPU EL2 stack slots. The host then triggers a hypercall (`HVC`) down to EL2. This invocation displaces the early dummy stub vectors with active pKVM exception handlers, allowing the hypervisor to turn on its Stage-1 MMU immediately and bootstrap its private runtime state.

- Entry level: Bootloader jumps into the kernel image at EL2; early kernel assembly sets placeholder stub vectors and drops to EL1 via `ERET`. - Memory carving: The host uses the `memblock` allocator early in the boot sequence to carve out dedicated physical memory frames for EL2 page tables. - Scaling factor: The hypervisor memory footprint scales proportionally with total system RAM capacity. - Hypercall transition: Host constructs temporary Stage-1 page tables and issues an `HVC` to install pKVM vector handlers and bootstrap hypervisor execution.

[10:21](https://www.youtube.com/watch?v=9npebeVFbFw&t=621s) Host Stage-2 Initialization and Memory Ownership Model

```mermaid flowchart TD accTitle: Host Stage-2 Isolation and Memory Ownership Model accDescr: Hypervisor isolates itself from the host and establishes ownership states for physical pages.

subgraph Boot["Boot & Stage-2 Setup"] A["Hypervisor enables Stage-1"] --> B["Build Stage-2 page table for Host"] B --> C["Unmap hypervisor text, data, and memory pool"] C --> D["Return to Host at EL1<br/>(Stage-2 enabled)"] end

subgraph States["Page Ownership States"] S1["Owned / Exclusive"] S2["Shared (Sharer / Lender)"] S3["Borrowed (Recipient)"] S4["Unmapped / Inaccessible"] end

D -. enforces.-> States ```

During initialization, the hypervisor allocates its internal structures out of the early memory pool and builds the stage-2 page tables for the host. Crucially, it unmaps its own code and data from this host stage-2 mapping—specifically peeling away the hypervisor text section, data section, and early memory pool so that the host can no longer access them. Once the stage-2 page table is activated, execution returns to the host at EL1, which continues booting as if nothing has changed, unaware that vast swathes of memory are now hardware-isolated behind the CPU's stage-2 translation regime. Quentin Perret notes that this isolation “makes our little friend here really happy.”

By mapping memory into its own stage-1 page table and explicitly unmapping it from the host stage-2 page table, the hypervisor effectively claims exclusive ownership over those physical pages. Within Protected KVM (pKVM), the hypervisor assumes strict responsibility for tracking page ownership across the entire platform and enforcing access-control boundaries. pKVM formally tracks several distinct owner types, including the host, the hypervisor itself, protected guest VMs, and external security environments such as Arm TrustZone. From the perspective of any given entity, a page exists in one of four distinct states: 1. **Owned**: The entity has exclusive, complete access to the page. 2. **Shared (Lender)**: The entity owns the page but has shared it with another entity. 3. **Borrowed (Recipient)**: The entity has received shared access to a page owned by someone else. 4. **Inaccessible**: The page is entirely unmapped from that entity's address space.

- The hypervisor isolates itself by stripping its own `.text`, `.data`, and memory pool allocations out of the host's stage-2 page table. - pKVM treats physical page ownership tracking as an explicit hypervisor invariant that governs all memory transitions. - Potential page owners recognized by pKVM include the host OS, the EL2 hypervisor, protected guest VMs, and TrustZone.

[12:36](https://www.youtube.com/watch?v=9npebeVFbFw&t=756s) Tracking Page Ownership via Architectural Software Bits

```mermaid flowchart LR accTitle: Host Stage-2 PTE Encoding accDescr: Layout of invalid stage-2 PTE bits used to encode memory ownership.

subgraph PTE["Stage-2 Page Table Entry (Invalid Entry: Bit 0 = 0)"] direction LR B0["Bit 0<br/>Valid Flag = 0"] B1["Bits 1–63<br/>Ignored by Hardware"] end

subgraph Usage["pKVM Software Metadata"] Owner["Owner ID (Bits 1–63)<br/>Host ID = 0x0"] Bits["Software Bits (4 bits)<br/>Ownership & Sharing State"] end

B0 --> Owner B1 --> Bits ```

To maintain ownership records without incurring heavy memory overheads, pKVM encodes tracking state directly inside the page table entries (PTEs). The Arm64 architecture reserves four software-defined bits within valid PTE descriptors; the hypervisor repurposes these bits across host, guest, and hypervisor page tables to track sharing status and transitions. Furthermore, sharing is strictly limited to peer-to-peer relationships between two distinct entities; complex multi-party sharing among three or more parties is deliberately prohibited to keep the state machine tractable.

Because the host stage-2 page table is strictly identity-mapped, the stage-2 MMU is used exclusively for access control rather than address translation. This identity mapping enables a powerful hardware trick: whenever a page is marked invalid in the host stage-2 page table (clearing bit 0), Arm hardware ignores the remaining bits (bits 1 through 63). pKVM repurposes these unused hardware bits to store the unique owner ID of the entity that currently possesses the page. By deliberate convention, the host’s owner ID is defined as zero (`0`). Consequently, an entry consisting entirely of zeros naturally represents a page owned by the host that is currently invalid/unmapped, perfectly aligning with default memory initialization.

State transitions—such as donations or sharing operations—follow a two-step validation model. The hypervisor first checks strict invariants in the initiator's page table to ensure that it has legitimate authority to initiate the transition, and then updates the page table entries of both the initiator and the recipient. For memory donations, the initiator must possess exclusive access; transitive sharing or donating a page that is actively shared is forbidden. As Perret emphasizes, “before a page can be donated to someone it has to be unshared first to reclaim to get back exclusive ownership.”

- Page ownership tracking is embedded directly into Arm64 page table entries rather than separate, bulky out-of-band metadata trees. - Valid PTEs utilize the 4 architectural software bits to record fine-grained state (such as sharing roles). - Host stage-2 PTEs utilize bits 1–63 of invalid entries (bit 0 = 0) to encode owner IDs, with host ownership assigned to ID `0`. - All memory state transitions (donation, sharing, unsharing) enforce two-phase validation: checking source invariants before applying modifications to both participants.

[15:25](https://www.youtube.com/watch?v=9npebeVFbFw&t=925s) VM Creation and the Dynamic Memory Donation Pattern

```mermaid flowchart TD accTitle: Dynamic Memory Donation during Protected VM Creation accDescr: Flow of memory allocation and hypervisor handle instantiation when creating a pKVM guest.

subgraph Host["Host (EL1)"] H1["1. Allocate memory pages for guest metadata"] H2["2. Issue VM creation Hypercall"] H3["5. Receive opaque Shadow Handle<br/>(Analogous to userspace vmfd)"] end

subgraph Hypervisor["Hypervisor (EL2)"] HY1["3. Convert allocated pages to EL2-private memory<br/>(Host-to-EL2 Donation)"] HY2["4. Allocate internal structures:<br/>- Shadow VM (struct kvm)<br/>- Shadow vCPUs (struct kvm_vcpu)<br/>- Stage-2 Page Table Root"] end

H1 --> H2 H2 --> HY1 HY1 --> HY2 HY2 --> H3 ```

When a user on a pKVM-enabled machine creates a protected guest VM, the host kernel must perform several extra orchestration steps. Under pKVM’s design principle of minimal EL2 footprint, the hypervisor operates with the bare minimum amount of static memory. Whenever EL2 needs memory to manage new virtual machines, it requires the host to allocate pages from EL1 and dynamically donate them to the hypervisor. As Perret explains, “every time we need to do something with the hypervisor one of the first things we have to do is allocate memory we're going to donate to it.”

Upon receiving a guest creation hypercall accompanied by the donated pages, the hypervisor converts those pages into EL2-private memory via a host-to-hypervisor memory donation. It then allocates and initializes its private EL2 virtualization metadata, creating an EL2-side VM structure (historically termed a shadow VM), per-vCPU tracking structures, and the root of the guest's stage-2 page table. The hypervisor retains internal pointers back to the corresponding host data structures and returns an opaque integer handle back to the host. Perret compares this mechanism to userspace virtualization interfaces: “The handle that the hypervisor has allocated is what the host can then use in order to talk to the hypervisor when it talks about that VM so it's comparable to the to the `anon_inode:kvm-vm` file descriptor (`vmfd`) that user space has when you create a VM with an `ioctl`.”

Perret also mentions a terminological shift occurring in the upstream patch reviews. While earlier iterations of the patch series described these isolated EL2 structures as "shadow" objects (such as `kvm_shadow_vm`), the development team began phasing out the "shadow" naming convention to avoid confusion. Perret notes that the upstream series was actively evolving even on the day of the presentation: “I think we're in the process of renaming those data structures to to not be called Shadows anymore so I think this might even be sort of out of date as of this morning because I think Will has posted a patch series.”

- The hypervisor runs in a tightly constrained footprint; all EL2 memory scaling requires dynamic, explicit host-to-hypervisor page donations. - Guest creation requires the host to supply memory pages, which EL2 unmaps from the host and claims as private storage for hypervisor data structures. - The returned handle acts as a secure capability handle for EL1-to-EL2 hypercalls, mirroring the role of a KVM `vmfd` in userspace.

[18:20](https://www.youtube.com/watch?v=9npebeVFbFw&t=1100s) Building an OS Environment in EL2 and vCPU Scheduling

Running hypervisor logic securely at EL2 means executing code in an austere, isolated execution level. Standard Linux kernel infrastructure cannot simply be linked into EL2. To simplify development and preserve sanity, the team gradually imported adapted kernel primitives into EL2 so that Linux developers working in the hypervisor feel right at home. However, EL2 has strict environmental constraints: because the hypervisor is strictly non-preemptible, blocking synchronization primitives like mutexes are prohibited, leaving spinlocks as the sole locking mechanism.

Over time, the developers implemented a rich suite of EL2 runtime utilities: - Custom spinlocks with debugging features to verify lock assertions and tracking. - A minimalist buddy-style page allocator tailored to strict static memory boundaries. - A specialized EL2 virtual mapping engine (`vmap`) alongside an ultra-compact `struct hyp_page` abstraction. - Tracing infrastructure capable of routing hypervisor-generated trace events back out to the host's Linux tracing subsystem (ftrace).

Once a guest and its virtual CPUs are fully instantiated, the host schedules and runs them. Upstream KVM relies on `vcpu_load()` and `vcpu_put()` to bind a virtual CPU to a physical CPU core and optimize the entry-exit run loop. pKVM mirrors this lifecycle across hypercalls, introducing dedicated `vcpu_load` and `vcpu_put` EL2 entry points. However, because the untrusted host drives the execution, the hypervisor cannot blindly accept a load request; whenever the host issues a `vcpu_load` hypercall, EL2 must perform rigorous internal sanity and security validations before allowing guest code execution.

- EL2 execution is entirely non-preemptible, restricting synchronization strictly to spinlocks rather than sleepable locks. - Core kernel abstractions—such as `vmap`, `struct hyp_page`, lockdep-style assertions, and ftrace hooks—were ported directly into the EL2 hypervisor context. - Scheduling a protected vCPU leverages hypercall-driven `vcpu_load` and `vcpu_put` routines that enforce rigorous security sanity checks before executing guest contexts.

[20:30](https://www.youtube.com/watch?v=9npebeVFbFw&t=1230s) vCPU Lifecycle: Load, Put, and the Sync/Flush Paradigm

Before pKVM allows a vCPU to be loaded, the hypervisor enforces strict verification checks at EL2. It must ensure that the target vCPU is not already loaded elsewhere on another core, and conversely, that the current physical CPU does not already have a vCPU loaded onto it. Once a vCPU is successfully loaded, subsequent operations depend on that established state; as Quentin Perret emphasizes, “subsequent hypercalls such as vcpu run will require that the vcpu has been loaded before and this is this will be strong hard requirement.”

When releasing the reference via a `vcpu_put` hypercall, the hypervisor performs what is known as syncing the shadow vCPU state back to the host (for non-protected VMs), drops the reference, and clears the per-CPU tracking structures maintained at EL2. Quentin clarifies the terminology of "sync" and "flush," which have historical roots in KVM/arm64 but can cause confusion: - **Sync**: Moving state from the EL2 hypervisor context back into the host's EL1 copy. This routinely occurs after running a vCPU so the host kernel can inspect fault info or handle virtual exits. - **Flush**: Moving state the other way around—from the EL1 host structures into the EL2 hypervisor structures before entering the guest.

```mermaid flowchart LR accTitle: KVM arm64 Sync and Flush Cycle accDescr: Diagram illustrating the flow of state between Host EL1 and Hypervisor EL2 during flush and sync operations. subgraph Host["Host (EL1)"] HState["Host vCPU State"] end subgraph Hyp["Hypervisor (EL2)"] Shadow["EL2 Shadow vCPU"] end HState -->|"Flush (cherry-picked trusted state)"| Shadow Shadow -->|"Sync (exit info & registers)"| HState ```

[21:47](https://www.youtube.com/watch?v=9npebeVFbFw&t=1307s) Executing vCPUs and Handling Exits

When it is time to execute the virtual processor, the `vcpu_run` hypercall in pKVM requires essentially no parameters because EL2 expects the vCPU to already be bound to the physical CPU; any arguments supplied by the host are simply ignored. During the run transition, the hypervisor flushes only a carefully cherry-picked, minimal subset of host-provided vCPU state that can be trusted without compromising security, then switches to the guest using the protected EL2 vCPU state before entering execution.

Upon a guest exit, the flow reverses: the hypervisor copies necessary exit metadata from the EL2 shadow state back to the host. A small set of exits can be handled entirely within EL2 without waking the host kernel—such as lazily switching Floating Point (FP) register state between host and guest or minimal vGIC handling. However, these are exceptions rather than the rule; the vast majority of exits are punted to the host. Because the host maintains its own `kvm` and `vcpu` structures populated with minimal exit context, standard KVM exit-handling code runs largely unmodified.

[23:23](https://www.youtube.com/watch?v=9npebeVFbFw&t=1403s) Memory Abort Handling and Stage-2 Faults

A major departure from standard KVM occurs during memory aborts (data or instruction aborts). When a guest encounters an unmapped page, the EL2 hypervisor copies the Exception Syndrome Register (ESR_EL2) and Faulting Intermediate Physical Address (IPA/GPA) into the host’s vCPU structure, returning to EL1 with a request to handle the fault. The host locates the corresponding `memslot` and translates the GPA to a Host Virtual Address (HVA). In pKVM's implementation, rather than performing standard get-user-pages (`gup`) and mapping the page immediately, KVM takes a long-term pin on the page backing the HVA:

> “The reason we take a long-term pin is to prevent swap and page migration because we are about to donate that page to the guest and lose access to it so we don't want KSM or whatever to mess with it.”

Following the pin, the host tops up its per-vCPU memory cache (`kvm_mmu_memory_cache`) and issues a `pkvm_guest_map` hypercall. EL2 then replenishes its own internal memory cache from the host-supplied pages, enforcing a strict host-to-hypervisor memory donation for every single cache page to preserve trust boundaries. Once EL2 holds sufficient memory, it constructs the guest's stage-2 page tables. For protected VMs, the memory is completely donated to the guest and entirely unmapped from the host's stage-2 page table; for standard, unprotected VMs, the page is shared, retaining its mapping in host stage-2. Finally, control returns to the host to re-enter `vcpu_run`.

```mermaid flowchart TD accTitle: Protected Stage-2 Memory Abort Resolution accDescr: Step-by-step sequence of handling a stage-2 data abort from a protected guest to EL2 and Host EL1. subgraph Fault["1. Guest Abort"] A["Guest Stage-2 Abort"] --> B["EL2 saves ESR & GPA to Host vCPU"] end subgraph HostAction["2. Host EL1 Resolution"] B --> C["Host converts GPA to HVA"] C --> D["Take long-term page pin (block swap/migration)"] D --> E["Host fills memcache & calls pkvm_guest_map"] end subgraph HypAction["3. Hypervisor EL2 Mapping"] E --> F["EL2 donates host cache pages to Hyp"] F --> G["Donate page to Guest & unmap from Host stage-2"] G --> H["Resume vcpu_run"] end ```

[25:38](https://www.youtube.com/watch?v=9npebeVFbFw&t=1538s) MMIO Handling, Guest Sharing, and Cold Booting

Memory-mapped I/O (MMIO) introduces unique architectural hurdles because EL2 has no concept of KVM `memslots`, and for confidentiality reasons, guest general-purpose registers cannot be exposed to the host upon an exit. To resolve this, pKVM exposes dedicated hypercalls directly to the guest kernel, allowing it to declare explicit MMIO ranges in its GPA space where it permits host emulation. When an abort occurs in an authorized range, EL2 mediates access, exposing only the specific transfer register (such as `x0`) transparently to the host.

A companion hypercall interface enables protected guests to share memory pages back with the host for paravirtualized I/O queues (e.g., virtio buffers). Because guest memory is lazily populated on demand, an edge case occurs when a guest attempts to share a GPA that has not yet been backed by physical memory: - EL2 deliberately fudges the ESR syndrome before returning to EL1, spoofing a data abort. - This tricks the host into donating the physical page via standard fault resolution. - Upon re-entering the guest, EL2 replays the original share hypercall instruction (`HVC`/`SMC`).

For loading guest firmware and payloads, pKVM relies on the early bootchain. During platform boot, the Android Bootloader loads a trusted guest bootloader image into a reserved memory region specified in the host Device Tree (DT). When EL2 initializes and unmaps its own private memory from the host, it simultaneously unmaps this guest bootloader reservation. Later, when the Virtual Machine Manager (VMM) sets up a protected guest, it designates the target GPA base for the bootloader. On the guest's initial boot cycle, EL2 forces the vCPU program counter directly to that base, deliberately inducing page faults; as the host donates pages to back those addresses, EL2 populates them with the trusted guest bootloader code, which verifies and measures the final guest kernel image before execution.

[28:47](https://www.youtube.com/watch?v=9npebeVFbFw&t=1727s) Lazy Host Stage-2 Faults and Security Violations

Protected KVM enforces stage-2 translations not only on guests, but also on the host itself. Like guest mappings, the host's stage-2 translation table is constructed lazily. Whenever a stage-2 fault occurs at EL2 due to host access, EL2 walks the host stage-2 page tables to assess ownership:

1. **Host-owned page**: If the page table entry (PTE) is marked invalid, EL2 inspects software bits 1 to 63. If the encoded owner ID matches the host, EL2 installs a valid stage-2 mapping and resumes execution. 2. **Race condition**: If the PTE is already valid, another host CPU resolved the mapping concurrently; EL2 returns to host execution immediately. 3. **Host violation**: If the PTE is invalid and the software ID belongs to a protected guest or the hypervisor, the host has attempted an unauthorized access to private memory: “and the ID we find in the remaining bits is not the one of the host then we have a problem it means we've got the host accessing private memory and this is where the fun begins.”

When a host stage-2 access violation occurs, EL2 re-injects the exception into the host kernel at EL1. If the fault originated from EL0, a host user-space process—typically the VMM retaining an obsolete virtual address mapping to a donated guest page—attempted the read or write. EL2 re-injects the fault while setting an auxiliary flag bit in the ESR so the host kernel can distinguish this violation from routine demand paging; the host kernel identifies that user space touched illicit memory and terminates the offending process. Conversely, if the violation originated directly from the EL1 host kernel, the situation is far more critical.

[31:16](https://www.youtube.com/watch?v=9npebeVFbFw&t=1876s) Host Fault Handling and Core MM Challenges

Quentin Perret explains the tricky problem of dealing with host-side faults when the host tries to touch memory that it has donated or shared. If the host attempts an illegal access to protected guest memory, the hypervisor can intercept the exception, modify (or "repaint") the Exception Syndrome Register (ESR) so it looks like a same-level fault, and re-enter the kernel with the attitude of: "there you go, you've caused an exception yourself to enter the exception handler, do deal with it." However, while there are situations where the Linux kernel can gracefully handle a same-level fault, those cases are limited.

For instance, if a compromised or malicious Virtual Machine Manager (VMM) in user space executes a system call and passes pointers to protected guest memory as parameters, the kernel accesses that memory using routines like `copy_to_user` or `copy_from_user`. Under those circumstances, the kernel's fault fixup mechanisms kick in, the same-level fault is handled cleanly, and the kernel simply fails the syscall with an error code (such as `-EFAULT`).

The scenario falls apart when an external process inspects that same memory. Quentin provides an example using `strace`: if an `strace` process tries to examine the arguments of a malicious VMM's system call, it does so using the `process_vm_readv` syscall. That syscall invokes `get_user_pages_remote()` and proceeds to read the target pages through the kernel's linear mapping (direct map). When the hypervisor traps this access and refuses it, the kernel has no fixup routine for an unexpected fault inside the linear map, leaving it with no option but to crash or panic the entire machine. As Quentin summarizes:

> “This is in my opinion the biggest remaining problem with the host side implementation of pKVM. I think the hypervisor is now getting pretty solid, but there are still work that needs to be done on that front before we can we can land this upstream.”

- **Handled cleanly:** `copy_from_user()` / `copy_to_user()` access faults fail the immediate syscall. - **Fatal crash:** Linear map accesses initiated by `process_vm_readv()` via `get_user_pages_remote()` trigger unhandled kernel panics.

---

[32:56](https://www.youtube.com/watch?v=9npebeVFbFw&t=1976s) Proposed Upstream Solutions: guest_memfd/private-fd, secretmem, and Guest Killing

To resolve this host MM problem without taking down the entire machine, Quentin outlines several potential avenues being explored in the upstream Linux community:

1. **Extending `private-fd` / `guest_memfd`:** Originally proposed by Intel TDX developers, the private file descriptor mechanism offers several appealing properties. If implemented correctly, it prevents swapping and page migration on protected pages, stops kernel side-channel/linear map access, and provides a clean API to build hypervisor-assisted page migration in the future. However, pKVM strictly requires support for "in-place conversions"—allowing non-destructive memory sharing, unsharing, and page donation without discarding contents. 2. **Extending `secretmem`:** The pKVM team is evaluating `secretmem` (secret memory) as another possibility. In pKVM, having guest memory mapped into the VMM user space is not inherently problematic because the hypervisor can handle user faults cleanly; the critical risk is when the host kernel attempts to touch it. Extending `secretmem` with features borrowed from the private FD proposal could eliminate direct map access while keeping VMM mappings viable. 3. **Hypervisor Guest Termination:** A third, far less appealing workaround is to have the hypervisor simply terminate the guest whenever the host kernel faults on its memory. Under this design, the hypervisor would kill the guest, poison (zero out) all its memory, return ownership to the host, and allow the host kernel's read or write to proceed with poisoned data rather than panicking. While this preserves existing long-term GUP (Get User Pages) pins without requiring invasive changes to Linux core MM, Quentin warns that it introduces heavy complexity at EL2, unjustly punishes the guest for host behavior, and notifies KVM of guest death purely asynchronously—only surfacing the next time a `KVM_RUN` hypercall is issued, making debugging nearly impossible.

---

[35:14](https://www.youtube.com/watch?v=9npebeVFbFw&t=2114s) Guest Teardown and Incremental Page Reclaim

When a protected guest finishes running, the host initiates teardown by issuing a hypercall. The hypervisor must verify that no guest vCPUs are currently running or loaded before tearing down the resources. All memory pages originally donated to the guest must be returned to the host memory management subsystem, but they must first be poisoned (cleared) so that leftover guest secrets are not exposed to the host OS.

Because the EL2 hypervisor runs in a strictly non-preemptible environment, clearing gigabytes of guest memory within a single continuous hypercall would stall EL2 execution for seconds or minutes—a latency spike that cannot be tolerated. To bypass this restriction, pKVM uses a staged, incremental reclaim protocol.

```mermaid flowchart LR accTitle: pKVM Guest Teardown and Incremental Page Reclaim accDescr: The step-by-step memory transition from guest donation to incremental page reclaiming by the host via EL2 hypercalls.

subgraph Guest Teardown A["Host Issues Teardown Hypercall"] --> B["EL2 Stops Guest & Marks Pages Pending"] B --> C["Host Page: Pending Reclaim"] end

subgraph Incremental Reclaim Loop C --> D["Host Issues Reclaim Hypercall (Single Page)"] D --> E["EL2 Maps via Fixmap & Clears Page"] E --> F["Page Restored to Host Ownership"] F --> G{"More Pages Pending?"} G -- "Yes (Reschedule in Host)" --> D G -- "No" --> H["Teardown Complete"] end ```

When teardown begins, the guest notionally disappears, and its memory pages transition into a state labeled `HOST_PAGE_PENDING_RECLAIM`. At this stage, the memory contents remain untouched. The host kernel then executes a loop of individual hypercalls to reclaim the pages one by one. During each call, the EL2 hypervisor maps the single page using a fixed map (`fixmap`), clears it to wipe guest secrets, and transitions ownership back to the host. Because the loop runs in host kernel context at EL1, the host can freely reschedule between hypercalls, avoiding long non-preemptible stalls at EL2.

---

[37:04](https://www.youtube.com/watch?v=9npebeVFbFw&t=2224s) Future Work and Current Limitations

Quentin wraps up the technical deep dive by highlighting several major features and architectural components that could not fit into the presentation:

- **Secure World interactions:** Mediating communications between non-secure EL2/EL1 and TrustZone / Secure EL3 in the Arm architecture. - **DMA protection and SMMU:** Implementing IOMMU/SMMU drivers at EL2 to block unauthorized device DMA against protected pages. - **Hypervisor infrastructure:** Interrupt controllers (GIC), timers, power management (PSCI), and hardware true random number generators (TRNG).

He also lists notable gaps in the current patch series that are being actively developed before upstreaming: - **Missing baseline KVM features:** Basic virtualization capabilities—such as dirty page logging and read-only memory slots (`memslots`)—are not yet supported even for non-protected guests. - **Large pages:** Huge pages (2MB / 1GB) are not yet implemented for protected guests. - **System features:** Lack of `kexec` support and direct peripheral device assignment.

Quentin concludes the talk and opens the floor for questions, drawing applause from the audience.

---

[38:33](https://www.youtube.com/watch?v=9npebeVFbFw&t=2313s) Q&A: SMMU Support, Host Stage-2 Overhead, and Asynchronous Reclaim

The session turns to questions from virtual and in-person attendees:

**SMMU Support and Ownership Tracking** An online attendee asks about the timeline for SMMU (System MMU) integration in pKVM and how page ownership will be tracked across the IOMMU. Quentin responds that work is actively underway (mentioning efforts involving Linaro) to land SMMU support upstream. Page ownership tracking follows the same logic as the host CPU stage-2 tables: whenever the EL2 hypervisor updates host CPU permissions or mappings, it must walk and update the corresponding SMMU stage-2 translation tables to prevent rogue DMA access, depending in part on whether SMMU page faults can be fielded directly at EL2.

**Host Stage-2 Translation Overhead** Asked whether running the host operating system under stage-2 translation incurs noticeable performance penalties, Quentin explains that the theoretical overhead comes from increased Translation Lookaside Buffer (TLB) pressure and the cost of stage-2 TLB misses. However, in practice, the performance impact is nearly zero:

> “The host owns the vast majority of memory, and all the host mappings are identity mapped, which means we're in a position where we can very easily use really, really large block mappings to cover everything... which makes the overhead of the stage-2 practically impossible to actually measure.”

**Teardown Latency and Asynchronous Cleaning** An attendee asks about the overhead of reclaiming and zeroing pages one-by-one during teardown and whether Google has considered running this asynchronously, noting that a similar approach is being adopted for IBM s390 Secure Execution. Quentin acknowledges the s390 talk, agreeing that offloading the cleanup:

> “I think the idea of just forking that and having a separate process do the cleanup for us to hide the latency to...”

He notes that in mobile environments where VMs typically use around 1 GB of memory, synchronous incremental reclaim is not a bottleneck. However, if pKVM scales to server environments with terabytes of RAM, adopting an asynchronous background cleanup process will likely become necessary.

[41:37](https://www.youtube.com/watch?v=9npebeVFbFw&t=2497s) QEMU Memory Dumps and In-Place Page Conversion

An audience member opens the Q&A by clarifying host-side debugging behavior: if a user opens a QEMU console on the host and attempts to dump physical memory that belongs to a secure guest, QEMU crashes immediately because it hits an unmapped or restricted page, which Quentin Perret confirms with a simple "yes." Another attendee then follows up on an earlier observation regarding guest private memory, asking Quentin to explain why pKVM specifically requires in-place memory conversion rather than purely allocating fresh, out-of-band private pages.

Quentin explains that pKVM is designed to modernize and replace long-standing phone security patterns that have existed for over a decade. Android devices have executed workloads isolated from the host kernel for roughly fifteen years using Arm TrustZone, bringing along significant architectural history and technical baggage. Many modern workloads transitioning out of TrustZone into virtual machines involve massive data buffers—most notably digital rights management (DRM) and 4K media playback. As Quentin notes:

> “If you have like 4K video frames you're not gonna have megabytes... potentially it can be hundreds of megabytes of memory that are going to be passed around between the host kernel and whatever is going to be handling that into that that sort of secure Island which is TrustZone.”

To move these legacy TrustZone workloads into virtual machines efficiently without crippling performance, the hypervisor cannot afford expensive buffer copies. A zero-copy memory transfer mechanism is required between the host and the protected VM, making in-place page conversion an absolute necessity.

[43:40](https://www.youtube.com/watch?v=9npebeVFbFw&t=2620s) Proposed Upstream Mechanism for Guest Private Memory

An audience member steps up to pitch an upstream design proposal, stating they are "like 95% sure" it can reconcile pKVM's need for in-place conversion with the ongoing guest private memory work in Linux. The proposal leverages a shim around `memfd` where file operations (`fops`) bury the real operations inside an outer layer that omits the standard `mmap` wiring. Under this scheme, KVM's user-space VMM API would be extended beyond simply marking memory as shared or private by introducing a third flavor: user-space mappable memory.

```mermaid flowchart TD accTitle: In-Place Conversion Flow accDescr: Workflow showing host user space filling memory and converting it in place via KVM. subgraph HostSpace["Host User Space"] A["Allocate Memory via memfd shim"] --> B["Populate / Fill Data Buffer"] B --> C["Issue In-Place Convert ioctl"] end subgraph Hypervisor["KVM / pKVM Hypervisor"] C --> D{"Hypervisor Allows In-Place?"} D -- "Yes (pKVM)" --> E["Map into Guest & Zero Memory"] D -- "No (TDX / SEV-SNP)" --> F["Reject Conversion"] end ```

This user-space mappable state is permitted only while the page is not mapped into the guest. KVM can then manage the mapping internally, avoiding any `memslot` updates and circumventing Sleepable Read-Copy Update (SRCU) synchronization bottlenecks. The host user space fills the memory buffers while mapped, requests an in-place conversion, and—if the underlying hypervisor allows it—the memory is zeroed or transitioned directly into the guest. While hardware-isolated technologies like Intel TDX and AMD SEV-SNP disallow this style of in-place conversion due to their hardware tracking constraints, pKVM permits it. Quentin welcomes the idea enthusiastically, noting he has seen patches floating on the mailing lists and is eager to explore them.

[45:03](https://www.youtube.com/watch?v=9npebeVFbFw&t=2703s) Page Table Ownership and Fault Handling

A question is raised regarding the mechanics of pKVM's stage-2 page tables: since ownership IDs are stored exclusively inside invalid page table entries (PTEs), could a fault handler ever need to know the owner of a valid, currently mapped PTE? Quentin clarifies that if a page is validly mapped in the host's stage-2 page table, the host has legitimate access to it and will not trigger a stage-2 translation fault.

The questioner presses further, asking what occurs if a guest attempts to touch a page that is currently mapped into the host. Quentin clarifies that ownership ID tracking inside invalid PTEs is applied specifically to the host's stage-2 page tables. When a guest triggers an instruction or data abort on an invalid PTE, the hypervisor does not arbitrarily pull an unassigned physical page to resolve it. Instead, guest memory mappings require an explicit host donation directed at a specific intermediate physical address (IPA):

> “Every time we we hit an invalid PTE into into the guest we're not going to map some just a memory random pages into the guest we'll have to have the host tell us this is the page that you can map into the guest at that IPA right now.”

The hypervisor then performs strict access-control checks to ensure the host actually owns the page before transferring ownership and mapping it into the guest's IPA space. Crucially, memory sharing within pKVM is constrained strictly between two defined entities at any given moment.

[46:51](https://www.youtube.com/watch?v=9npebeVFbFw&t=2811s) Guest Attestation and Firmware Boot Flow

An attendee asks how guest payloads are attested. While the host kernel on Android is authenticated via the standard device bootloader verification chain, guest boot firmware requires an entirely independent chain of trust. Quentin explains that the host is intentionally blind to the guest's bootloader. Instead, the pKVM hypervisor directly copies a trusted, cryptographically signed bootloader image into the guest's memory and forces the guest vCPU to execute that bootloader first.

```mermaid flowchart TD accTitle: pKVM Guest Attestation Flow accDescr: Sequence showing device bootloader reserving firmware down to guest secret derivation. subgraph EarlyBoot["Early Device Boot"] A["Device Bootloader writes Guest FW to Carveout"] --> B["Hypervisor unmaps Carveout from Host"] end subgraph GuestInit["Guest Launch"] B --> C["Hypervisor copies FW into Guest Memory"] C --> D["Guest forces Execution of Trusted Bootloader"] D --> E["Bootloader loads Payload via virtio / RAM"] E --> F["Bootloader measures Payload & derives Identity Keys"] end ```

In the initial implementation, this trusted guest bootloader is based on U-Boot, though the team plans to rewrite it in a memory-safe, higher-assurance language. Once executing, the trusted bootloader receives the actual guest payload (for example, through `virtio` or direct memory inspection) and measures its contents. Because the trusted bootloader receives access to hardware-derived secrets that are completely hidden from the host kernel, it generates an authenticated, portable identity for the guest. The guest can subsequently present this identity to remote parties to prove that it booted an untampered payload inside an authentic pKVM enclave.

[48:15](https://www.youtube.com/watch?v=9npebeVFbFw&t=2895s) Hardware Root of Trust and Device Unlock / Rooting

The discussion concludes with an inquiry into the root of trust underpinning the guest bootloader: how does the hypervisor verify that the bootloader image it injects has not been tampered with if a device has been modified or rooted? Quentin explains that during early system boot, the primary device bootloader places the trusted guest firmware image into a dedicated physical memory carveout. The pKVM hypervisor immediately unmaps and isolates this carveout from the host kernel, ensuring the host OS cannot manipulate the binary.

When asked how DRM use cases remain secure if an end user unlocks the bootloader or roots their smartphone, Quentin explains the broader hardware security model used across Android:

> “If you root the device the the keys that the bootloader can use to sign whatever payload is done are removed so we just wipe the keys when you [unlock] the device.”

By intentionally erasing device-unique hardware attestation keys upon unlocking the bootloader, rooted devices lose the ability to forge valid attestation signatures. Furthermore, the hypervisor refuses to supply trusted bootloader secrets or signing keys to non-protected virtual machines. Without these burnt-in hardware credentials, a modified guest or rooted environment cannot masquerade as an authorized DRM endpoint. Quentin closes the session to applause.

## Notable quotes > [38:33](https://www.youtube.com/watch?v=9npebeVFbFw&t=2313s) The host owns the vast majority of memory, and all the host mappings are identity mapped, which means we're in a position where we can very easily use really, really large block mappings to cover everything... which makes the overhead of the stage-2 practically impossible to actually measure. — Quentin Perret

> [23:23](https://www.youtube.com/watch?v=9npebeVFbFw&t=1403s) The reason we take a long-term pin is to prevent swap and page migration because we are about to donate that page to the guest and lose access to it so we don't want KSM or whatever to mess with it. — Quentin Perret

## Mentioned Quentin Perret, Google, Protected KVM (pKVM), KVM, arm64, Linux Foundation, Android, Intel TDX, AMD SEV, AMD SEV-SNP, Arm CC-A, Arm TrustZone, Linaro, IBM s390, QEMU, U-Boot

## Video description Protected KVM on Arm64: A Technical Deep Dive - Quentin Perret, Google

Protected KVM (a.k.a. pKVM) is an extension of KVM/arm64 providing a Confidential Computing solution for Arm v8.0+ CPUs. pKVM targets SoCs that don't feature Confidential Computing hardware extensions, which makes it applicable to a wide spectrum of domain-spaces, including mobile (Android). pKVM extends the existing KVM/arm64 nVHE hypervisor with the ability to manage the CPU's stage-2 MMU, hence allowing the enforcement of access-control restrictions on host accesses to guest memory. In this talk, we will do a technical deep dive on pKVM, describe its architecture and implementation [1], and discuss opportunities for sharing core infrastructure (e.g. memory management) with other Confidential Computing solutions such as Intel TDX, AMD SEV or Arm CC-A. [1] https://lore.kernel.org/kvmarm/20220519134204.5379-1-will@kernel.org/

[Source](https://www.youtube.com/watch?v=9npebeVFbFw)

中文简报

谷歌改造Linux KVM以在无需新芯片的情况下保护移动安全飞地 通过在EL2借助软件对宿主内核强制执行第二阶段转换,pKVM在标准Arm芯片上实现了媲美Intel TDX的内存隔离,但因无法修复的线性映射崩溃而面临上游合并阻碍。 Quentin Perret详细介绍了Protected KVM(pKVM)在Arm64上的架构以及上游Linux内核实现。他解释了pKVM如何将轻量级nVHE EL2上下文切换存根提升为强化型管理程序,从而对客户机以及不可信的宿主Linux内核同时强制执行第二阶段页表转换。本次演讲梳理了平台引导、通过第二阶段PTE中未使用的软件位跟踪的内存归属模型、动态内存捐赠、vCPU生命周期管理,以及拆除期间的增量页面清零。最后,Perret探讨了处理宿主线性映射错误相关的重大技术障碍,并提出与guest_memfd/private-fd等机制集成的方案,以适应需要原位零拷贝页面转换的用例。 Protected KVM(pKVM)扩展了KVM/arm64 nVHE模式,无需借助Intel TDX或AMD SEV等专用硬件扩展,即可在Arm v8.0+ SoC上提供机密计算,通过增强的EL2软件管理程序强制实施内存隔离。 (0:00) 该架构在EL1宿主内核自身上激活第二阶段MMU转换,在对宿主内存进行恒等映射的同时,将归属ID编码在无效第二阶段PTE的第1至63位中,实现零运行时元数据树开销。 (12:36) 为防止strace等宿主工具在已捐赠内存上触发远程用户页查找时发生内核崩溃,上游内核工作正在评估支持零拷贝原位内存转换的guest_memfd和secretmem扩展。 (31:16) 证明和4K DRM等安全工作负载依赖于管理程序注入的引导加载程序和硬件证明密钥,Android在引导加载程序解锁后会特意撤销这些密钥以防止未授权访问。 (48:15)

Reading summary

Google's Protected KVM (pKVM) extends standard Arm64 KVM to deliver confidential computing on standard application processors without proprietary hardware extensions, using an enlightened EL2 software hypervisor to isolate guest and hypervisor memory from an untrusted host kernel. Host Isolation and Architecture in nVHE Mode (2:44) Standard KVM on Arm64 operating in non-Virtualization Host Extensions (nVHE) mode executes the host Linux kernel at EL1 while delegating low-level context switches to an isolated EL2 world-switch stub. In vanilla nVHE, the host operates with Stage-2 address translation disabled, leaving physical memory—including hypervisor code and guest payloads—exposed to host compromise. pKVM subverts this baseline by activating Stage-2 translation on the host itself, stripping EL1 access permissions from physical frames assigned to protected guests or reserved for hypervisor bookkeeping. Retaining the co-located nVHE deployment model offers lifecycle advantages over standalone hypervisors. Because the EL2 hypervisor is compiled directly within the kernel binary and updated atomically through existing Android channels, developers avoid freezing a backwards-compatible ABI between EL1 and EL2. Quentin Perret notes that the hypervisor's memory footprint remains nearly unmeasurable in performance overhead because host mappings are identity-mapped using large block translations. Page Ownership Tracking and Invariant Validation (10:21) pKVM enforces physical memory isolation through an explicit page ownership model tracked directly inside Arm64 page table entries (PTEs). Valid descriptors use four architectural software bits to record fine-grained state, such as lender and recipient roles in peer-to-peer sharing. For invalid host Stage-2 entries, bits 1 through 63 store the owner ID, with host ownership assigned ID 0. This convention ensures that cleared memory naturally represents unmapped, host-owned space. Memory transitions—including explicit host-to-hypervisor donations, peer sharing, and unsharing—require two-phase validation. The hypervisor verifies source authority before modifying entries across both entities. The hypervisor operates within a minimal static footprint carved out at boot via the kernel memblock allocator; subsequent virtualization metadata, such as shadow VM and per-vCPU structures, requires the host to dynamically allocate and donate memory pages to EL2 in exchange for opaque capability handles. vCPU Scheduling and Stage-2 Fault Resolution (20:30) Because EL2 execution is non-preemptible, synchronization is restricted entirely to spinlocks, supported by imported kernel primitives including custom allocators, virtual mapping engines, and ftrace hooks. Virtual CPUs are scheduled through vcpu_load and vcpu_put hypercalls that validate execution state. During execution, the hypervisor flushes only minimal, trusted state to EL2 before guest entry, later syncing exit metadata back to EL1 while handling select routines, like floating-point switching, locally. When a guest triggers a Stage-2 data abort, the host pins the backing Host Virtual Address (HVA) long-term to block swapping, compaction, or Kernel Samepage Merging (KSM). The host tops up hypervisor caches before EL2 donates the memory to the guest and unmaps it from the host's Stage-2 tables. For paravirtualized I/O queues and DRM buffers, guests can share memory back to the host via explicit hypercalls. To avoid expensive copies for large 4K video frames transitioning out of legacy Arm TrustZone environments, pKVM strictly requires zero-copy, in-place memory conversion. Host Faults and Core MM Upstream Challenges (31:16) Enforcing Stage-2 translation on the host introduces complex fault-handling challenges. When a host userspace process passes illicit guest pointers via system calls, copy_from_user routines cleanly catch the fault and return an error code like -EFAULT. However, if an external tracing tool such as strace inspects the target via process_vm_readv and get_user_pages_remote, the kernel accesses the memory through its linear direct map. The hypervisor traps this unauthorized access, and because the kernel lacks fixup handlers for unexpected direct-map faults, the system panics. Perret identifies this failure mode as the primary obstacle to upstream integration. Community approaches under consideration include adapting guest_memfd/private-fd to support pKVM's in-place conversion requirements or extending secretmem to eliminate direct-map exposure while preserving userspace mappings. A third option—having the hypervisor terminate the guest, poison its memory, and let the host read proceed—was deemed undesirable due to added EL2 complexity, unfair guest termination, and delayed, asynchronous error reporting. Incremental Reclaim and Independent Attestation (35:14) When a protected guest terminates, donated memory must be zeroed before returning to the host pool. Because zeroing gigabytes of memory inside a single EL2 hypercall would trigger unacceptable non-preemptible latency spikes, pKVM uses an incremental reclaim loop. The hypervisor marks pages as pending reclaim, allowing the EL1 host kernel to issue individual hypercalls that clear and restore pages one by one while rescheduling freely between calls. Guest trust relies on an independent bootchain separate from the host kernel. During early boot, the Android bootloader loads a signed guest bootloader into a reserved memory carveout that EL2 isolates from the host. At guest startup, EL2 forces the vCPU program counter directly to this bootloader, which measures the guest payload and derives keys from hardware-backed secrets. If a device bootloader is unlocked or rooted, Android automatically wipes these hardware attestation keys, preventing modified environments from forging valid credentials for sensitive workloads like DRM. While the EL2 hypervisor core is functionally robust, upstreaming pKVM requires resolving direct-map kernel crashes and integrating SMMU drivers to prevent unauthorized device DMA. Baseline KVM features—such as dirty page logging, read-only memory slots, huge pages, and kexec support—also remain unsupported in the current patch series.

精炼阅读摘要

Google Android 虚拟化团队工程师 Quentin Perret 详细阐述了针对 Arm64 架构的 Protected KVM(pKVM)设计。该技术在缺乏专用硬件机密计算扩展的处理器上,通过将轻量级 EL2 Hypervisor 转化为权威安全执行层,实现对不可信宿主内核的内存隔离与客户机保护。 架构核心:提升 EL2 与双向 Stage-2 隔离 (5:04) 在标准 Arm64 nVHE 模式下,宿主 Linux 内核运行于 EL1,并通过 EL2 的轻量级 world-switch 代码切换虚拟机;此时宿主内核的 Stage-2 转换处于禁用状态,其可直接访问全物理内存。pKVM 改变了这一信任结构,将 EL2 代码转变为具备强制安全检查能力的 Hypervisor,同时对宿主内核激活 Stage-2 MMU 映射。EL2 通过剥离宿主 Stage-2 页表中属于受保护客户机或 Hypervisor 自身的物理页面,从硬件层级限制了 EL1 宿主内核的物理访问特权。 与独立 Hypervisor 相比,pKVM 的 EL2 代码直接与 Linux 内核源码树联合编译,并内嵌于同一个内核镜像中。Perret 指出,这种设计消除了宿主与 Hypervisor 之间维持长期稳定 ABI 的包袱,能够直接利用 Android 既有的内核空中下载(OTA)升级渠道实现原子化部署。此外,运行于 EL2 的完整代码均向 Linux 上游开源,提供了全面的社区审计透明度。 零开销页表追踪与双阶段状态流转 (10:21) 为追踪物理内存归属且不引入庞大的带外元数据结构,pKVM 直接复用了 Arm64 页表项(PTE)内的预留位。在有效的页表项中,Hypervisor 利用 4 个软件定义位记录精细的共享角色;在宿主 Stage-2 的无效页表项(bit 0 为 0)中,硬件会忽略高 63 位,pKVM 则利用这些闲置位编码当前持有者的 ID。系统将宿主所有权 ID 默认设为 0,使得全零初始化的内存天然代表属于宿主但未映射的页面。所有权状态在独占(Owned)、共享出资方(Shared)、借入方(Borrowed)和完全不可访问(Inaccessible)四种状态间转换,且仅支持两方点对点共享。 在启动阶段,系统经由安全验证启动并在 EL2 执行几条指令后降级至 EL1,早期内核通过 memblock 分配器动态切出一块随物理 RAM 容量等比缩放的私有内存池并捐赠给 EL2。当宿主触发 HVC 调用初始化 pKVM 向量表后,Hypervisor 构建宿主 Stage-2 恒等映射,并将其自身的代码、数据段及私有内存池彻底解绑。因为宿主 Stage-2 属于完全恒等映射,Hypervisor 能够广泛采用极大的块映射(Block Mapping),Perret 强调实际运行中由此引入的 TLB 压力和 Stage-2 转换开销极低、几乎不可测。 受保护客户机生命周期与渐进式内存回收 (15:25) pKVM 遵循微型 EL2 运行时原则,Hypervisor 仅保留静态内存基线,创建虚拟机所需的数据结构(如 Shadow VM、Shadow vCPU 结构体和 Stage-2 根页表)均要求宿主从 EL1 分配物理页面并动态捐赠给 EL2。在执行调度上,pKVM 复制了 upstream KVM 的 `vcpu_load` 与 `vcpu_put` 生命周期,要求在运行 `vcpu_run` 前必须在当前物理核心通过严格的安全上下文与绑定校验。在发生内存中止(Stage-2 Data Abort)时,宿主利用长期锁定(Long-term Pin)固定页面以杜绝换页(Swap)或 KSM 内存合并干扰,随后将其移交 EL2 并彻底从宿主 Stage-2 页表中剥离。 当受保护虚拟机终止时,EL2 必须清理并抹除内存中的秘密数据。但由于 EL2 环境完全不可抢占且仅支持自旋锁,一次性擦除数 GB 内存会引发灾难性的系统延迟。pKVM 因而引入分步渐进式回收协议:Hypervisor 先停止虚拟机并将页面标记为待回收状态,交由宿主内核在 EL1 通过循环逐页触发 HVC 调用。在单次调用中,EL2 利用临时固定映射(Fixmap)清零单页并归还宿主,允许宿主内核在循环迭代间隙自由调度,消除了长延迟停顿。 核心内存管理痛点与零拷贝就地转换需求 (31:16) 尽管 EL0 用户空间触发的越权可通过重写异常校验寄存器(ESR)并由内核终止问题进程,但宿主内核直接引发的违规访问仍是 pKVM 向上游合并的最大阻碍。例如,外部调试工具通过 `process_vm_readv` 和 `get_user_pages_remote()` 经由内核直接线性映射(Direct Map)读取受保护内存时,EL2 的拦截会导致内核在缺乏修复机制(Fixup Routine)的代码路径中崩溃或触发 Kernel Panic。目前上游正在评估扩展 `guest_memfd`(private-fd)或 `secretmem`,通过剥离线性映射来保护直接访问。 此外,与 Intel TDX 或 AMD SEV-SNP 依赖全新分配私有页面的机制不同,pKVM 必须支持内存的“就地转换”(In-place Conversion)。Perret 解释,这是为了接管以往运行在 Arm TrustZone 中的遗留任务(如 4K 媒体回放与 DRM)。由于视频帧数据量可达数百兆字节,虚拟机与宿主之间若执行数据拷贝会严重拖慢性能,因此系统必须允许在不销毁缓冲区块的情况下,动态完成宿主与虚拟机间的安全所有权转移。 信任链根基、外设防护与上游演进方向 (37:04) 对于受保护虚拟机的固件度量,系统在设备冷启动时由原生 Bootloader 将受信任的客户机固件(初期基于 U-Boot)写入预留内存,EL2 初始化时立即将其从宿主剥离。在虚拟机启动时,EL2 强制 vCPU PC 指针跳转至该固件执行度量并派生硬件密钥。针对设备解锁与 Root 风险,Android 体系会在 Bootloader 解锁时彻底抹除设备唯一硬件认证密钥,防止被篡改的环境伪造合法的 DRM 认证凭据。 在设备安全防护方面,pKVM 正与 Linaro 合作在 EL2 实现 SMMU(System MMU)驱动,确保 IOMMU Stage-2 转换表与 CPU Stage-2 同步更新,从而阻断恶意设备的直接内存访问(DMA)攻击。目前该架构仍在补齐基础虚拟化功能,包括脏页记录(Dirty Page Logging)、只读内存槽、针对受保护客户机的大页(Huge Pages)支持,以及跨体系架构的通用内存管理抽象落地。 pKVM 成功在缺乏硬件隔离扩展的 Arm64 平台上,通过纯软件 Hypervisor 构建出等同于硬件机密计算的保护屏障。目前该技术在宿主线性映射 Fault 处理、Linux 上游 guest_memfd 就地转换支持以及 SMMU 设备隔离方面仍存在未决工程挑战,其未来的生产级通用性取决于与上游内核核心内存子系统的进一步整合。

Detailed notes — zh

## Background 本内容由 The Linux Foundation 于 2022 年 10 月 8 日在 YouTube 平台发布,主讲人为来自 Google Android 虚拟化团队的 Quentin Perret。视频聚焦于 Arm64 架构下的 Protected KVM(pKVM),深入探讨了如何在缺乏专用机密计算硬件扩展的 Arm v8.0+ 芯片上,通过扩展 KVM/arm64 nVHE 模式并接管 CPU Stage-2 MMU,实现宿主机与受保护客户机内存的访问控制与机密计算能力。 ## TL;DR Quentin Perret 详细解析了 pKVM 的底层系统架构、内存所有权追踪模型以及虚拟机生命周期管理。pKVM 将 EL2 级别的 World-switch 代码升级为独立的微型 Hypervisor,利用 Stage-2 MMU 恒等映射与软件位编码,强制剥离 Host 对 Guest 内存和 Hypervisor 自身的访问权,并严格限定单对单的内存所有权流转。针对内存管理,演讲剖析了基于 GUP pin 锁定的按需物理页捐赠、惰性共享机制,以及在不可抢占的 EL2 环境下规避系统假死的逐页渐进式内存回收流程。最后,演讲还直面了内核线性映射访问导致的 Host 崩溃难题,评估了 Private FD、原位转换(In-Place Conversion)等解决方案,并阐释了结合硬件根密钥擦除的 Guest 安全引导与度量机制。 ## Atmosphere & style 整场演讲呈现出典型 Linux 内核社区硬核、务实且坦诚的技术交流氛围。Quentin Perret 讲解条理清晰、抽丝剥茧,既展现出精妙架构设计的自信,又毫不避讳地指出 Host 端内存访问处理中的瓶颈与设计妥协。问答环节中与现场及线上资深内核开发者的深度互动尤为热烈,技术交锋不仅激发出针对原位转换的全新构想,也让整场分享洋溢着开源协作探索技术前沿的纯粹与严谨。 ## Bulletin - [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) Google Android 虚拟化团队展示了 pKVM(Protected KVM)技术,它通过扩展 Arm64 nVHE 架构并在宿主机执行期间强制开启 Stage-2 MMU,无需额外的专有机密计算硬件即可保护客户机内存免受不可信宿主机访问。 - [13:24](https://www.youtube.com/watch?v=9npebeVFbFw&t=804s) pKVM 采用严格的单对单页面所有权状态机模型,巧妙利用 Host Stage-2 无效页表项(Invalid PTE)的第 1 至 63 位存储所有者 ID,借此实现动态内存捐赠、共享与隔离校验。 - [31:16](https://www.youtube.com/watch?v=9npebeVFbFw&t=1876s) 宿主机非法访问受保护客户机内存是目前上游合并的最大挑战;EL0 级别越界可由内核直接杀死该 VMM 进程,但 EL1 级别的线性映射访问仍会导致整机崩溃(Panic),团队正探索结合 Private FD 或 secretmem 的解决方案。 - [47:03](https://www.youtube.com/watch?v=9npebeVFbFw&t=2823s) 受保护客户机的安全引导依赖设备 Bootloader 在系统启动早期将已签名的引导镜像存入保留物理内存并对宿主机彻底取消映射,随后由 Hypervisor 负责注入并进行度量与密钥派生。 ## Key takeaways - [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) pKVM 针对 Arm v8.0+ 架构提供机密计算支持,核心安全目标与 Intel TDX 及 AMD SEV 类似,但在缺乏专用机密硬件的情况下通过纯软件与现有 Stage-2 MMU 实现。 - [02:45](https://www.youtube.com/watch?v=9npebeVFbFw&t=165s) 依托 KVM nVHE 模式,pKVM 将原本仅负责上下文切换的 EL2 代码升级为微型 Hypervisor,使其在 Host 自身执行时同样强制开启 Stage-2 转换以剥夺 Host 对私有内存的访问权。 - [02:45](https://www.youtube.com/watch?v=9npebeVFbFw&t=165s) nVHE 架构方案让 Hypervisor 代码与 Linux 内核共同编译和原子部署,既无需维护长期稳定的内部 ABI,又保证了特权代码的完全开源透明。 - [06:51](https://www.youtube.com/watch?v=9npebeVFbFw&t=411s) 系统启动时 Bootloader 必须在 EL2 拉起内核,随后内核安装临时向量表并降级至 EL1,借由 memblock 早期预留物理内存池用于支持 EL2 页表构建。 - [10:21](https://www.youtube.com/watch?v=9npebeVFbFw&t=621s) 在 Hypervisor 初始化阶段,EL2 建立持久化 Stage-1 映射,并在 Host 的 Stage-2 页表中解绑自身代码段、数据段和内存池,彻底切断 Host 的物理访问能力。 - [11:06](https://www.youtube.com/watch?v=9npebeVFbFw&t=666s) pKVM 形式化定义了内存所有权跟踪模型,页面可在独占拥有、共享借出/借入及不可访问之间转换,且严格限制为点对点单双向共享以避免状态爆炸。 - [13:24](https://www.youtube.com/watch?v=9npebeVFbFw&t=804s) 由于 Host Stage-2 采用恒等映射,pKVM 将所有者 ID 编码在无效页表项(Invalid PTE)的未用比特位中,优雅地将状态机前置校验与硬件页表统一。 - [15:25](https://www.youtube.com/watch?v=9npebeVFbFw&t=925s) 受保护 Guest 的创建依赖动态内存捐赠原则,EL2 不维护庞大的静态内存,其元数据结构(Shadow 句柄、私有 kvm/vcpu 结构)全部由 Host 按需提供并完成所有权让渡。 - [18:20](https://www.youtube.com/watch?v=9npebeVFbFw&t=1100s) EL2 运行在完全不可抢占环境下,为此团队移植并裁剪了微型自旋锁、lockdep 断言、页面分配器等基础设施,确保内核开发者的开发体验一致。 - [20:30](https://www.youtube.com/watch?v=9npebeVFbFw&t=1230s) 在 vCPU 调度循环中,pKVM 实施强制的上下文绑定检查,并在上下文切换时只向下冲刷(flush)精选的可信状态,最大化缩小宿主机带来的安全隐患。 - [23:23](https://www.youtube.com/watch?v=9npebeVFbFw&t=1403s) 处理 Stage-2 缺页时,Host 对找到的物理页获取长期 GUP pin 以禁止 Swap、迁移或 KSM 合并,并通过 Host 到 Hypervisor 的内存捐赠机制建立隔离映射。 - [25:38](https://www.youtube.com/watch?v=9npebeVFbFw&t=1538s) 针对 MMIO 和 VirtIO 等共享场景,Guest 需通过显式 Hypercall 注册白名单;若出现惰性共享未建页,EL2 会伪造 Data Abort 诱导 Host 补充页面后再重放调用。 - [28:47](https://www.youtube.com/watch?v=9npebeVFbFw&t=1727s) Host 若试图访问已捐献给受保护客户机的私有内存,EL0 用户态违规会导致该 VMM 进程被 Host 内核杀死,但 EL1 级别的内核远程访问(如调试工具走线性映射)目前仍会导致系统 Panic。 - [32:56](https://www.youtube.com/watch?v=9npebeVFbFw&t=1976s) 为根治宿主机崩溃难题,pKVM 正在调研 Private FD(支持原位转换、解共享)以及 secretmem 等结合方案,以替代会引入复杂度和延迟隐患的“杀死客户机”策略。 - [35:14](https://www.youtube.com/watch?v=9npebeVFbFw&t=2114s) 因 EL2 不可抢占,客户机拆除采用“标记待回收 + Host 循环单页回收”的渐进机制,允许在多次 Hypercall 间隙正常调度,防止因大范围内存毒化清零导致整机卡死挂起。 - [37:07](https://www.youtube.com/watch?v=9npebeVFbFw&t=2227s) Host 运行于 Stage-2 MMU 之下的性能开销在实测中可忽略不计,因为 Host 拥有的大部分内存通过 1GB 大块映射(Block mapping)实现,极大地减轻了 TLB 遍历压力。 - [41:37](https://www.youtube.com/watch?v=9npebeVFbFw&t=2497s) 在 Android 移动端场景下,为承接 4K DRM 等原本运行在 TrustZone 的大体量业务,pKVM 必须依赖原位转换(In-Place Conversion)来规避拷贝与频繁更新 memslot 的高昂代价。 - [47:03](https://www.youtube.com/watch?v=9npebeVFbFw&t=2823s) 设备一旦被解锁或 Root,底层硬件签名与根密钥将被物理擦除,从而阻止攻击者利用非受保护客户机或受损引导链路伪造合法凭证或窃取机密。 ## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) pKVM 简介与机密计算愿景 Quentin Perret 介绍了自己来自 Google 的 Android 虚拟化团队(Android Virtualization Team),并引出本次分享的核心主题:基于 Arm64 架构的 Protected KVM(简称 pKVM)。pKVM 的核心定位是 KVM 在 Arm64 平台上的扩展功能,旨在即使 CPU 缺乏专用的高级机密计算硬件扩展(如类似架构的专用硬件模块)时,也能提供机密计算(Confidential Computing)能力。 在安全目标层面,pKVM 与英特尔的 TDX(Trust Domain Extensions)或 AMD 的 SEV(Secure Encrypted Virtualization)类似,都是为了防止不可信的宿主机(Host)非法访问客户机(Guest)的内存空间。然而,pKVM 实现这一目标的底层机制却大不相同。这篇演讲正值该代码库诞生一周年,Quentin 提到目前社区中已有较大规模的补丁集(patch series)正在审核,他希望借由这场纯技术向的演讲为社区审查提供参考背景,并启发其他硬件架构的虚拟化方案。正如 Quentin Perret 所说:“pKVM is an extension of KVM for Arm64 that provides confidential computing features even on CPUs that don't necessarily have additional fancy hardware support for it.” - **演讲者背景**:Quentin Perret,供职于 Google,Android 虚拟化团队成员。 - **目标对比**:功能目标对标 Intel TDX 和 AMD SEV(防护 Host 偷窥 Guest 内存),但软硬件实现路径截然不同。 - **演讲导向**:规避宏观业务或商业原因阐述,聚焦在底层技术细节与系统实现机制。 --- ### [02:45](https://www.youtube.com/watch?v=9npebeVFbFw&t=165s) KVM nVHE 模式与 pKVM 架构原理 为了阐明 pKVM 的改造逻辑,Quentin 首先梳理了 Arm64 架构下标准的 KVM nVHE(non-Virtualization Host Extensions)运行模式。Arm 架构定义了多个异常级别(Exception Levels): - **EL0**:用户空间(User space)运行级别,包括 Host 应用程序与 Guest 应用程序。 - **EL1**:操作系统内核级别(Kernel layer)。在 nVHE 模式下,Host Linux 内核运行在 EL1,而非常见的全特权虚拟化层,这意味着 Host 自身无法直接操控硬件虚拟化扩展指令。 - **EL2**:Hypervisor 级别。负责执行一段独立的低阶代码,称为“KVM 切换代码”(World-switch code)。 - **EL3**:固件层(Firmware/Secure Monitor),本次讨论不做展开。 在标准 nVHE 模式下,World-switch 代码虽然在源码和编译层面内嵌在 Linux 内核镜像内(置于 ELF 文件的独立 section,并在启动时驻留在 EL2),但它在运行时只被动负责根据 Host 的请求,在 Host 上下文与 Guest 上下文之间进行保存与恢复。此时,硬件的 Stage-2 MMU(负责客户机物理地址 GPA 到宿主机物理地址 HPA 的转换)仅在执行 Guest 上下文时启用;而当 CPU 处于 Host 运行模式时,Stage-2 MMU 被关闭。这导致标准 KVM 下的 Host 拥有整个物理地址空间的访问权限,可以任意读取 Guest 内存或 Hypervisor 自身的私有内存。 pKVM 巧妙地利用了这种独特的架构设计,将 EL2 的 World-switch 代码升级为一个更智能、独立的微型 Hypervisor。pKVM 让 EL2 具备了在 Host 自身执行时同样强制开启 Stage-2 MMU 转换的能力。通过为 Host 施加 Stage-2 访问控制表,EL2 得以剥夺 Host Linux 内核对 Guest 私有页面以及 EL2 自身运行内存的访问权,从而用纯软件与现有硬件 MMU 实现了机密计算防护。 ```mermaid flowchart TD accTitle: pKVM nVHE Privilege Levels accDescr: Diagram shows the separation of Host, Guest, and Hypervisor across EL0, EL1, and EL2 in pKVM. subgraph EL0["EL0 (User Space)"] HostUser["Host User Space"] GuestUser["Guest User Space"] end subgraph EL1["EL1 (Kernel Layer)"] HostKernel["Host Linux Kernel<br/>(Controlled by Stage-2 MMU)"] GuestKernel["Guest Kernel<br/>(Stage-1 & Stage-2 MMU)"] end subgraph EL2["EL2 (Hypervisor Layer)"] pKVM["pKVM World-Switch & Enforcer<br/>(Manages Stage-2 Page Tables)"] end HostUser --> HostKernel GuestUser --> GuestKernel HostKernel -.->|"Hypercalls (HVC)"| pKVM GuestKernel -.->|"Traps / Faults"| pKVM pKVM -->|"Restricts Access to Memory"| HostKernel ``` 采用将 pKVM 做成 nVHE 扩展的架构,带来了三项核心优势: 1. **原子更新与维护性(Updateability)**:在 Android 生态中,内核更新通道非常成熟。Hypervisor 代码与 Linux 内核打包在同一个二进制镜像中,系统更新时能够实现原子同步部署,无需为 Hypervisor 重新造一套维护工具链。 2. **免除稳定的内部 ABI 束缚**:正因为内核与 EL2 代码一体化编译和发布,Host 内核与 EL2 Hypervisor 之间的交互接口完全不需要维持长期的向后兼容(Backwards Compatibility),团队可以自由迭代内部机制。 3. **开源透明性(Open Source)**:运行在最高执行权限等级的代码全部开源,安全社区能够对其进行审计、加固、排错并共享补丁成果。 Quentin 概括其核心变化时指出:“With pKVM, we're basically using this somewhat unusual setup to our advantage and we're basically extending the EL2 component... making this hypervisor code a little bit smarter and we're giving it the ability to enable the Stage 2 translation even when the host is running.” --- ### [06:51](https://www.youtube.com/watch?v=9npebeVFbFw&t=411s) 系统冷启动与 EL2 早期初始化流程 为了更清晰地拆解技术实现,Quentin 借由一个终端用户操作设备的故事线,从“设备冷启动(Device Boot)”的第一步开始解析 pKVM 的具体介入逻辑。 在开机阶段,安全启动链路(Secure Boot)首先启动。引导加载程序(Bootloader)通过工业标准的签名验证机制,确保加载的系统镜像完全符合设备原始设备制造商(OEM)的意图。随后,Bootloader 必须依照 Arm 架构的 KVM 通用规范,在 EL2 级别跳转进入 Linux 内核镜像。 当内核在 EL2 被接管后的前 20 条机器指令之内,它会检测当前的执行级别,迅速在 EL2 向量基址寄存器中安装一套临时的占位异常向量表(Stub Vectors),这些 Stub 向量只提供极基础的处理甚至什么都不做;紧接着内核立刻执行 `eret` 指令降级至 EL1,继续像常规操作系统一样引导 Linux 内核的基础子系统。 随着引导推进至 Linux 内存管理(MM)子系统初始化的关键节点,Hypervisor 需要预留专属的内存,用于后续存放自己的 Stage-1 页表以及即将对 Host 实施管控的 Stage-2 页表结构。pKVM 当前的实现是在早期通过内核的 `memblock` 机制分配并剥离出一片专用内存池(Memory Pool),其预留内存的体量完全取决于系统总物理内存的大小,因为管控整个地址空间所需的页表阶数和尺寸是成比例增长的。 随后进入 KVM 核心初始化流程。因为在 Arm 架构中 KVM 必须静态编译进内核而不能作为内核模块(Loadable Module)加载,所以在系统进入用户空间之前,初始化动作就已触发。在 pKVM 模式下,Host 内核会额外协助完成以下前置准备: - 为 Hypervisor 分配一套临时的 Stage-1 页表,这套临时页表使得 Hypervisor 在随后的初始化中可以立刻启用 MMU。 - 为 EL2 运行环境分配运行堆栈(Stack Slots)和基础控制页。 - 一切就绪后,Host 触发一次 Hypercall 调用陷入 EL2。 - EL2 捕获该调用后,正式用功能完整的 pKVM 向量表替换掉早期内核留在 EL2 的 Stub 向量表,并利用前期预留的物理内存重建属于 Hypervisor 自己的持久化 Stage-1 独立页表,从而开启自身的自举(Bootstrapping)过程。 Quentin Perret 描述了这一早期的降级控制转移过程:“It will detect that it is running at EL2, install what we call the stub vectors... which are dummy exception vectors basically which do nothing, and then ERET to EL1 and proceed to boot from there.” - **启动入口要求**:Arm64 规范强制要求内核必须由 Bootloader 从 EL2 入口拉起。 - **内存预留时机**:利用早期启动阶段的 `memblock` API,依据系统整体 RAM 规模静态切分捐赠给 EL2 的保留内存。 - **模块限制**:Arm64 平台的 KVM 必须内置于 Linux 内核镜像(Built-in),不采用 `.ko` 动态加载形式。 ### [10:21](https://www.youtube.com/watch?v=9npebeVFbFw&t=621s) Hypervisor 启动隔离与 Host Stage-2 映射剥离 在完成内存池的设置后,Hypervisor 会为 Host 初始化其专用的 Stage-2 页表,并将 Hypervisor 自身的代码与数据从 Host 的 Stage-2 映射中彻底解绑(unmap)。这不仅包括 Hypervisor 的代码段(text section)和私有数据段(data section),还涵盖了此前预留的 Hypervisor 内部内存池(memory pool)。一旦完成这些映射剔除,Hypervisor 便会返回到 Host 系统。从此时起,Host 将继续执行正常的引导过程,表面上看起来风平浪静、一切照常,但实质上底层的 Stage-2 内存保护机制已经被激活:Host 已彻底丧失访问这部分系统页面的能力。 Quentin Perret 幽默地指出,这种硬件级的访问权限剥离,确保了 Host 即使发生崩溃或被恶意攻陷,也无法窥探或篡改 Hypervisor 内存,这正是设计保护性隔离的关键一环,“and in fact the host is no longer able to access certain pages and systems specifically the hypervisor pages which makes our little friend here really happy.” ```mermaid flowchart TD accTitle: Hypervisor 隔离与 Stage-2 页表建立流程 accDescr: 展示 Hypervisor 如何初始化自身 Stage-1,并在 Host Stage-2 中注销自身内存以实现隔离。 subgraph INIT["Hypervisor 初始化阶段"] A["Hypervisor 建立 Stage-1 映射"] --> B["初始化 Host Stage-2 页表"] B --> C["Host Stage-2 解绑 Hypervisor 内存<br>(代码段、数据段、内部内存池)"] end subgraph BOOT["控制权交接与隔离"] C --> D["激活 Host Stage-2 转换与访问控制"] D --> E["返回 Host 内核继续 Boot 流程"] E --> F["Host 无法再访问 Hypervisor 私有物理页面"] end ``` ### [11:06](https://www.youtube.com/watch?v=9npebeVFbFw&t=666s) pKVM 的内存所有权追踪模型(Ownership Tracking) 当 Hypervisor 通过 Stage-1 页表映射自身,并从 Host Stage-2 页表中将其解映射时,从概念上讲,这意味着 Hypervisor 正式“接管”了这些物理页面的所有权。在 pKVM 的架构设计中,Hypervisor 核心职责之一便是精确追踪系统中任意时刻每个物理页面的所有者归属,并强制执行对应的访问控制限制。为此,pKVM 在体系内对所有权跟踪机制进行了形式化定义。系统中的内存实体被划分为几种不同角色:Host、Hypervisor、被保护的 Guest VM,以及类似 TrustZone 等外部安全实体。 从每个实体的视角来看,系统中的页面可能处于四种状态之一: 1. **完全独占拥有(Owned)**:该实体拥有页面所有权,且享有排他性访问权限; 2. **共享借出(Shared - Provider)**:该实体拥有页面,但将其共享借出给其他实体; 3. **共享借入(Shared - Borrower)**:该实体并不拥有页面,仅作为借入方获得访问权限; 4. **不可访问(Inaccessible)**:页面已被解映射,该实体无法访问(例如 Host 被解绑的 Hypervisor 页面)。 ```mermaid flowchart LR accTitle: pKVM 物理页面所有权状态机 accDescr: 物理页面在独占所有、共享以及不可访问状态之间的转换机制。 OWNED["独占拥有<br>(Exclusive Owner)"] SHARED["共享状态<br>(Provider / Borrower)"] UNMAP["不可访问<br>(Inaccessible)"] OWNED -->|"Share 操作"| SHARED SHARED -->|"Unshare 操作"| OWNED OWNED -->|"Donate / Unmap"| UNMAP UNMAP -->|"Reclaim / Map"| OWNED ``` - **实体类别**:涵盖 Host、Hypervisor、受保护的 Guest VM,以及预留的外部特权实体(如 TrustZone)。 - **共享模型**:当前 pKVM 仅支持点对点(Peer-to-Peer)共享。为了避免状态爆炸和逻辑复杂度,系统严格禁止三个或更多实体同时共享一个页面(即不支持任意 N 方共享)。 - **页面捐赠不可传递**:任何页面在向另一方捐赠(Donation)之前,必须先通过反向共享(Unshare)收回独占所有权,严格杜绝在共享状态下发生所有权转移。 ### [13:24](https://www.youtube.com/watch?v=9npebeVFbFw&t=804s) Stage-2 页表项(PTE)与元数据编码 在追踪机制的具体实现上,所有权信息直接下沉并编码在页表层级。pKVM 无论对于自身 Stage-1、受保护 Guest Stage-2,还是 Host Stage-2 页表,都由运行在 EL2 的 Hypervisor 统筹维护。为了记录页面由谁拥有以及与谁共享,pKVM 充分利用了 Arm64 体系结构规范中为软件预留的 4 个软件比特位(Software Bits)。 更有意思的是 Host Stage-2 页表的设计方案:在该页表中始终采用恒等映射(Identity-mapped),即物理地址与中间物理地址一一映射,页表完全用于访问控制限制,而非地址重定位。由于该特性,当某个条目无效(Invalid,即 Bit 0 为 0)时,硬件 MMU 会彻底忽略 Bit 1 至 Bit 63 的内容。pKVM 巧妙地利用了这一点,直接在无效页表项的空闲比特位中存放对应物理页面的所有者 ID(Owner ID)。特别地,Host 的所有者 ID 被定义为 0。因此,若 Host Stage-2 PTE 的所有比特全为 0,即天然且优雅地表达了“该页面所有权归属于 Host”。 每当系统执行状态转换时,Hypervisor 都会遵循严格的两步式不变量检查:首先在发起方与目标方的页表中校验前置条件(例如发起捐赠方必须享有独占访问权,而接收方必须尚无访问权),在确认满足不变量后,再原子性地修改页表项并更新软件位。 ### [15:25](https://www.youtube.com/watch?v=9npebeVFbFw&t=925s) 动态内存捐赠与 Guest VM 的创建流程 在完成引导并具备 pKVM 能力后,系统便进入了创建虚拟机的业务流程。在 pKVM 下,Host 创建 Guest 存在一系列显著区别于传统 KVM 的动作。其中最为核心的原则,是 Hypervisor 本身仅维持极度紧凑的静态内存开销,其后续管理所需的内存资源均由 Host 按需动态“捐赠”(Donate)。Quentin Perret 强调,只要需要 Hypervisor 介入处理新的结构,“分配并捐献内存”就成了一个高频模式:“Every time we need to do something with the hypervisor, one of the first things we have to do is allocate memory we're going to donate to it.” 当 Host 准备创建 Guest 时,流程如下: 1. **内存分配与 Hypercall 发起**:Host 先在自身内存中分配物理页面,随后触发 Hypercall,请求 Hypervisor 创建虚拟机,并将分配好的页面作为存储 VM 元数据的载体捐赠给 Hypervisor。 2. **所有权转换**:Hypervisor 截获该请求,执行 Host-to-Hypervisor 捐赠校验,将这些物理页面转为 Hypervisor 私有内存,并从 Host 侧彻底解映射。 3. **元数据初始化与句柄返回**:Hypervisor 在私有内存中分配所谓 Shadow 句柄(Shadow Handle),并初始化其 EL2 级别的私有控制块,包括私有的 `struct kvm`、各虚拟 CPU 的 `struct kvm_vcpu` 以及 Guest Stage-2 页表的根节点。初始化完成后,Hypervisor 将该句柄返回给 Host,作为后续管理该 VM 的不透明通信引用,其职能等同于用户空间通过 ioctl 获取的 `vm_fd`。 关于命名的演进,Quentin 补充说明道,早期补丁集普遍使用了“Shadow”(影子数据结构)这一称谓(如 `kvm_shadow_vm`),但在最新的社区上游迭代中,这些命名正逐步被重命名为私有数据结构,以避免概念混淆。 ```mermaid sequenceDiagram accTitle: pKVM 创建 Guest 内存捐献与句柄生成交互 accDescr: 展示 Host 与 Hypervisor 在创建 Guest 过程中的内存捐赠、页表解映射及句柄交互流程。 autonumber actor Host as Host (EL1) participant Hyp as Hypervisor (EL2) Host->>Host: 分配用于 VM 元数据的空闲物理页面 Host->>Hyp: 发起 Hypercall: 请求创建 VM (附带捐赠页面物理地址) Hyp->>Hyp: 状态机校验: 确保 Host 独占这些页面 Hyp->>Hyp: 将捐赠页面纳入 EL2 私有内存 (Host Stage-2 解映射) Hyp->>Hyp: 分配 Shadow Handle,初始化私有 struct kvm / vcpu / Stage-2 根页表 Hyp-->>Host: 返回 VM Handle (类似 vm_fd) Host->>Host: 在 EL1 struct kvm 中保存该 Handle 并绑定反向指针 ``` ### [18:20](https://www.youtube.com/watch?v=9npebeVFbFw&t=1100s) EL2 运行时环境构建与 vCPU 调度语义 Hypervisor 完全驻留在 Arm 的 EL2 异常级别中,运行于高度受限的环境下。它无法直接复用 EL1 的 Linux 内核基础设施,而是一个完全不可抢占(non-preemptible)的代码空间。为了让内核开发者在 EL2 编写逻辑时不至于手足无措,pKVM 团队在过去的一年多时间里,从 Linux 内核移植并裁剪了一套微型基础设施: - **同步原语**:提供自旋锁(Spinlocks)。因为 Hypervisor 严格处于不可抢占上下文,所以不存在互斥锁(Mutexes)。 - **锁与断言调试**:支持类似 lockdep 的断言功能,允许在关键路径检测特定锁是否已被持有。 - **轻量内存分配器**:实现了一套专用的页面分配器与极简的 `vmap` 机制,甚至定义了一个轻量版的 `struct hyp_page`。 - **调试与 Tracing**:正在打通与 Host Tracing 子系统的接口,允许 EL2 Hypervisor 将 Trace Event 安全地输出至 Host 的跟踪缓冲区中,便于系统级性能分析。 Quentin 指出,借鉴内核风格的设计思想,是为了让内核开发者在面对 EL2 空间时有宾至如归的开发体验:“if you're a kernel hacker, you should somewhat feel at home when you're working with the EL2 layout.” 随着 Guest VM 的创建就绪,系统便进入执行阶段。传统 KVM 拥有 `vcpu_load` 与 `vcpu_put` 的抽象,目的是让 vCPU 绑定并驻留在特定物理 CPU 上,以加速核心执行循环(vCPU run loop)。pKVM 延续了这套概念模型,Host 通过 Hypercall 触发 `vcpu_load` 和 `vcpu_put`,但由于安全边界的介入,EL2 在响应这些调用时被赋予了更为严格的安全合规检查职责。 ### [20:30](https://www.youtube.com/watch?v=9npebeVFbFw&t=1230s) vCPU 加载与运行:状态同步机制与运行流 pKVM 在允许 vCPU 加载到物理 CPU 之前,必须执行极其严格的上下文检查。例如,EL2 hypervisor 必须验证该 vCPU 当前没有在其他物理 CPU 上加载,同时也要确认当前物理 CPU 并没有已经处于加载状态的 vCPU。这一系列前提条件构成了强硬的硬性要求,所有后续的 Hypercall(如 `vcpu_run`)都必须以目标 vCPU 已成功加载为绝对前提。而在执行 `vcpu_put` 释放 vCPU 引用时,hypervisor 会把维护在 EL2 的影子状态(shadow state)与 host 进行所谓的“同步”(sync),随后丢弃引用并清理每 CPU(per-CPU)跟踪数据。 ```mermaid flowchart TD accTitle: vCPU 调度与状态同步流程 accDescr: 展示 vCPU 加载、运行时的状态冲刷、上下文切换以及退出时的状态同步全过程 subgraph Host["Host (EL1)"] H1["发出 vcpu_load"] H2["发出 vcpu_run"] H3["处理退出 / 发出 vcpu_put"] end subgraph Hypervisor["Hypervisor (EL2)"] L1{"严格检查状态<br/>(未在别处加载且当前 CPU 为空)"} L2["记录 per-CPU 追踪"] F1["Flush:精选可信状态注入"] S1["Sync:将必要状态回写至影子结构"] C1["清理引用与追踪"] end subgraph Guest["Guest (Protected VM)"] G1["Guest 上下文运行"] end H1 --> L1 L1 -->|"通过"| L2 H2 --> F1 F1 -->|"上下文切换"| G1 G1 -->|"VM-Exit"| S1 S1 --> H3 H3 --> C1 ``` Quentin Perret 解释了在 KVM/arm64 传统术语中经常出现的“同步(sync)”与“冲刷(flush)”概念:所谓 sync 是指将 EL2 的执行状态写回到 EL1 的宿主机副本中,这通常发生在 vCPU 退出后、需要 host 介入处理故障的场景;而 flush 则是反向操作,即把 host 侧的状态同步进 EL2。当调用 `vcpu_run` 时,在 pKVM 上下文里该 Hypercall 实际上不接受任何有效参数,因为系统默认目标 vCPU 已经提前加载到物理 CPU 上,传入的任何参数都会被直接忽略。在切入 Guest 之前,hypervisor 只会从 host 挑选极小一部分被认为完全可信、绝不会造成安全漏洞的状态进行 flush,随后利用 EL2 内部保存的 vCPU 拷贝完成上下文切换并运行 Guest。 对于退出事件(VM-Exit),绝大多数仍旧交给 host 处理。浮点状态(FP state)等少数资源可以在 EL2 中透明且延迟(lazily)地处理,但这属于少数例外。Quentin Perret 强调,由于 host 仍保留自己的 `kvm` 和 `vcpu` 结构体,EL2 只会将关注的状态字段填充回去,因此绝大多数 host 侧的退出处理流程与标准 KVM 几乎无异。 - **加载安全检查**:确保 vCPU 未在其他物理核心活跃,且当前物理核心未被其他 vCPU 占用。 - **状态同步语义**:`sync` 代表 EL2 到 EL1 的状态回写,`flush` 代表 EL1 到 EL2 的状态下发。 - **参数解耦**:`vcpu_run` Hypercall 不需要携带额外参数,完全依赖物理核心先前绑定的加载状态。 - **最小化信任**:上下文切换时,hypervisor 仅摄取 host 传入的极少数经过审计的安全状态字段。 --- ### [23:23](https://www.youtube.com/watch?v=9npebeVFbFw&t=1403s) 内存异常处理与页面捐献机制 在内存访问故障的处理上,pKVM 与传统 KVM 产生了关键分歧。当 Protected Guest 在执行时产生数据中止(Data Abort)或指令中止(Instruction Abort),hypervisor 在捕获该异常后,会将异常综合寄存器(ESR,Exception Syndrome Register)和客户机物理地址(GPA)写回到 host 的 vCPU 结构中,随后返回 EL1 并请求 host 处理缺页异常。Host 依旧遵循传统逻辑查找内存槽(memslot),将 GPA 转换为宿主机虚拟地址(HVA)。 在标准流程之后,pKVM 采取了截然不同的安全约束:“在当前实现中,我们并没有直接做 `get_user_pages` 然后把页面映射给客户机,而是让 KVM 对找到的 HVA 页面获取一个长期的 GUP pin。”Quentin Perret 解释道:“我们获取长期 GUP pin 的原因是为了防止页面交换(swap)和页面迁移(page migration),因为我们即将把该物理页捐献给客户机并彻底失去对其的访问权限,所以我们绝不希望 KSM 或者其他机制去碰它。” ```mermaid flowchart TD accTitle: 客户机 Stage-2 缺页与页面捐献流程 accDescr: 从 Guest 产生 Data Abort 到 Host 取页 Pin 住,再通过 Hypercall 完成向 Guest 捐献并解绑 Host 的全生命周期 subgraph Fault["1. 异常触发与转发"] A["Guest 发生 Data/Instruction Abort"] --> B["EL2 提取 ESR 与 GPA"] B --> C["回写至 Host vCPU 结构并返回 EL1"] end subgraph HostAction["2. Host 解析与内存锁定"] C --> D["Host 查找 memslot 将 GPA 转为 HVA"] D --> E["获取长期 GUP pin<br/>(防 swap/migration/KSM)"] E --> F["填充 Host 的每 CPU memcache"] F --> G["发起 pkvm_guest_map Hypercall"] end subgraph EL2Mapping["3. EL2 校验与页面重映射"] G --> H["从 Host memcache 补充 EL2 内部 cache<br/>(触发 host-to-hyp 页面捐献)"] H --> I{"是否为受保护客户机?"} I -->|"是"| J["修改 Guest Stage-2 页表<br/>并从 Host Stage-2 彻底取消映射"] I -->|"否"| K["共享映射 (保留 Host Stage-2)"] J --> L["返回 Host,继续执行 vcpu_run"] K --> L end ``` 完成 Pin 锁定后,Host 必须先给自己的每 CPU 内存缓存(`memcache`)充值,然后向 hypervisor 发起 `pkvm_guest_map` Hypercall。EL2 收到请求后,会把 Host 提供的 `memcache` 页面转移到 EL2 专用的内部内存缓存中;每从 Host 拉取一个页面,都必须经历一次完整的 Host 到 Hypervisor 的内存所有权捐献流程,以维持系统的不变量。一旦 EL2 具备足够的页表管理内存,它就会去修改 Guest 的 Stage-2 页表。如果是受保护客户机(Protected Guest),该物理页将被完全捐献,同时从 Host 的 Stage-2 页表中彻底解绑;如果是普通的非受保护客户机,则仅按传统共享方式建立映射。处理完毕后,控制权交还 Host,再次进入 `vcpu_run` 的循环。 - **长期 GUP Pin**:阻止页面的隐式回收、换出(Swap)、跨 NUMA 节点迁移或 KSM 合并。 - **双层 Memcache 充值**:Host 准备内存元数据支撑页,EL2 在接管这些页时强制执行所有权捐献。 - **Stage-2 隔离断开**:为保护 Guest,EL2 在写入 Guest Stage-2 页表的同时,强制剥离 Host Stage-2 映射。 --- ### [25:38](https://www.youtube.com/watch?v=9npebeVFbFw&t=1538s) MMIO 异常处理、内存共享与 Guest 启动加载 MMIO(内存映射 I/O)在 pKVM 下面临严峻挑战。首先,EL2 的 hypervisor 完全没有 memslot 概念;其次出于机密性考虑,Guest 发生 VM-Exit 退出时,通用寄存器绝不可能直接复制回 Host 内核。为了解决这一矛盾,pKVM 向 Guest 暴露了特定的 Hypercall,允许 Guest 主动声明其允许 Host 模拟的 MMIO 地址范围。当 Guest 在这些声明区间触发访问退出时,hypervisor 会使用特定的寄存器(如 X0)作为透明的中转寄存器传递交互数据。 类似地,系统还暴露了共享 Hypercall,允许受保护客户机主动将特定页面共享回 Host,主要用于建立 VirtIO 等 I/O 通信通道。然而,由于 Guest 的内存都是按需惰性换入(lazily paged in)的,Guest 可能会尝试共享一个当前尚未映射物理页的地址空间。面对这种情况,hypervisor 必须主动介入伪造异常综合寄存器(ESR),伪装成来自客户机的数据中止(Data Abort)返回给 Host,以此诱导 Host 触发缺页处理并捐献物理页面,待补齐页面后再在下次 `vcpu_run` 时向客户机重放该 Hypercall。 在启动加载流程中,pKVM 也形成了一套严密的受信任链条: ```mermaid flowchart LR accTitle: 受保护客户机启动加载链 accDescr: 展示从 Android Bootloader 保留内存到 EL2 隔离,再到缺页注入引导镜像的启动过程 A["Android Bootloader"] -->|"拷贝 Trusted Bootloader"| B["保留内存区域<br/>(Host DT 描述)"] B -->|"pKVM 初始化"| C["EL2 隔离该区域<br/>(从 Host 解绑)"] C -->|"VMM 指定 IPA 空间"| D["强制设置 Guest PC 触发缺页"] D -->|"Host 捐献物理页"| E["EL2 写入 Bootloader 镜像"] E -->|"启动校验"| F["加载 Guest Payload 并远程度量"] ``` 宿主机设备启动加载器(如 Android Bootloader)会先将经过签名认证的受信任 Guest Bootloader 写入一段由设备树(DT)描述的预留内存中。pKVM 初始化时,不仅会把自身占用的内存从 Host 剥离,也会把这段预留内存从 Host Stage-2 彻底取消映射。当 VMM 创建客户机并指定 Bootloader 映射的 IPA 范围后,系统在首次执行 vCPU 时会强制将客户机的程序计数器(PC)定位到该区域,进而触发缺页。随着 Host 将对应的物理页逐一捐献给 Guest,EL2 会将预留的 Bootloader 镜像直接写入这些物理页中,由其继续接管并加载 Guest 核心负载,确保进入受保护 VM 的代码绝对可信。 - **MMIO 显式白名单**:Guest 必须通过 Hypercall 主动注册允许模拟的 GPA/IPA 空间。 - **寄存器代理防护**:禁止暴露所有通用寄存器,仅复用单个透明寄存器完成 MMIO 状态交换。 - **惰性共享中的 ESR 伪造**:在未建页共享时,EL2 伪造 Data Abort 触发 Host 缺页补齐流程,随后重放 Hypercall。 - **受保护链条**:DT 标记的 Bootloader 预留区在系统启动伊始即对 Host 隐藏,随后按页捐献直接注入。 --- ### [28:47](https://www.youtube.com/watch?v=9npebeVFbFw&t=1727s) Host Stage-2 缺页与非法私有内存访问拦截 与普通虚拟化只需捕获客户机异常不同,pKVM 同样必须接管并处理 Host 产生的 Stage-2 缺页异常。Host 的 Stage-2 映射同样是惰性构建的。当 EL2 捕获到一个由 Host 引发的 Stage-2 故障时,它会去遍历 Host Stage-2 页表并核验该物理页的真实状态: 1. **PTE 无效(Invalid)**:hypervisor 会检查该条目第 1 至 63 位所记录的页面所有者 ID。如果所有者依然是 Host,说明只是尚未建表,EL2 直接补齐有效的 Stage-2 映射并恢复执行;如果所有者不是 Host,说明 Host 试图跨界访问已被捐献给 Guest 的私有物理内存。 2. **PTE 有效(Valid)**:说明发生了并发竞争,另一核心刚刚已经建好了该条目,EL2 仅需重新恢复进入 Host 执行即可。 Quentin Perret 生动地指出:“但如果页表项无效,且我们在其余位中找到的 ID 并不是宿主机的 ID,那我们就有麻烦了。这意味着宿主机正在访问私有内存,而这正是‘好戏上演’的地方。” 针对这种非法跨界访问,pKVM 采取了分层阻断策略。Hypervisor 统一将该异常重新注入回 Host,要求 Host 自行处置。如果该故障是由 Host 的用户空间(EL0)触发的,通常意味着是 VMM(如运行在用户态的虚拟机监控进程)在将内存捐献给 Guest 后,其进程内部的虚拟地址(HVA)空间仍然保留着对该物理页的旧映射,并且试图去读写它。对于 EL0 触发的越界访问,EL2 会在重新注入给 Host 的 ESR 寄存器中置位一个特殊的标志位,使得 Host 内核能够清晰识别该异常并不是普通的 EL0 用户态缺页换入事件。Host 内核在 EL1 识别到这一标志后,能够明确获知是由于用户态进程试图偷窥已保护的客户机私有内存,进而直接采取最严厉的惩罚手段——将其直接杀死。而如果越界访问直接发生在 Host 内核自身的 EL1 级别,其背后的处置和恢复流程则要复杂得多。 - **所有权元数据追踪**:无效 PTE 的 1-63 位充当状态槽,记录当前页面的确切归属 ID。 - **并发访问容忍**:对已被其他核心修复的有效 PTE,直接重新进入 Host 避免重复中断。 - **EL0 越界识别与隔离**:通过修改回注的 ESR 标志位提示 Host 内核,Host 快速定位非法访问并就地终止(SIGKILL)用户态 VMM 进程。 ### [31:16](https://www.youtube.com/watch?v=9npebeVFbFw&t=1876s) Host 处理异常的瓶颈与内存访问故障难题 当 Host 触发异常时,pKVM 现有的机制是重写异常综合征寄存器(ESR, Exception Syndrome Register),假装当前拦截到的故障是一个同级故障(Same-level fault),然后再重新返回内核,相当于在对内核说:“喏,你自己捅了篓子触发了异常,你自己去进异常处理程序解决吧。”然而,内核能够妥善处理同级故障的场景其实非常有限。 Quentin Perret 举例分析了不同场景下的差异。假设存在一个受损的恶意虚拟机监控器(VMM)或用户空间进程,它发起了一个系统调用(Syscall),并将属于受保护客户机(Protected Guest)的内存作为参数传递进去。这种情况下,内核会通过 `copy_to_user` 或 `copy_from_user` 等常规接口去访问该内存,这属于内核可以捕获同级故障的少数情况之一,内核完全可以优雅地让该系统调用直接报错返回失败。 然而,如果换一个场景:有一个调试进程正在对该恶意 VMM 进行跟踪(例如使用 `strace`)。跟踪进程会尝试去检查传入系统调用的内存数据,例如通过 `process_vm_readv` 系统调用来读取。此时内核在底层会调用 `get_user_pages_remote`(GUP),并试图直接通过线性映射(Linear map)去访问那些物理页。在这个关口,处于 EL2 的 Hypervisor 会介入并直接判定:“你不能访问这些页面”,随后强行返回内核。但此时内核在当前执行路径上根本没有机制去恢复或处理这种线性映射故障,最终结果只能是导致整台机器崩溃死机(Panic)。 Quentin Perret 明确指出:“在我看来,这是目前 pKVM Host 端实现中剩下的最大难题;我认为 Hypervisor 端现在已经相当稳固了,但在正式将其合并入上游(Upstream)之前,这块领域仍有工作亟待完成。”(“This is in my opinion the biggest remaining problem with the host side implementation of pKVM I think the hypervisor is now getting pretty solid but there are still work that needs to be done on that front before we can we can bend this Upstream.”) ```mermaid flowchart TD accTitle: Host access to protected guest memory scenarios accDescr: Comparison between handled syscall and unhandled linear map access leading to panic subgraph UserSpace["用户空间 (EL0)"] MalVMM["受损 / 恶意 VMM"] Strace["调试工具 (如 strace)"] end subgraph KernelSpace["Host 内核 (EL1)"] SyscallHandler["copy_to/from_user 处理"] LinearAccess["process_vm_readv / GUP 远程访问\n(通过线性映射)"] end subgraph Hypervisor["pKVM Hypervisor (EL2)"] S2Check["Stage-2 权限检查拦截"] end MalVMM -->|"传入 Guest 物理内存作为参数"| SyscallHandler Strace -->|"读取目标进程内存"| LinearAccess SyscallHandler -->|"触发 Stage-2 错误"| S2Check LinearAccess -->|"触发 Stage-2 错误"| S2Check S2Check -->|"重写 ESR,模拟同级故障"| SyscallHandler SyscallHandler -->|"安全返回,Syscall 失败"| MalVMM S2Check -.->|"无法恢复的故障"| LinearAccess LinearAccess ==>|"内核崩溃 (Panic)"| KernelCrash["整机崩溃 Crash"] ``` --- ### [32:56](https://www.youtube.com/watch?v=9npebeVFbFw&t=1976s) 解决 Host 故障的备选思路:Private FD、Secretmem 与极端策略 为了根治上述 Host 端非法访问导致的整机崩溃问题,pKVM 团队评估了几种潜在的架构方案。其中最具前景的方案是扩展最初源自 Intel TDX 团队提出的“私有文件描述符”(Private FD,即 `guest_memfd` 的雏形)。Private FD 具备很多非常契合 pKVM 需求的技术特性:它在设计上就能天然阻止内存页被换出(Swap)和页面迁移(Page migration),只要实现得当,还能直接从源头上阻止内核通过旁路接口(Side things)或常规线性映射随意访问客户机内存。同时,该机制未来还能提供一套适合实现 Hypervisor 辅助页面迁移(Hypervisor-assisted page migration)的 API。但 Quentin Perret 强调,pKVM 对此有一个核心硬性需求,即必须支持所谓的“就地转换”(In-Place conversions),这意味着必须允许非破坏性的内存共享(Share)、解除共享(Unshare)以及捐献(Donation)操作。 团队考察的第二种途径是 Linux 内核的 `secretmem`。由于 pKVM 并不介意让受保护客户机的内存在 VMM 的地址空间中保持映射状态——只要能确保只有 VMM 能访问这些页面即可(因为如果是 VMM 违规访问,pKVM 可以非常干净利落地让 VMM 崩溃挂掉而不会波及整个系统)——真正棘手的问题仅仅发生在内核去访问这些页面的时候。因此,将 `secretmem` 与 Private FD 提案中的部分特性相结合,或许也是一个值得尝试的方向。 另外还存在第三种看似可行但其实很糟糕的极端方案:一旦发生非法访问,直接由 Hypervisor 处死客户机(Kill the guest)。 - **具体逻辑**:当 Hypervisor 探测到 Host 试图非法访问受保护客户机内存时,立即杀死该客户机,将其所有内存打上破坏性毒化标记(Poison),然后归还给 Host;允许 Host 去读写这些毒化数据并继续正常执行,从而避免主机崩溃。 - **优点**:可以保留长期的 GUP(Get User Pages)机制,无需对 Linux 核心内存管理(Core MM)做大幅度的底层侵入修改。 - **缺陷**: - 会在 EL2 层引入大量不必要的逻辑复杂度; - 客户机自身没有任何过错,却要替 Host 进程的违规操作“背黑锅”被直接杀掉,架构设计上很不优雅; - 这种销毁是完全异步的。KVM 宿主机可能根本不知道客户机已死,直到下一次尝试调用 `VCPU_RUN` Hypercall 时才愕然发现客户机已经不复存在,不仅无法及时搞清客户机因何而死,也给系统调试带来了极大的困难。 --- ### [35:14](https://www.youtube.com/watch?v=9npebeVFbFw&t=2114s) 客户机拆除流程与非可抢占 EL2 的异步内存回收 讲完了客户机的创建与运行,生命周期的最后一环便是客户机的销毁与资源拆除(Teardown)。Host 会通过发出一个特定的 Hypercall,明确告知 Hypervisor 自己不再需要该客户机。Hypervisor 在执行销毁前,首先必须进行一系列状态校验,例如确保在拆除期间该虚拟机的任何一个 vCPU 都不处于运行加载状态。之后,Hypervisor 必须将所有此前捐献(Donate)给客户机的物理页全部回收并归还给 Host,以便重新释放进内核的通用内存管理系统。 在归还这些页面之前,必须对所有页面进行毒化/擦除(Poison/Clear),否则极易导致客户机遗留在内存中的私密数据(Secrets)泄露给 Host。然而这里存在一个严重的架构限制:**EL2 运行在完全不可抢占模式下(Non-preemptible)**。 Quentin Perret 解释道:“提醒一下,EL2 是不可抢占的,所以我们绝不可能一口气把客户机的所有内存全部毒化完毕,然后让整个系统卡死在一个长达数分钟的不可抢占调用中,这是绝对行不通的。”因此,团队设计了一套更为精巧的分步拆解流程: ```mermaid flowchart LR accTitle: Guest Teardown and Memory Reclamation Flow accDescr: Step-by-step memory reclamation to prevent long un-preemptible execution at EL2 subgraph Step1["阶段 1: 发起拆除"] A["Host 发送 Teardown Hypercall"] --> B["Guest 逻辑上立即消失"] B --> C["物理页标记为:\nHost Page Pending Reclaim"] end subgraph Step2["阶段 2: 渐进式回收循环"] C --> D["Host 循环发起单页回收 Hypercall"] D --> E["Hypervisor 使用 Fixed-map 映射页面"] E --> F["物理页数据清零 (Poison/Clear)"] F --> G["页面归还 Host 内存管理"] G -.->|"在多次 Hypercall 之间\nHost 允许正常调度 (Reschedule)"| D end ``` 以 Android 系统作为 Host 为例: 1. **概念销毁**:Host 发起拆除请求后,客户机在逻辑上立即消失,但其占用的物理页面此时并未直接擦除,而是全部被打上一种名为“等待 Host 页面回收”(Host page pending reclaim)的中间状态。此时物理内存中的数据尚未做任何变动。 2. **单页回收循环**:Host 开始以逐页循环的方式连续发起回收 Hypercall。Hypervisor 每次接收到调用后,在 EL2 内部利用固定映射(Fixed map)将该物理页映射进来,完成清零擦除,随后安全地归还给 Host。 3. **避免系统卡死**:采用这种逐页归还的设计,Host 操作系统可以在多次 Hypercall 的调用间隙自由地进行任务重新调度(Reschedule),彻底消除了长时间不可抢占带来的系统假死隐患。 --- ### [37:07](https://www.youtube.com/watch?v=9npebeVFbFw&t=2227s) 当前局限性、未竟工作与问答:SMMU 支持及虚拟化开销 在结束主体演讲前,Quentin Perret 简要提到了由于时间关系未能深入探讨的其他复杂工程领域,包括与 Arm 架构中 Secure World(安全世界)的交互通信、基于 IOMMU 的 DMA 保护机制、物理中断、虚拟定时器、电源管理以及真随机数生成器(TRNG)集成等。 同时,他直言不讳地指出了当前 Patch 系列存在的局限性,部分通用功能甚至在非受保护客户机(Non-protected guests)上都暂不可用: - **暂不支持的功能**:脏页记录(Dirty logging,影响虚拟机动态迁移)、只读内存槽(Read-only memslots)、大页支持(Huge pages)、内核快速重启(kexec)以及设备直通分配(Device assignment)。 - Quentin 明确表示,大页支持已经在紧锣密鼓的开发计划中,而对于脏页跟踪和只读内存槽这类特性,“在正式向上游合并之前,我们肯定会让它们在非受保护客户机上完全可用。” 在问答环节,现场与线上观众提出了若干关键问题: - **关于 pKVM 对 SMMU(System MMU)的支持与页所有权管理**: 线上观众提问 pKVM 未来如何支持 SMMU 以及如何维护其页面归属。Quentin 透露,Linaro 团队目前正在主导推进该部分工作,预计很快就会推向邮件列表。在逻辑上,SMMU 的管理与 Host 类似:当 Hypervisor 需要更新 Host 的页表时,也必须去遍历并同步修改 SMMU 的 Stage-2 页表映射。 - **Host 运行在 Stage-2 转换之下的性能开销**: 现场有人问及 Host 自身被置于 Stage-2 转换运行是否会带来显著的性能衰退。Quentin 坦言,理论上的主要开销来自 TLB 压力以及 TLB Miss 的惩罚(因为多了一层转换机制导致硬件需要遍历更多级页表)。“但是在这方面有一个非常有趣的细节:Host 拥有绝大部分的内存,而且所有的 Host 映射都是恒等映射(Identity mapped),这意味着我们完全可以直接使用极大块的映射(Really large block mappings)来覆盖整个地址空间。”在系统引导期间,pKVM 可以直接使用 1GB 级别的大块巨页来覆盖几乎全部 Host 物理内存,这使得 Stage-2 转换所带来的额外虚拟化性能损耗在实际测量中几乎无法被感知。 - **拆除流程与异步回收的讨论**: 有参会者指出单页回收毒化开销明显,并提问为何不采用异步清理,同时提到 IBM s390 架构上正在做类似的异步优化。Quentin 回应称,目前 pKVM 的核心靶向场景是移动端设备(如 Android 手机),虚拟机的内存规格通常只有 1GB 左右,而非动辄几个 Terabyte 内存的巨型服务器,因此目前的拆除开销在移动端并不构成系统瓶颈;但他完全认同,如果要将 pKVM 扩展到更大型的服务器场景中,像 s390 那样通过 Fork 一个独立工作进程在后台异步清洗内存以隐藏延迟,绝对是非常明智的做法。 ### [41:37](https://www.youtube.com/watch?v=9npebeVFbFw&t=2497s) 原位转换(In-Place Conversion)的需求与设计设想 问答环节开始时,现场观众首先求证了一个具体的行为细节:如果用户尝试从 QEMU 控制台转储(dump)受保护 Guest 的内存,QEMU 是否会直接崩溃(die)?Quentin Perret 明确给出了肯定的回答。紧接着,现场讨论转向了 Guest 私有内存(Guest Private Memory)的原位转换机制,观众希望 Quentin 能详细展开解释为何 pKVM 必须支持内存的原位转换。 Quentin 解释道,pKVM 的核心驱动力之一是取代——或者说以更优的方式实现——Android 手机上已经运行了 15 年的 TrustZone 架构机制。长期以来,将内存页在 Host 与安全隔离区(Secure Island)之间流转已经积累了沉重的历史包袱。在手机端,某些受保护的有效载荷(Payload)需要消耗极其庞大的内存空间,最典型的例子就是 4K 视频播放等数字版权管理(DRM)场景。处理 4K 视频帧不可能只分配几兆字节,往往需要在 Host 内核与安全隔离区之间传递数十乃至数百兆字节(hundreds of megabytes)的内存数据。Quentin 强调:“If we want to have some of these TrustZone workloads moved towards VM, we need to find an efficient way to have that zero copy sharing, like memory transfer happening between these two things, and this is why the in-place conversion stuff is necessary.” 随后,一位参与 KVM 社区内存重构的开发者分享了一个构想,表示自己有 95% 的把握解决原位转换与取消映射 Guest 私有内存的问题。该方案基于最新的 Guest 私有内存提案(基于 `memfd` 的包装垫片机制),具体通过扩展 KVM API 来增加第三种内存状态: ```mermaid flowchart TD accTitle: KVM 扩展私有内存原位转换状态机 accDescr: 展示用户空间内存如何从共享映射安全原位转换为 Guest 私有内存而不触发 memslot 更新 subgraph Host["Host 阶段"] A["分配 memfd 内存"] --> B["标记为可映射状态 (User-space Mappable)"] B --> C["Host 用户空间填充数据 (填充 4K 视频帧等)"] end subgraph Conversion["pKVM 转换与映射"] C --> D{"底层 Hypervisor 是否支持原位转换?"} D -- "pKVM: 允许" --> E["内存清零/安全处理并完成原位转换"] D -- "TDX / SEV-SNP: 不支持" --> F["走标准私有内存路径"] E --> G["KVM 内部完成 Stage-2 映射 (避免 memslot 更新与 SRCU 开销)"] G --> H["安全安全交付给 Guest"] end ``` - **免除 memslot 更新开销**:通过在 KVM 内部处理映射与取消映射,避免更新 `memslot`,从而彻底绕开 SRCU(Sleepable RCU)带来的同步停顿。 - **差异化 Hypervisor 处理**:Intel TDX 或 AMD SEV-SNP 等技术无法支持此类自由的原位内存转换,但 pKVM 允许原位转换;Host 用户空间填充完数据后发出指令转换,并在必要时将页面清零后直接接入 Guest。Quentin 对此回应表示非常振奋,虽然看过社区相关的补丁,但一直没抽出空深入研究。 --- ### [45:03](https://www.youtube.com/watch?v=9npebeVFbFw&t=2703s) Stage-2 页表所有权 ID 与缺页处理 随后,一位听众提出了一个关于底层页表实现的深入问题:既然所有者 ID(Owner ID)仅保存在无效页表项(Invalid PTE)中,那么在缺页处理程序(Fault Handler)中,是否存在需要读取有效页表项(Valid PTE)所有者的情况?或者反过来,当某页已被 Host 映射,Guest 去触碰时会发生什么?为什么 Guest 会命中无效 PTE 而不能直接访问有效 PTE? Quentin 澄清了这套所有权追踪机制的边界。他解释道,在无效 PTE 中编码 Owner ID 的技巧只应用于 Host 的 Stage-2 页表。当 Guest 发生 Stage-2 缺页(命中 Invalid PTE)时,Hypervisor 绝不会随意将任意物理内存页映射给 Guest。相反,必须由 Host 明确向 Hypervisor 发起请求,指明“哪一个具体的物理页可以映射到 Guest 的指定 IPA(Intermediate Physical Address,中间物理地址)空间”。接收到指示后,pKVM 会执行严格的安全校验,核实 Host 是否确实拥有将该物理页捐赠或共享给 Guest 的合法所有权。 - **Host Stage-2 页表编码**:所有权元数据主要存在于 Host 的 Stage-2 页表中,用于防止 Host 违规访问属于 Guest 或 Hypervisor 的页面。 - **单对单共享限制**:现场参会者随之补充点明了关键逻辑——pKVM 架构中的内存共享严格遵循双向单对单实体模型(Sharing is only between two entities),不存在复杂的跨域多路混用,因此在页表项有效映射时无需额外重叠记录多重所有权。 --- ### [47:03](https://www.youtube.com/watch?v=9npebeVFbFw&t=2823s) Guest 引导安全、度量与信任根 讨论进入了 Guest 的远程证明(Attestation)与安全引导架构。听众提问:Host 可以通过 Android 既有的 Bootloader 链条建立信任,但 Hypervisor 如何确信 Guest 的 Bootloader 与固件(Firmware)是正确受信任的?整套系统的最终信任根(End Root of Trust)又是什么? Quentin 阐明了 Guest 固件的分发与隔离机制:Host 操作系统对 Guest 的 Bootloader 处于完全不可见的状态。受信任的 Guest Bootloader 预先经过数字签名,并在系统启动极早期,由设备级 Bootloader(Device Bootloader)放置在一段物理内存保留区(Memory Carve-out)内。Hypervisor 在启动初始化时,会立即将该保留区从 Host 的 Stage-2 映射中剔除。在启动 Guest 时,Hypervisor 亲自负责将这段 Bootloader 拷贝至 Guest 的内存中,并强制 Guest 必须从该 Bootloader 开始执行: ```mermaid flowchart LR accTitle: pKVM 受保护 Guest 启动与度量流 accDescr: 设备引导加载程序将签名固件放入保留区,由 Hypervisor 剔除 Host 映射并注入 Guest,随后进行度量 A["设备 Bootloader (写入保留物理内存)"] --> B["Hypervisor 启动初期从 Host 取消映射"] B --> C["Hypervisor 将受信任 Bootloader 拷贝入 Guest"] C --> D["强制 Guest 优先运行受信任 Bootloader"] D --> E["Bootloader 通过 virtio 接收 Guest 镜像并度量"] E --> F["派生 Guest 签名身份与机密证明"] ``` Quentin 补充说明,目前原型实现中的 Guest Bootloader 是基于 U-Boot 改写的,但团队计划未来采用更强安全保障的语言(如 Rust)进行重写。该 Bootloader 在 Guest 内部运行后,通常通过 VirtIO 等通信机制获取 Guest Payload 并完成代码度量(Measurement)。更关键的是,Bootloader 能访问 Host 无法触及的专用硬件派生机密,进而为 Guest 派生出专属签名凭证与可移植身份(Portable Identity),使 Guest 能够向远端证明自身的真实有效性。 关于设备被 Root 后 DRM 与安全机制是否会被绕过的问题,Quentin 坦诚自己不是硬件安全的最深专家,但他指出:“If you root the device, the keys that the bootloader can use to sign whatever payload is done are removed — so we just wipe the keys when you unlock the device.” - **解密密钥硬件擦除**:一旦设备解锁或被 Root,用于派生安全凭证与签名载荷的硬件根密钥会被物理擦除。 - **防冒用防御**:pKVM 绝不会将这套特权引导程序加载给非受保护的普通 Guest,即使攻击者在非受保护 Guest 中加载正确的载荷,由于设备在解锁状态下密钥已经被彻底销毁,攻击者依然无法伪造出合法身份或窃取受保护的 DRM 密钥。演讲在全场的掌声中正式结束。 ## Notable quotes > [00:00](https://www.youtube.com/watch?v=9npebeVFbFw&t=0s) pKVM is an extension of KVM for Arm64 that provides confidential computing features even on CPUs that don't necessarily have additional fancy hardware support for it. — Quentin Perret > [35:14](https://www.youtube.com/watch?v=9npebeVFbFw&t=2114s) EL2 是不可抢占的,所以我们绝不可能一口气把客户机的所有内存全部毒化完毕,然后让整个系统卡死在一个长达数分钟的不可抢占调用中,这是绝对行不通的。 — Quentin Perret > [23:23](https://www.youtube.com/watch?v=9npebeVFbFw&t=1403s) 我们获取长期 GUP pin 的原因是为了防止页面交换(swap)和页面迁移(page migration),因为我们即将把该物理页捐献给客户机并彻底失去对其的访问权限,所以我们绝不希望 KSM 或者其他机制去碰它。 — Quentin Perret > [41:37](https://www.youtube.com/watch?v=9npebeVFbFw&t=2497s) If we want to have some of these TrustZone workloads moved towards VM, we need to find an efficient way to have that zero copy sharing, like memory transfer happening between these two things, and this is why the in-place conversion stuff is necessary. — Quentin Perret > [47:03](https://www.youtube.com/watch?v=9npebeVFbFw&t=2823s) If you root the device, the keys that the bootloader can use to sign whatever payload is done are removed — so we just wipe the keys when you unlock the device. — Quentin Perret ## Mentioned Quentin Perret, Google, Android, pKVM, KVM, Arm64, Intel TDX, AMD SEV, Arm CC-A, TrustZone, QEMU, Linaro, U-Boot, Linux ## Video description Protected KVM on Arm64: A Technical Deep Dive - Quentin Perret, Google Protected KVM (a.k.a. pKVM) is an extension of KVM/arm64 providing a Confidential Computing solution for Arm v8.0+ CPUs. pKVM targets SoCs that don't feature Confidential Computing hardware extensions, which makes it applicable to a wide spectrum of domain-spaces, including mobile (Android). pKVM extends the existing KVM/arm64 nVHE hypervisor with the ability to manage the CPU's stage-2 MMU, hence allowing the enforcement of access-control restrictions on host accesses to guest memory. In this talk, we will do a technical deep dive on pKVM, describe its architecture and implementation [1], and discuss opportunities for sharing core infrastructure (e.g. memory management) with other Confidential Computing solutions such as Intel TDX, AMD SEV or Arm CC-A. [1] https://lore.kernel.org/kvmarm/20220519134204.5379-1-will@kernel.org/ [Source](https://www.youtube.com/watch?v=9npebeVFbFw)

Source quotes

[0:00] Quentin Perret: pKVM is an extension of KVM for Arm64 that provides confidential computing features even on CPUs that don't necessarily have additional fancy hardware support for it. [35:14] Quentin Perret: EL2 是不可抢占的,所以我们绝不可能一口气把客户机的所有内存全部毒化完毕,然后让整个系统卡死在一个长达数分钟的不可抢占调用中,这是绝对行不通的。 [23:23] Quentin Perret: 我们获取长期 GUP pin 的原因是为了防止页面交换(swap)和页面迁移(page migration),因为我们即将把该物理页捐献给客户机并彻底失去对其的访问权限,所以我们绝不希望 KSM 或者其他机制去碰它。 [41:37] Quentin Perret: If we want to have some of these TrustZone workloads moved towards VM, we need to find an efficient way to have that zero copy sharing, like memory transfer happening between these two things, and this is why the in-place conversion stuff is necessary. [47:03] Quentin Perret: If you root the device, the keys that the bootloader can use to sign whatever payload is done are removed — so we just wipe the keys when you unlock the device.

Source quotes

[38:33] Quentin Perret: The host owns the vast majority of memory, and all the host mappings are identity mapped, which means we're in a position where we can very easily use really, really large block mappings to cover everything... which makes the overhead of the stage-2 practically impossible to actually measure. [23:23] Quentin Perret: The reason we take a long-term pin is to prevent swap and page migration because we are about to donate that page to the guest and lose access to it so we don't want KSM or whatever to mess with it.

Original transcript

[0:00] okay [0:02] hello everyone [0:04] my name is konta I'm welcome for Google [0:06] I'm part of the Android virtualization [0:09] team [0:10] and today today I want to talk about the [0:13] work we've done on protected KVM on [0:15] non-64. [0:17] so [0:19] pkvm as we as we call it is uh [0:22] you know has been mentioned I think [0:23] several times during the conference so [0:25] hopefully by now most of you should have [0:28] at least the rough ID of what pkvm is [0:29] about [0:30] but if you don't the idea of pkvm is [0:33] it's basically an extension of KVM for [0:36] arm64. that provides confidential [0:39] Computing features [0:41] even on CPUs that don't necessarily have [0:45] additional fancy Hardware support for it [0:48] so pkvm is is to some degree comparable [0:51] with with TDX or SUV for instance in [0:54] terms of what it tries to achieve it's [0:56] all about protecting guest memory from [0:58] the host but it's quite different from [1:01] from from TDX or icv in how it achieves [1:04] that and it's precisely the how we made [1:06] that happen that I want to talk about [1:07] today [1:08] so why talk about pkvm so first of all I [1:11] think it's I think it's pretty cool [1:13] it is a slice slightly biased opinion [1:16] obviously so I'll let you decide but [1:18] it's also the first birthday of the code [1:20] so it felt like a good time to look back [1:22] and and see all the things we've done [1:24] over the last year [1:25] I'm also hoping that this talk will be [1:27] inspiring to other architectures and in [1:29] fact [1:30] you know I have seen that some of the [1:32] concepts that we have uh you know came [1:35] up with for pkvm I've already been [1:37] inspiring for other architectures so I'm [1:39] hoping that this will help even more [1:41] and and finally we have a relatively [1:44] large patch series out there which [1:46] implements uh most of the things I'm [1:48] going to to discuss and so I'm hoping [1:50] that you know this this talk may bring [1:53] some color and maybe help a little bit [1:55] the review of this battery which is [1:56] which is quite big and they are still [1:58] open this discussion points that I would [2:00] like to to discuss as well [2:03] so quick disclaimer as opposed to the [2:06] previous talk this very much intends to [2:08] be a technical talk so here's the cake [2:10] if you were waiting for it [2:13] um what this means is I'm not going to [2:15] be talking too much about why we're [2:17] doing any of this why Android is [2:19] interested in confidential Computing or [2:20] things like that but I'll be talking [2:22] about what we actually do and how we [2:24] make that happen if you are interested [2:25] in those things though please feel free [2:27] to reach out at the end of the talk and [2:29] there are also resources that I've [2:31] listed here on the slides the slides [2:34] themselves have quite a bit I'll try and [2:35] cover as much material as I can [2:37] uh but yeah hopefully they can be useful [2:41] as a future reference as well so pkbm [2:45] the first thing I've said is pkvm is an [2:48] extension of KVM 464 so before I can [2:51] explain how we extend it I think it [2:53] makes sense to start by talking about [2:55] how pkvm works in the first place [2:57] in this picture I'm [3:00] showing the KVM setup for the so-called [3:04] nvhe execution mode of KVM we have two [3:07] different modes of execution for on arm [3:09] 64. I will be focusing on the nvac part [3:12] today just because it's the one that is [3:13] relevant to pkvm [3:16] so as you can see the arm architecture [3:18] defines multiple exception levels so [3:19] except when people zero which is [3:21] typically why user space runs el1 is for [3:24] the kernel layer er2 is the hypervisor [3:27] layer there is also neo3 it is defined [3:29] in the architecture for the thermal [3:30] layer but I'm not going to talk about it [3:33] so on the left side we have the host [3:34] user space and right side the gesture [3:36] space the one interesting thing to [3:38] notice with this [3:40] setup here is that the host Linux kernel [3:42] is running at the L1 and not that [3:44] hypervisor layer which means that KVM [3:46] itself does not have directly access to [3:49] virtualization features the way we [3:52] enable the actualization of guests is by [3:54] having a separate piece of code running [3:56] a tl2 which we call the KVM wall switch [3:59] code which as the name suggests is about [4:03] you know is is meant to context switch [4:05] between host context and guest context [4:09] the kvm1 switch code is [4:12] part of Linux technically it is in a [4:14] Linux source code it is built into Linux [4:17] linked into the the the the image would [4:20] Place into a different section of the L [4:22] file and it is essentially Left Behind [4:24] during Boot and it is just responsible [4:28] essentially for context between contact [4:30] switching to guess when the host [4:32] requested [4:33] another thing to notice here is that [4:36] when when you're running on guest [4:37] context [4:38] uh we have the stage two translation [4:40] which is enabled the stage two mmu is in [4:43] in the arm world how we do the the get [4:45] physical address to host vehicle address [4:47] translation in the hardware [4:50] and interestingly the the host doesn't [4:52] have a stage 2 mmu on when it's running [4:54] which means the host has access by [4:56] default in normal KVM non-pkvm the host [4:58] has access to all of memory which [5:00] includes guest memory as well as [5:01] hypervisor memory [5:04] with pkvm we're basically using this [5:08] somewhat uh unusual or the setup to our [5:11] advantage and we're basically extending [5:14] the el2 component that sits at the [5:16] bottom of the picture here and we're [5:18] making this hypervisor code a little bit [5:19] smarter and we're giving it the ability [5:21] to enable the stage 2 translation even [5:25] when the host is running which puts the [5:27] hypervisor in a position to enforce [5:29] Access Control restriction to Pages [5:31] arbitrary pages in the system it can [5:34] thanks to this prevent the host kernel [5:36] from accessing guest memory or private [5:38] accessing hypervisor memory [5:41] the reason why we've designed pkvm as an [5:44] extension of K of the nvhe mode like [5:48] this is because this means we can have [5:50] the hypervisor and the kernel in the [5:51] same image which has a few different [5:53] benefits first it's great for [5:55] updateability [5:56] in the Android World we've put a lot of [5:59] efforts into kernel updateability [6:01] deploying updated kernels to devices on [6:03] the field we don't want to just reinvent [6:04] the wheel for the hypervisor so it can [6:06] just have the hypervisor package with [6:07] the kernel we get that for free [6:10] uh sends the hypervisor and the kernel [6:12] are part of the same image and they are [6:14] by construction updated atomically which [6:18] means that but we don't have to keep a [6:20] stable API between the host kernel and [6:22] the hypervisor that gives us a great [6:24] deal of flexibility to invent whatever [6:26] you know mechanism we can we can come up [6:29] with without having to fear for [6:30] background compatibility [6:32] and finally the type of the hypervisor [6:34] is open source it's quite a nice thing [6:37] to know that the code that's running at [6:38] high privilege level on your system is [6:40] something that you can look at fix audit [6:43] improve we can come up with any sort of [6:45] feature security feature ordering on top [6:47] of it and the community can can you know [6:48] will benefit from all of that [6:51] so I mean this is all great how did it [6:54] how you know how does it actually work [6:56] in order to do that I would like to [6:58] introduce you to your uh best friend for [7:02] the next 25 minutes which is this little [7:04] character in the bottom left corner this [7:07] will represent our user and what I would [7:09] like to suggest is that we follow that [7:11] user as they interact with their device [7:13] and we'll make a few stops along the way [7:17] and look at how pkvm gets involved in [7:19] direct in the interaction of the user or [7:21] the device and a few different places [7:22] will take a small tensions to talk about [7:24] interesting things that hypervisor does [7:26] as well [7:28] the first thing that I will [7:30] you your user will do is to simply just [7:32] power the device on and put the device [7:36] the [7:38] pkvm story I would say starts pretty [7:42] early when we boot the device [7:44] we have the expectation that the [7:46] bootloader will be responsible for [7:48] checking that the boot image that is [7:50] loaded in the device is effectively the [7:52] one that vendor intended to load using [7:55] signature checking all the industry [7:57] style things [7:58] the kernel must be entered by the book [8:01] of the rtr2 that's a requirement for KVM [8:04] on arm in general not just pkvm [8:06] and one of the very very first things [8:08] that a kernel will do when it is [8:09] enter.tl2 what I mean one of the first [8:11] thing is like in the first maybe 20 [8:13] instructions or something [8:15] it will it will detect that it is [8:17] running at tl2 install what we call the [8:19] sub vectors the exception vectors and [8:21] cl2 which are dummy exception vectors [8:23] basically which do nothing [8:25] and then e-ret to L1 and proceed to boot [8:28] from there [8:30] a little bit later uh [8:32] we'll we'll hit the point where we set [8:34] up the memory management subsystem in [8:36] Linux and the hypervisor is going to [8:39] need some memory for itself in order to [8:41] manage a few different things including [8:43] its page table its own set of stage one [8:45] page table as well as the page table of [8:48] the host it's going to require memory [8:49] for that [8:50] and so the current implementation that [8:52] we have uh sort of [8:55] uses the Min block API fairly early on [8:58] to allocate a pull-out memory that will [8:59] be donated to hypervisor as well and the [9:02] the amount of memory that we have to [9:04] reserve is a function of uh you know how [9:06] much memory there is on your system [9:07] because the page tables you know the [9:09] size of the page table is depend on that [9:12] later on when pkvm initializes and [9:16] um I should mention on on arm pkvm sorry [9:19] KVM is not modular so we cannot load it [9:21] as a module it has to be built into the [9:23] kernel so this happens before we choose [9:25] our space [9:26] when we [9:28] reach the point where KVM tries to [9:31] initialize in the context of pkvm we [9:33] have to do an additional a few [9:35] additional steps so we have we expect [9:37] the host to allocate a temporary stage [9:40] one page table called hypervisor which [9:42] will come handy when we start when we [9:45] want to initialize the hypervisor [9:46] because we can use that page table to [9:48] turn the mmu on immediately which is [9:49] which is really really useful to [9:51] bootstrap the hypervisor [9:53] and host allocates a few more things [9:54] such as this tax law for er2 and post a [9:58] few pages and then it will issue a hyper [10:00] call to el2 [10:02] which will [10:04] replace the dummy step vectors that we [10:08] have installed replace them with the [10:09] pkvm step back turns [10:11] and then the hypervisor can start sort [10:14] of bootstrapping itself first it will [10:17] recreate its own stage one page table [10:19] using the pages that have been reserved [10:21] in the memory pool I mentioned earlier [10:23] and it will also initialize the stage 2 [10:25] page table for the host [10:27] and unmap itself from the hostage tool [10:29] so it will unmap we will unmap the text [10:31] section and hypervisor data section as [10:33] well as the memory prototype that I [10:35] mentioned earlier [10:37] and from that point the hypervisor will [10:39] return to the host which will proceed to [10:41] boot as if nothing happens except at [10:43] that point now the stage 2 page table of [10:45] the host is enabled and it in fact the [10:48] host is no longer able to access certain [10:49] pages and systems specifically the [10:51] hypervisor pages which makes our little [10:53] friend here really happy [10:56] before we get back to our little user [10:58] and see what what he's going to do next [11:00] I would like to take the opportunity I [11:03] would like to focus on something that [11:05] I've mentioned here [11:06] I've explained that the hypervisor will [11:09] map itself using with stage one page [11:11] table and unmap itself from the hostage [11:13] too conceptually this means that the [11:15] hypervisor is effectively taking [11:17] ownership of those memory pages [11:19] in the in the context of pkvm it's the [11:22] hypervisor's responsibility to track [11:24] which pages are owned by which entity in [11:27] the system at any point in time and [11:28] enforce the access control restrictions [11:30] of white wire [11:31] and we have [11:33] to some extent formalize this in in the [11:35] context of pkbm so I just want to spend [11:37] a few minutes to talk about memory on [11:39] our shape tracking [11:40] uh [11:41] pkvm says [11:44] save all possible types of owners for [11:47] memory [11:48] so the host may be one owning memory [11:50] hypervisor maybe owning memory guest VMS [11:53] when they are protected maybe owning [11:54] memory they are potentially other [11:56] entities not going to describe them too [11:58] much but you can think about trust Zone [12:00] and things like this [12:01] and from the point of view of each of [12:03] these owners the pages in the system can [12:06] be can be in either of in one or four [12:08] states either on the page is completely [12:10] owned by that entity which implies that [12:12] whoever is the owner has exclusively [12:15] exclusive access to the page or the page [12:17] can be shown and you can either be the [12:20] the the provider of the of the share all [12:23] the recipients uh or the borrower in [12:25] this case and finally we have the state [12:28] to imply that the page is no longer [12:29] accessible which is what happens for [12:31] instance when we unmap something from [12:32] The Host [12:34] um [12:36] the the way we track ownership is uh [12:41] you know is implemented at the page [12:42] table level itself the hypervisor [12:44] maintains the page tables for I mean its [12:46] own page table as well as the guest page [12:48] tables and the host page tables to see [12:50] which pages are owned by who and shared [12:52] with who we use uh software bits in the [12:54] page table entries so we have four four [12:57] bits uh defined in the architecture the [12:59] 12 original software use we use them [13:01] exactly for that [13:03] we have [13:05] sort of restricted sharing to only be [13:07] sort of a peer-to-peer thing for now for [13:10] Simplicity so we don't allow any way [13:12] sharing well you will have three or more [13:13] entities sharing a page that's for [13:15] Simplicity reason uh and I mean anywhere [13:18] sharing has its own set of problems so [13:21] this we've simply started simple [13:24] an interesting thing to say as well is [13:26] that the host stage 2 page table is [13:28] identitymapped so we never use [13:30] translation we only use Access Control [13:32] restrictions for mapping memory into the [13:34] hostage too and that lets us do some [13:37] interesting things [13:38] when we there is an invalid page table [13:41] entry into the host page table and if so [13:44] repeat 0 is clear it means that bit 1 to [13:47] 63 will be completely ignored by the [13:49] hardware so we reuse them to store [13:51] metadata about the pages and [13:52] specifically what we do in this case is [13:54] we store the owner ID of whoever is the [13:57] owner of the page that corresponds to to [13:58] the to the mapping [14:00] we've defined the host identifier to be [14:02] zero such that basically if you have a [14:05] PT in the host stage 2 that is just [14:07] completely zeros to send codes ownership [14:09] to The Host [14:12] um [14:13] now this will Define the state we have [14:16] also sort of formalized the way we can [14:17] do the conversions or you know we can [14:20] transition from one state to another [14:22] donation of pages uh ultimate all of [14:26] those transitions happen in two steps we [14:28] first check that we have a certain [14:29] invariant that is correct into the page [14:31] table of the initiator of the transition [14:33] and the completed our transition and [14:35] then apply the the changes to the page [14:36] table [14:37] for donations we expect that the [14:40] initiator of the transition has [14:41] exclusive exclusive access to the page [14:43] that is being donated and obviously it [14:46] sort of implied that the computer [14:47] doesn't have access to it and then apply [14:49] this I'd like to emphasize here that we [14:52] don't accept a donation for a page that [14:55] is shared for instance we don't have [14:57] like transitive sharing or anything or [14:59] that thing before a page can be donated [15:01] to someone it has to be unsure first to [15:03] reclaim to to get back exclusive [15:06] ownership [15:07] uh sharing a page we have the same [15:10] initial state that obviously we will go [15:12] and map pages in both page tables with [15:15] the uh the appropriate mappings in the [15:17] page table into the software bits and [15:20] the engine obviously is the reverse [15:22] operation [15:25] um all right so now that you know [15:26] everything there is to know about memory [15:28] ownership tracking and pkb and we can we [15:30] can go one step further so our user has [15:31] been able to boot the device and now [15:33] it's at home it's great I have a pkvm [15:35] enabled device [15:36] I should try and create a guest and well [15:39] that's what that's what we'll be doing [15:41] when when we're creating a guess with [15:43] bkvm the host has a few extra steps to [15:46] do [15:47] first it needs to allocate uh more [15:50] memory and you will see that this is [15:52] actually a common team [15:53] uh every time we need to do something [15:55] with the hypervisor one of the first [15:57] things we have to do is allocate memory [15:58] we're going to donate to it we are [16:00] running the hypervisor with as little [16:02] memory as we possibly can and we donate [16:04] we donate memory dynamically to it and [16:06] reclaim it when that's possible as well [16:08] so the host will allocate memory and [16:11] then issue a hyper call to to tell the [16:13] hypervisor please I would like to create [16:15] a guest and hear all the pages you can [16:17] use in order to install the metadata for [16:19] that VM [16:20] the hypervisor in that case will convert [16:22] all of those allocated pages to you know [16:25] hypervisor private memory either with a [16:27] host to hypervisor donation as I've just [16:29] described and then it will allocate uh [16:32] what we call a shadow handle and then [16:34] initialize all of the ER to private data [16:37] structures which include a struct KVM [16:39] and a strike vcpu for HB CPU as well as [16:42] the root of the of the page table [16:44] and then return that handle back to the [16:46] host [16:47] the handle that the allocator the the [16:49] hypervisor was allocated is what the [16:51] host can then use in order to talk to [16:54] the hypervisor when it talks about that [16:55] VM so it's comparable to the to the vmfd [16:57] that userspace has when you create a VM [16:59] with an iot tool [17:02] uh maybe one one point to make so a lot [17:05] of the initial [17:07] past series we've posted use the the [17:10] notion of Shadow data structures uh I [17:12] think when process of renaming those [17:14] data structures to to not be called [17:15] Shadows anymore so I think this might [17:17] even be um uh sort of uh out of date out [17:20] of this morning because I think we'll [17:22] expose that Bachelor is [17:24] um [17:25] the the Shadow the shadow terminology is [17:28] consistent with the the series at the [17:30] time well I was uh I wrote the slide [17:34] so this is basically what it looks like [17:36] when host creates a guest first it will [17:39] allocate its own [17:40] KVM struct kbm structure CPU and all of [17:44] that stuff and one of the members [17:46] instruct KVM into the art code will be [17:48] the shadow handle [17:49] once the el1 has asked the hypervisor to [17:54] create your Spain the hypervisor will [17:56] have its own private copies well not [17:59] copies but those its own private [18:01] instances of uh you know similar data [18:03] structures with the what we call the kbm [18:06] shadow VM which includes extra KVM plus [18:08] a few other things and same thing for [18:10] the vcpus [18:11] uh one of the things that the hypervisor [18:14] has as well is a back points to the to [18:16] the host data assumptions [18:20] um as I've explained earlier we are [18:23] running the hypervisor with as little [18:24] memory as we possibly can and in fact [18:26] the whole hypervisor runs a tl2 in a [18:29] fairly constrained and limited [18:31] environment we can't just run kernel [18:33] code it's a separate exception level [18:35] and it el2 was and is a fairly [18:39] restricted place where you can run code [18:40] but over the years we've been I mean [18:42] over the last year essentially we've [18:45] been adding first infrastructure to the [18:47] hypervisor to try and make our life a [18:49] little bit easier and most of the the [18:51] things that we've added that we've added [18:53] are [18:54] Concepts borrowed from the kernel and [18:56] the idea is really to you know if your [18:59] kernel hacker you should somewhat feel [19:00] at home when you're working with the el2 [19:03] layout so we have spin locks we only [19:05] have new textures because the hypervisor [19:07] was completely done preemptible that's a [19:09] hard constraint [19:10] we have debug features which allow us to [19:12] to assert that certain logs are held in [19:14] certain places we have a page allocator [19:17] it's quite limited and how it can be [19:18] used just because we have very very [19:21] limited memory constraints [19:23] infrastructure we have a vmap as well [19:27] it's we have a struct height page which [19:29] is really really small but it's quite [19:30] convenientful for some things [19:32] and we're working on on interfacing with [19:35] the host uh tracing subsystem as well to [19:38] have the hypervisor emit Trace event [19:40] into the into the host trade with all of [19:42] those sort of things so this is [19:43] there's a lot of things we can already [19:45] already [19:47] so we have with the device created the [19:49] guest now it's time to run it [19:53] so KDM [19:55] has uh the notion of vcpu load and vcput [19:59] the ID of vcpu load in VC group BCP you [20:02] put is to make the vcpu essentially [20:05] resident on physical CPU as a way to [20:07] optimize the vcp Run Loop [20:09] uh pkvm uses a similar model we have [20:13] hyper calls for vcpu load and vcput but [20:16] there is a little bit of added semantic [20:18] on top of that [20:19] in the context of when when the host [20:22] tries to issue a vcpu load hyper call [20:24] the hypervisor will have to do a number [20:27] well a few security sort of Sanity [20:30] checks before it will allow that to [20:31] happen for instance it must make sure [20:33] that that vcpu is not currently loaded [20:36] somewhere else or that that BCP we don't [20:39] already have a vcpu loaded on the [20:40] physical CPU these type of things [20:43] and then subsequent hyper calls such as [20:46] vcpu run will [20:48] require that the vcpu has been loaded [20:50] before and this is this will be strong [20:52] heart requirement [20:55] and vcp you put when we you know release [20:58] the reference we have on the on the on [21:00] the vcpu we'll sync what we call sync [21:02] the vcpu shadow state with the with the [21:06] host for non-protected VM and we'll drop [21:08] the reference and then clear the sort of [21:10] el2 tracking we have the persu tracking [21:12] we have to do [21:14] tracked the loaded vcpus [21:17] just to talk about singing flash quickly [21:19] these are our names that were [21:21] traditionally used in KVM arm they may [21:24] not be used for I mean clear to everyone [21:26] so the idea of the sync is going from [21:29] the el2 state back to the el1 copy which [21:33] typically happens after we've run the [21:35] vcpu and we're going to complete part of [21:36] the state back into the host so it can [21:39] handle a fault for instance and sync and [21:42] flush it the other way around [21:45] um so [21:47] okay we've loaded the bcpu now it's time [21:49] to run it the vcpu Run hyper call in the [21:52] context of pkvm takes essentially no [21:55] parameters because vcpu run expects the [21:57] vcpu that we want to load is already [21:59] loaded on the physical CPU so whatever [22:01] is passed as a parameter will be will be [22:03] ignored [22:05] in this case the hypervisor will flush [22:07] part of the vcpu state which means it [22:10] might it will copy a very small it will [22:12] cherry pick a very small [22:14] part of the of the the vcpu state that's [22:17] coming from the host that it believes [22:18] can be trusted or is you know not not [22:21] going to close security problem and then [22:23] context switch [22:24] to the to the guest but using the el2 [22:27] copy of the vcpu and then erecting to [22:29] that cast [22:30] when we exit back then the host will [22:33] sync part of the state which means copy [22:35] some of the state from from the shadow [22:37] vcpu stripe back to the host [22:40] um when the guest exits there are some [22:43] things we can handle at el2 directly [22:46] for instance the FP state is Switched [22:50] between the host and guest transparently [22:53] uh so sorry lazily so this is kind of [22:55] kind of things we can do directly with [22:57] we have some some Logic for for the big [23:00] stuff that can be done as well [23:01] but it's more the exception than the [23:04] rule most of the exits have to be [23:06] handled by The Host [23:07] and and in fact because the hosts still [23:11] have its own KVM struct and vcpu struct [23:14] and we only populate the state that we [23:16] care about into that thing most of the [23:18] Hostile handling is effectively [23:19] unlellified which bkvm is just very [23:21] similar to normal KVM [23:23] one no table exception however is memory [23:25] evolved [23:26] so let's imagine that we are that we are [23:30] running a tl2 we have a guest that is [23:32] exiting and we just we managed to handle [23:34] that exit every time and real tell the [23:35] guests at some point we'll get a data [23:38] about or an instruction about coming [23:39] from the guest what the hypervisor will [23:41] do in this case is copy the ESR which is [23:44] the exception syndrome register [23:46] and the GPA if we copy that back into [23:48] the the host vcpu struct and then return [23:51] to vl1 saying please handle the fault [23:53] for me [23:55] um as with traditional KVM we will [23:59] expect you know we'll look up the mem [24:00] slot and convert the the GPA into an HBA [24:04] and in the current implementation that [24:06] we have instead of just doing a gap and [24:08] mapping things into the guests we will [24:11] have KVM take a long-term Gap pin on the [24:14] page that is returned that is found on [24:16] the hva page [24:18] uh the reason we take a long-term gift [24:20] then is to prevent uh Swap and Page [24:22] migration because we are about to donate [24:24] that page to the guest and lose access [24:26] to it so we don't want [24:27] KSM or whatever to mess with it I'll [24:30] talk about a bit more about that later [24:33] once that is done we need to have KVM [24:36] top up aprb CPU memcaf similar to what [24:38] we already do with the NMU cache and [24:41] then issue a pkvm guest map hyper call [24:44] at that point the hypervisor will try [24:47] and top up its own vcpu mem cache from [24:49] the memcache that the host has provided [24:51] and every time we will take a page out [24:54] of the host main cache and into our own [24:55] main cache into the Hypervenom cache we [24:57] have to go through a full host to [24:59] hypervisor donation to make sure we we [25:01] have the writing Bonds in place [25:03] once the hypervisor has enough memory [25:05] into its memcache to create guest [25:08] mappings it will go and modify the guest [25:12] stage 2 page table for protected guests [25:14] all of the the pages will be donated [25:16] completely to the guest so we'll also on [25:19] map from the host for a non-protected [25:21] guests so traditional KVM guest will [25:23] just do a share and keep the mapping [25:24] we'll retain the mapping in the whole [25:26] stage 2 as well [25:28] once that is done we can then return to [25:30] the host which which can proceed to run [25:33] the vcp around hyper call and rinse [25:34] repeat [25:38] um I've mentioned how we handle Memorial [25:41] boards that doesn't tell us how we do [25:42] mio exits there are a few problems uh [25:46] with that which requires of additional [25:48] work one of the things is that the [25:50] hypervisor has no understanding of mem [25:51] slots we just don't have that concept at [25:53] all at tl2 right now [25:55] and for I think fairly obvious [25:58] confidentiality reasons the guest [25:59] registers are not copied back to the to [26:01] the host kernel when we exit the vcpu [26:04] so it makes mmyu handling I mean a [26:07] little bit complicated [26:08] so we had to expose from the height of [26:12] advisor a set of hyper calls to guests [26:15] to allow them to declare their MMI [26:17] ranges basically so the guest can issue [26:21] the hyper call to say I'm willing the [26:23] host to handle mmio for me in this [26:25] region of my IPA space or GPA space and [26:28] then hypervisor when it says Exit in [26:31] those regions it will use the the Aussie [26:34] or x0 sorry as a transparent register [26:39] um in in a similar idea we have also [26:42] exposed to guests share hyper calls [26:44] which allows the guest instantiate to [26:47] share Pages like protected guests to [26:48] share Pages back to uh with the host to [26:52] allow Communications so typically about [26:54] IO Communications and things like that [26:56] uh it is a little bit interesting in [27:00] some cases because all of guest memory [27:01] is paged in lazily which means that the [27:04] guest may try to share a page that it [27:06] doesn't actually have [27:07] in that case we we need to have the [27:09] hypervisor essentially we have the [27:11] hypervisor um [27:14] uh [27:16] fudge the the exception General register [27:18] before it returns to the to the host to [27:20] pretend that this is in fact a data I [27:22] bought from the guest to trigger the [27:24] fold handling path into the into the [27:25] host get the host to donate the page and [27:27] then replay the ABC on the guest side on [27:30] the next vcpu run [27:34] few notes on how we load things into the [27:36] guest because it ties into how memory [27:39] gets mapped in in into there [27:41] the the expectations we currently have [27:44] is that the device bootloader which is [27:47] the Android Bootloader for us [27:49] has to copy a trusted guest bootloader [27:53] into a result memory region which will [27:55] be described into DT of the host [27:58] and that memory region will then be [28:00] unmapped by the hypervisor when it [28:02] initializes so when I said earlier that [28:04] the hypervisor initial unmaps itself [28:06] from the host it also on maps to get [28:09] and the the vmm can then specify when it [28:13] creates a guest I want the bootloader to [28:16] be loaded at this particular region of [28:18] the guest IPA IBA space [28:20] and the first time the vcpu is run we [28:23] run the guest we will force the program [28:25] counter of the guest to be into those [28:27] regions to fold 10 pages into into that [28:30] range and as the hypervisor will see [28:32] those pages being donated by the host it [28:34] will copy the guestbook loader into the [28:36] guest which can then proceed to load the [28:39] guest payload in a test that whatever is [28:40] being loaded into the protected guest is [28:42] what it is expected [28:47] um so I've I've been talking about [28:49] guests a lot but the interesting thing [28:51] with pkvm is we have not only to handle [28:55] guest Falls but also host stage two [28:57] folds because this might happen [28:59] in in the implementation we have the [29:01] whole stage 2 mappings are created [29:03] lazily just like they are forecasts [29:05] essentially [29:06] and every time we take a photo a folder [29:08] tl2 because of a stage 2 stage 2 fault [29:11] we have to check the state of the Page [29:12] by walking the hostage two page tables [29:15] if we find that the pte that corresponds [29:19] to the to to the address was trying to [29:21] to review access [29:23] is invalid we will check bits 1 to 63 as [29:27] I mentioned earlier to see we who is the [29:30] owner of that page if the owner of that [29:32] page happens to be the host we can just [29:33] create a valid mapping and replay [29:36] if the pte is valid that we found it [29:38] probably means we'll just rate it with [29:39] someone else who has done that for us [29:41] just before so we can just re-enter the [29:43] host and hope for the best [29:44] but if with the pte is invalid and the [29:47] ID we find in the in the remaining bits [29:50] is not the one of the host then we have [29:53] a problem it means we've got the host [29:54] accessing private memory and this is [29:56] where the fun begins [29:58] yeah [30:01] um [30:02] the the handling that we have [30:04] implemented in the in the current in [30:06] pkvm is is basically that we have the [30:09] hypervisor just re-inject the exception [30:11] back into the host and have the host [30:12] handle it [30:14] um if the fold if the fault was taken [30:16] from el0 [30:18] it means that it was user space that was [30:21] that accessed Pages uh that have been [30:24] donated to a gears so that's typically [30:26] going to be the vmm that still has a [30:28] mapping on the page that has been that [30:30] has been donated it tries to access that [30:32] and obviously something has to be done [30:35] in that case the the hypervisor will [30:38] just re-inject the exception back into [30:39] the host [30:40] uh and and set an additional bit into [30:43] the exception Center and register to [30:45] make sure that the host Handler can [30:47] distinguish that exception from just a [30:49] normal like el0 paging [30:52] full [30:54] when when we see that at el1 in the host [30:57] kernel we'll say oh the hypervisor was [30:59] telling me that that guest has been [31:01] accessing memory it shouldn't and I [31:03] really don't know what to do so I'll [31:05] just take video user space process that [31:06] did that [31:08] if default was taking from year one [31:10] however it's a little bit more [31:12] complicated [31:13] in this case we have the hypervisor sort [31:16] of repaint the exception syndrome [31:18] register to pretend that the fault we've [31:20] taken is a is a same level fault and [31:22] then re-enter the kernel is saying there [31:25] you go you you've created you've covered [31:27] yourself to enter the exception Handler [31:30] do deal with it [31:32] there are cases where the kernel can [31:34] handle same level faults but not that [31:36] many [31:37] so for instance if [31:39] uh let's imagine that we have a [31:41] compromise user space a compromised vmm [31:44] or malicious vmm for instance that tries [31:46] to do a Cisco and it passes as [31:49] parameters that's called memory that [31:51] belongs to a protected guest [31:52] in that case the kernel will access that [31:55] memory in using copy to user copy from [31:58] user and things like this which means [32:00] that we will be in position to handle [32:01] the same level fault and actually just [32:03] fail the Cs call [32:04] but if you take the same example and [32:07] have a process as trace this value [32:09] should be a mem then the asteroids [32:12] process will try and inspect whatever [32:14] memory is being passed to the to the to [32:16] the to the system call and it will do [32:18] that using the I have an example here [32:21] using the process VM real V Cisco which [32:25] will do get user Pages remote if our [32:28] memory is correct and then access those [32:29] pages through the linear map in that [32:32] case the hypervisor will say okay you [32:33] can't access those pages Oriental the [32:35] kernel but we're not in a position to uh [32:37] to actually handle the fault it will [32:39] just have to bring the machine down [32:41] this is in my opinion the biggest [32:44] remaining problem with the host side [32:46] implementation of pkvm I think the title [32:49] advisor is now getting pretty solid but [32:50] there are still work that needs to be [32:51] done on that front before we can we can [32:53] bend this Upstream [32:56] one promising solution to that problem [32:58] would be to extend the the private fde [33:01] proposal that that came from the TDX uh [33:04] I think the TDX developers it has a lot [33:08] of good features for what we're trying [33:10] to do here it should prevent uh Swap and [33:12] Page migration and it should if it if [33:15] don't correctly prepared the kernel from [33:16] accessing just memory through sort of [33:18] side side things and also offers [33:21] potentially a suitable API in order to [33:23] implement hypervisor assisted page [33:25] migration data [33:27] uh however we really need to have [33:29] support for for what we call In-Place [33:31] conversions which means non-destructive [33:33] shares or on shares of donations [33:37] another thing we're currently looking at [33:39] is whether secret men would would be [33:41] actually a good option for this because [33:43] we don't necessarily mind keeping guest [33:46] memory mapped into the vmm as long as [33:49] only the bmm can access those pages [33:51] because we can say via like cleanly if [33:54] that happens the biggest problem is when [33:56] the kernel had accesses those pages so [33:58] maybe secret mem extended with some of [34:00] the features provided by the private FB [34:03] couples or would be a good option [34:05] another possibility but which is not [34:08] supromising would be to have the [34:10] hypervisor just kill the guest [34:13] um that is slightly ugly but it should [34:15] work [34:17] the idea would be that you know the [34:19] hypervisor when it sees that uh you know [34:21] the the host access make the guest [34:24] memory it kills the guest poison all of [34:26] it and returns it to the to the host and [34:28] just let the host write or read the [34:31] poison values and instantly just proceed [34:33] and not fail [34:35] um one of the pros is that it means we [34:37] can just keep our long-term Gap in thing [34:40] working and we don't have to do any [34:41] changes from the on the core mm side [34:43] essentially the problem is it's quite a [34:46] bit of complexity that is added at the [34:48] L2 I think the guest being blamed for [34:50] something it's not responsible for is [34:52] not necessarily the cleanest option [34:54] and it also means that potentially KVM [34:56] will have will be made aware that the [34:57] guest has been killed but completely [34:58] asynchronously it's only the next time [35:00] you'll try to do a vcp run hyper call [35:03] that you will see oh well this guest is [35:05] gone what what happened and it's way too [35:07] late to actually you know know what why [35:09] you've been killed makes things really [35:11] hard to debug [35:14] all right so coming back to our little [35:16] guy here [35:17] um [35:18] the last thing we've done we need to do [35:19] is shut down the guest we've created the [35:21] guest run it run the guest and now it's [35:23] your way down [35:25] the the teardown procedure is again just [35:28] the host issuing a hyper call to say I [35:31] no longer want to use that guest the [35:33] hypervisor has to do a few [35:35] things before it can let that happen one [35:37] of them is checked that there are no [35:38] load the CPU while all this is happening [35:41] and then we need to take all of the [35:43] pages that have been donated to the [35:44] guest and return them back to the host [35:46] so they can be free into the memory [35:47] management system [35:49] obviously we need to poison the pages as [35:51] we do that uh because otherwise we might [35:53] risk exposing uh guest secrets that are [35:55] that are in those pages [35:57] as a reminder er2 is non-framemptable so [36:00] we just cannot do poison all of gas [36:02] memory in one go and just like take [36:04] minutes long Empty Bottle it's just not [36:07] happening so we had to do that in a [36:09] slightly more complicated way [36:12] um so assuming that we have a system [36:14] like this where we have Android and and [36:15] which is our host I'm using Android as [36:18] an example we have the whole studio host [36:20] OS on the on the left hand side and a [36:22] guest and then our host tries to tear [36:25] down the the guest [36:27] what will happen is the gas will [36:28] notionally disappear but we will put the [36:31] the pages into a state which is host [36:34] page pending recline [36:36] um and those pages still at that point [36:39] have not been uh [36:41] touched by in any sort of way and then [36:43] we'll let the the host issue hyper calls [36:46] to reclaim the pages one by one when [36:48] that happens the hypervisor will will [36:49] map the pages using a fixed map clear [36:51] them and then return them to the host [36:52] and so on and so forth until everything [36:55] has been reclaimed which means that we [36:57] can reschedule between all of those [36:58] hyper calls and make sure that we don't [37:00] have a really long [37:02] um non-functionable section [37:04] all right that's uh [37:07] kind of the end of the of the story here [37:10] um there are a lot of things that I have [37:12] not [37:13] talked about there's [37:15] you know there is there is a lot more [37:17] that there's just no time to talk about [37:19] it but obviously there are [37:20] considerations around uh talking to the [37:22] secure World which is a big sort of a [37:24] big big thing in only in the arm world [37:26] we need DNA protections we have iom and [37:28] music port some are um use into into [37:31] pkvm Intel timers power management trng [37:35] and there's probably a lot of other [37:37] things that I just couldn't talk about [37:40] um and and to finish I just wanted to [37:43] mention a few limitations of the of the [37:46] past series that we have right now so we [37:48] are currently missing features even for [37:50] non-protected guests so things such as [37:52] dirty logging or read-only mem slots and [37:54] things like that we're working on all of [37:56] those things this is three things we [37:58] intend to have to have fully fully work [38:00] at the very least for what I mean [38:01] definitely for our non-protected guests [38:03] before we can learn this stuff [38:05] uh some of the other things we don't [38:08] currently support is huge huge pages [38:09] also very much on the to-do list we [38:12] don't support care exec uh or things [38:15] like device assignment yet [38:18] and with that I will thank you all and [38:21] open it up four questions all right [38:26] [Applause] [38:33] could I ask the virtual questions first [38:37] um yeah what's the plan to support smmu [38:40] in pkvm and how to manage the page [38:43] ownership after smmu [38:45] yeah so uh we definitely plan to to have [38:49] SMA Municipal [38:50] um [38:51] uh there is somewhere the Lenovo of the [38:53] Sun going to to get that to get that [38:55] done I'm not sure if anything has been [38:57] posted on the list yet but I will expect [38:59] that to happen uh eventually [39:02] um in terms of page ownership the the [39:04] way I mean for the SMU is relatively [39:07] simple we just do almost the same thing [39:09] as we do for the host we just have to it [39:12] also depends on whether or not we can [39:14] handle uh some new folds directly at tl2 [39:18] but uh it's it's it's not fundamentally [39:21] different every time you have to update [39:23] the host page table we would need to [39:24] have to go and walk the stage two page [39:26] tables for the SMU page table sorry and [39:29] update the page tables accordingly to to [39:31] match what we've done to them to the to [39:34] the host [39:35] the other question is is there any [39:37] performance implication that the host [39:39] now runs in a stage 2 translation just [39:42] by this translation itself and how [39:44] noticeable is this yeah so there is uh [39:48] potentially I mean theoretically the the [39:50] main [39:51] overhead is is going to come from Amin [39:55] there are two things it's going to be uh [39:57] tlb pressure and the cost of tlb misses [40:01] but essentially which is essentially the [40:03] same thing but yes by having an [40:05] additional layer of translation it means [40:06] we we you know need to take moles we use [40:09] more the trbs we put more pressure on [40:11] the clb's uh one thing that's [40:13] interesting to to note however on that [40:16] front is that [40:18] the host owns the vast majority of [40:20] memory [40:21] and all the host mappings are identity [40:24] mapped which means we're on a position [40:25] where we can very easily use really [40:28] really large block mappings to cover [40:29] everything so we can use gigabyte [40:31] mappings to cover pretty much all of [40:33] memory during boot which makes the the [40:35] yellow head of the of the stage 2 [40:37] practically impossible to actually [40:39] measure so yeah [40:43] okay so I have a question it seems like [40:45] the tear down process has some overhead [40:48] because you need to uh do the yeah [40:51] that's exactly that yeah [40:55] how much of a overhead is that and have [40:58] you considered doing that asynchronously [41:00] uh [41:01] I well so so far it's not been too big [41:05] of a problem for us and that's I think [41:06] that's mostly because you know this the [41:09] the main users of this so far are like [41:12] Mobile use cases and we don't have [41:13] extremely large machines with like [41:15] terabytes of memory or something it's [41:16] like a gig [41:17] so it's not the end of the world I think [41:19] if we wanted to scale this yes then [41:21] doing things asynchronously would be uh [41:24] would probably be a good idea because [41:26] that's what we're trying to do on s390 [41:28] now so right yes well I've seen your [41:30] talk it was quite interesting so I think [41:31] the the idea of of uh just forking that [41:34] and having a separate process to the [41:35] cleanup for us to hide the latency to [41:37] some degree I think it's a what would [41:39] perfectly be applicable to this just a [41:41] second another thing which is like we [41:44] just want to understand something [41:46] from what I understand is like if the [41:48] user [41:50] from the qme console tries to like dump [41:53] some memory from a secure guest qm will [41:55] die yes okay [41:57] thanks [42:09] yeah so uh regarding his observation [42:13] that's why they are going to look into [42:15] the private memory thing and my question [42:18] to you if you could go also for the [42:20] record a bit more in detail as to why [42:22] you need the in place conversion [42:24] uh okay so then [42:27] the [42:30] one of the reasons why we're doing pkvm [42:32] is is to replace uh uh well maybe not [42:36] replace but to [42:38] to have a better way of of doing [42:39] something that we've been doing for for [42:41] many times for for many years uh having [42:43] things that run protected from the host [42:46] kernel on the phone is actually really [42:48] old we've been doing that for 15 years [42:49] and this is It's the trust tone uh story [42:52] so there's a lot of baggage there which [42:54] just have a ton of History [42:55] and transitioning pages to to trade zone [42:59] and having payloads that can in some [43:01] cases use really large amounts of memory [43:03] to deal with I don't know video playback [43:06] DRM use cases you know you can think of [43:09] of uh you know if you have like 4K video [43:12] frames you're not gonna have megabytes [43:14] uh you know potentially it can be [43:16] hundreds of megabytes of memory that are [43:18] that are going to be passed around [43:19] between the host kernel and whatever is [43:23] going to be handling that into that that [43:24] sort of secure Island which is stress [43:27] Zone if we want to have some of the [43:29] Australian workloads moved towards VM we [43:31] need to find an efficient way to have [43:33] that zero copy sharing like memory [43:35] transfer happening between these two [43:36] things and this is why the In-Place [43:38] conversion stuff is necessary [43:40] yeah yeah so for the In-Place conversion [43:43] I think I have an idea that I'm like 95 [43:45] will work with the unmapping guest [43:47] private memory with the latest uh [43:50] proposal it's a shim around [43:53] um from MFD and the way that works is [43:55] that they just buried the App Ops inside [43:58] of another F Ops that doesn't wire up in [43:59] that map okay and so I think what we can [44:01] do is have KVM expose [44:04] or rather extend its API for how the [44:07] user space vmm says this is shared this [44:09] is private this is whatever and we can [44:11] add a third flavor that says this is [44:14] shared mapper I'm not shared this is [44:16] like user space mappable and only allow [44:19] that when it's not mapped into the guest [44:21] and then we can map from inside KVM so [44:24] that it doesn't require a mem slot [44:26] update so we avoid any srcu pane then [44:29] you can do all the filling from host [44:31] user space and then host user base can [44:33] say convert in place and if the [44:35] underlying hypervisor allows that so [44:37] like tdxse SNP don't allow that but pkbm [44:41] does yeah then we can do an In-Place [44:42] conversion we have to zero the memory [44:43] and it just naturally gets into the [44:44] gasket so I'm like 95 certain that'll [44:46] work yeah that's great to heal honestly [44:48] I've I've seen patches floating around I [44:50] have enough time to actually look at [44:51] them but it's it's really really good to [44:53] hear [44:54] thanks [45:03] so I'm sorry that's really a very basic [45:06] question but uh since you have the owner [45:09] ID only in the invalid pte's other [45:11] scenarios where you need to know the [45:14] owner of a PT uh in a full Handler [45:18] when it's valid when it's actually [45:19] mapped [45:21] um [45:22] so if it's mapped into the host that [45:25] means that it has access to it so we're [45:26] not going to take a fall if you have a [45:28] you know [45:29] I'm thinking about the case where uh the [45:33] guest would actually [45:34] touch that page but now it's mapped by [45:36] the host so why would that not [45:40] is that what is [45:43] um I'm not sure why can you [45:45] maybe so if the guest can can hit an [45:47] invalid pte why can't it vary the a [45:50] valid pity [45:52] that's the question I have [46:01] yes yeah yeah so [46:05] yeah so we're doing that that game with [46:08] the ownership ID only for the host stage [46:12] two page table uh every time we we hit [46:15] an invalid PT into into the guest we're [46:17] not going to map some just a memory [46:18] random pages into the guest we'll have [46:20] to have the host tell us this is the [46:22] page that you can map into the guest at [46:24] that IPA right now that's a little IPA [46:26] space so and then we'll have to do the [46:29] checks that the host is actually allowed [46:30] to donate that page to get all that [46:32] stuff oh I think the the key point there [46:34] is the other the thing you said at some [46:35] other place that the sharing is only [46:38] between two entities a little more okay [46:51] uh how do you attest to the guests I'm [46:54] guessing the host you attest through [46:56] some bootloader stuff in Android but [46:57] what do you do about the guest payloads [46:59] like the boot firmware and make sure [47:01] that you have everything running right [47:03] yeah so the [47:04] the boot code that I was tough sorry [47:08] yeah the idea is that the the host [47:10] doesn't actually see the bootload from [47:11] from the guests uh it's the it's the [47:14] hypervisor that copies a trusted [47:17] bootloader that was provided by the you [47:19] know that was signed and whatever that [47:21] gets copied into the guest and the host [47:22] doesn't get involved into this at all [47:24] and then this in our first [47:27] implementation we have something that's [47:29] based on new boot that has that we'd [47:31] like to rewrite that in some other [47:33] language that's a bit more a bit [47:35] stronger but uh the idea is that we have [47:37] the hypervisor copy the bootloader into [47:40] the guests false the guests to run that [47:42] bootloader first and then that [47:44] bootloader will be in charge of [47:45] receiving the guest payload through [47:47] whatever mechanism it wants in practice [47:49] for us it's about IO but it could just [47:51] read it from gas memory directly and [47:53] then can measure the gas payload and [47:56] then that bootloader will have access to [47:58] secrets that the host doesn't have [47:59] access to in order to sort of derive a [48:03] signed identity and portable identity [48:05] for for the guests which the guests can [48:07] then use to say okay I can I can prove [48:09] based on what the trusted bootloader has [48:11] given me I can prove that I am who I [48:13] pretend to be [48:15] what's the root of trust for The Trusted [48:17] bootloader that gets shot into the [48:18] guests then sorry how do you [48:20] um how does the hypervisor know that the [48:24] bootloader is loading into the guest is [48:26] actually trusted and correct so the [48:28] bootload that gets loaded into the guest [48:30] is [48:31] uh placed by the the device bootloader [48:34] in a memory card out which we just take [48:37] away from the host uh really early [48:40] during boot so we unmap if we have [48:42] American region in physical mainly where [48:44] the bootloader attempts you know we dump [48:46] the kernel image as well as the device [48:47] with loader and just unmapped that from [48:48] the host recursively though how what's [48:51] your end route of trust like [48:53] if you're using a DRM use case and the [48:57] whole point of that is someone doesn't [48:58] want you decoding your video how do you [48:59] know that someone hasn't rooted your [49:00] phone and is using pkvm to get it and [49:03] they've slightly modified things or [49:05] something and they're loading a so yeah [49:08] so if you the the way these things [49:11] typically work so I'm not the best [49:12] person to answer that question I'll be [49:13] I'll be honest but the way this [49:15] typically works is if you wrote the [49:16] device the the keys that the bootloader [49:19] can use to sign whatever payload is done [49:21] are removed so we just wipe the keys [49:23] when you bring the device thanks [49:27] and that's also how you know you cannot [49:29] just uh you know we will never provide a [49:33] bootload that bootloader to [49:34] non-protected guest for instance and [49:36] just load the right payload but in a [49:37] non-protected guest and just have you [49:39] know the way the way this works is just [49:41] by just not providing the key when you [49:43] come and that gets wiped when when the [49:45] device is rooted [49:55] all right well thank you very much [49:59] foreign