@@ -112,6 +112,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
@@ -132,6 +136,11 @@ struct arm_boot_info {
bool secure_board_setup;
arm_endianness endianness;
+
+ /*
+ * Confidential guest boot loads everything into RAM so it can be measured.
+ */
+ bool confidential;
};
/**
@@ -1154,7 +1154,31 @@ 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);
+ }
+
+ fw_size = load_image_targphys_as(firmware_filename,
+ info->firmware_base,
+ info->firmware_max_size, as);
+ 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) */
@@ -1205,6 +1229,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
@@ -1243,9 +1271,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
info->dtb_filename = ms->dtb;
info->dtb_limit = 0;
- /* Load the kernel. */
+ /* Load the kernel and/or firmware. */
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);
}
@@ -1178,6 +1178,10 @@ static PFlashCFI01 *virt_flash_create1(VirtMachineState *vms,
static void virt_flash_create(VirtMachineState *vms)
{
+ 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");
}
@@ -1213,6 +1217,10 @@ static void virt_flash_map(VirtMachineState *vms,
hwaddr flashsize = vms->memmap[VIRT_FLASH].size / 2;
hwaddr flashbase = vms->memmap[VIRT_FLASH].base;
+ if (virt_machine_is_confidential(vms)) {
+ return;
+ }
+
virt_flash_map1(vms->flash[0], flashbase, flashsize,
secure_sysmem);
virt_flash_map1(vms->flash[1], flashbase + flashsize, flashsize,
@@ -1228,6 +1236,10 @@ static void virt_flash_fdt(VirtMachineState *vms,
MachineState *ms = MACHINE(vms);
char *nodename;
+ 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);
@@ -1263,6 +1275,26 @@ 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);
+ memory_region_add_subregion(sysmem, fw_base, fw_ram);
+
+ return true;
+}
+
static bool virt_firmware_init(VirtMachineState *vms,
MemoryRegion *sysmem,
MemoryRegion *secure_sysmem)
@@ -1271,6 +1303,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],
@@ -2367,7 +2408,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;
The flash device that holds firmware code relies on read-only stage-2 mappings. Read accesses behave as RAM and write accesses as MMIO. Since the RMM does not support read-only mappings we cannot use the flash device as-is. That isn't a problem because the firmware does not want to disclose any information to the host, hence will not store its variables in clear persistent memory. We can therefore replace the flash device with RAM, and load the firmware there. Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org> --- v1->v2: new --- include/hw/arm/boot.h | 9 +++++++++ hw/arm/boot.c | 34 ++++++++++++++++++++++++++++++--- hw/arm/virt.c | 44 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-)