From: Jean-Philippe Brucker <[email protected]> Use RAM instead of flash to store firmware image and boot information, including UEFI variables. That way the firmware can be measured and included in the Realm's initial measurements. It also prevents variable stored in flash and modified as part of the boot process to be leaked outside the Realm.
Signed-off-by: Jean-Philippe Brucker <[email protected]> Signed-off-by: Mathieu Poirier <[email protected]> --- hw/arm/boot.c | 37 +++++++++++++++++++++++++-- hw/arm/virt.c | 58 +++++++++++++++++++++++++++++++++++++++++++ include/hw/arm/boot.h | 9 +++++++ 3 files changed, 102 insertions(+), 2 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index bcab21d349b4..aea044fbed9a 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -1113,7 +1113,36 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu, } } -static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info) +static void arm_setup_confidential_firmware_boot(ARMCPU *cpu, + struct arm_boot_info *info, + const char *firmware_filename) +{ + ssize_t fw_size; + const char *fname; + AddressSpace *as = arm_boot_address_space(cpu, info); + + fname = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware_filename); + if (!fname) { + error_report("Could not find firmware image '%s'", firmware_filename); + exit(1); + } + + /* + * Load the firmware image in the Realm's address space. Mapping of the + * firmware area in the Realm's address space is done in function + * virt_confidential_firmware_init(). + */ + fw_size = load_image_targphys_as(firmware_filename, + info->firmware_base, + info->firmware_max_size, as, NULL); + if (fw_size <= 0) { + error_report("could not load firmware '%s'", firmware_filename); + exit(1); + } +} + +static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info, + const char *firmware_filename) { /* Set up for booting firmware (which might load a kernel via fw_cfg) */ @@ -1169,6 +1198,10 @@ static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info) } } + if (info->confidential) { + arm_setup_confidential_firmware_boot(cpu, info, firmware_filename); + } + /* * We will start from address 0 (typically a boot ROM image) in the * same way as hardware. Leave env->boot_info NULL, so that @@ -1216,7 +1249,7 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { - arm_setup_firmware_boot(cpu, info); + arm_setup_firmware_boot(cpu, info, ms->firmware); } else { arm_setup_direct_kernel_boot(cpu, info); } diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 2e8e29244284..8b79c3ea28e5 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1821,6 +1821,16 @@ static PFlashCFI01 *virt_flash_create1(VirtMachineState *vms, static void virt_flash_create(VirtMachineState *vms) { + /* + * For Realms, the firmware image is placed directly in the guest's + * RAM area. The association between the final location in the + * guest's RAM and the system memory is done in function + * virt_confidential_firmware_init(). + */ + if (virt_machine_is_confidential(vms)) { + return; + } + vms->flash[0] = virt_flash_create1(vms, "virt.flash0", "pflash0"); vms->flash[1] = virt_flash_create1(vms, "virt.flash1", "pflash1"); } @@ -1871,6 +1881,16 @@ static void virt_flash_fdt(VirtMachineState *vms, MachineState *ms = MACHINE(vms); char *nodename; + /* + * For Realms the firmware images are stored in the guest's address + * space. As such there is no need for flash configuration in the FDT. + * See function virt_confidential_firmware_init() and + * arm_setup_confidential_firmware_boot() for details. + */ + if (virt_machine_is_confidential(vms)) { + return; + } + if (sysmem == secure_sysmem) { /* Report both flash devices as a single node in the DT */ nodename = g_strdup_printf("/flash@%" PRIx64, flashbase); @@ -1906,6 +1926,32 @@ static void virt_flash_fdt(VirtMachineState *vms, } } +static bool virt_confidential_firmware_init(VirtMachineState *vms, + MemoryRegion *sysmem) +{ + MemoryRegion *fw_ram; + hwaddr fw_base = vms->memmap[VIRT_FLASH].base; + hwaddr fw_size = vms->memmap[VIRT_FLASH].size; + + if (!MACHINE(vms)->firmware) { + return false; + } + + assert(machine_require_guest_memfd(MACHINE(vms))); + + fw_ram = g_new(MemoryRegion, 1); + memory_region_init_ram_guest_memfd(fw_ram, NULL, "fw_ram", fw_size, + &error_fatal); + /* + * Map the guest's firmware image directly in its address space. + * Copying of the firmware image itself is done in function + * arm_setup_confidential_firmware_boot(). + */ + memory_region_add_subregion(sysmem, fw_base, fw_ram); + + return true; +} + static bool virt_firmware_init(VirtMachineState *vms, MemoryRegion *sysmem, MemoryRegion *secure_sysmem) @@ -1914,6 +1960,15 @@ static bool virt_firmware_init(VirtMachineState *vms, const char *bios_name; BlockBackend *pflash_blk0; + /* + * For a confidential VM, the firmware image and any boot information, + * including EFI variables, are stored in RAM in order to be measurable and + * private. Create a RAM region and load the firmware image there. + */ + if (virt_machine_is_confidential(vms)) { + return virt_confidential_firmware_init(vms, sysmem); + } + /* Map legacy -drive if=pflash to machine properties */ for (i = 0; i < ARRAY_SIZE(vms->flash); i++) { pflash_cfi01_legacy_drive(vms->flash[i], @@ -3276,7 +3331,10 @@ static void machvirt_init(MachineState *machine) vms->bootinfo.get_dtb = machvirt_dtb; vms->bootinfo.skip_dtb_autoload = true; vms->bootinfo.firmware_loaded = firmware_loaded; + vms->bootinfo.firmware_base = vms->memmap[VIRT_FLASH].base; + vms->bootinfo.firmware_max_size = vms->memmap[VIRT_FLASH].size; vms->bootinfo.psci_conduit = vms->psci_conduit; + vms->bootinfo.confidential = virt_machine_is_confidential(vms); arm_load_kernel(ARM_CPU(first_cpu), machine, &vms->bootinfo); vms->machine_done.notify = virt_machine_done; diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index 94386fdbea6c..fe7e14e94651 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -113,6 +113,10 @@ struct arm_boot_info { */ bool firmware_loaded; + /* Used when loading firmware into RAM */ + hwaddr firmware_base; + hwaddr firmware_max_size; + /* Address at which board specific loader/setup code exists. If enabled, * this code-blob will run before anything else. It must return to the * caller via the link register. There is no stack set up. Enabled by @@ -136,6 +140,11 @@ struct arm_boot_info { /* CPU having load the kernel and that should be the first to boot. */ ARMCPU *primary_cpu; + + /* + * Confidential guest boot loads everything into RAM so it can be measured. + */ + bool confidential; }; /** -- 2.43.0
