TDX VM needs to boot with Trust Domain Virtual Firmware (TDVF). Unlike
that OVMF is mapped as rom device, TDVF needs to be mapped as private
memory. This is because TDX architecture doesn't provide read-only
capability for VMM, and it doesn't support instruction emulation due
to guest memory and registers are not accessible for VMM.
On the other hand, OVMF can work as TDVF, which is usually configured
as pflash device in QEMU. To keep the same usage (QEMU parameter),
introduce ram_mode to pflash for TDVF. When it's creating a TDX VM,
ram_mode will be enabled automatically that map the firmware as RAM.
Note, this implies two things:
1. TDVF (OVMF) is not read-only (write-protected).
2. It doesn't support non-volatile UEFI variables as what pflash
supports that the change to non-volatile UEFI variables won't get
synced back to backend vars.fd file.
Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
---
hw/block/pflash_cfi01.c | 25 ++++++++++++++++++-------
hw/i386/pc_sysfw.c | 14 +++++++++++---
2 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c
index 74c7190302bd..55e8bb2bd5ee 100644
--- a/hw/block/pflash_cfi01.c
+++ b/hw/block/pflash_cfi01.c
@@ -87,6 +87,7 @@ struct PFlashCFI01 {
void *storage;
VMChangeStateEntry *vmstate;
bool old_multiple_chip_handling;
+ bool ram_mode; /* if 1, the flash is mapped as RAM */
};
static int pflash_post_load(void *opaque, int version_id);
@@ -818,17 +819,24 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp)
total_len = pfl->sector_len * pfl->nb_blocs;
- memory_region_init_rom_device(
- &pfl->mem, OBJECT(dev),
- &pflash_cfi01_ops,
- pfl,
- pfl->name, total_len, errp);
+ if (pfl->ram_mode) {
+ memory_region_init_ram(&pfl->mem, OBJECT(dev),pfl->name, total_len, errp);
+ } else {
+ memory_region_init_rom_device(
+ &pfl->mem, OBJECT(dev),
+ &pflash_cfi01_ops,
+ pfl,
+ pfl->name, total_len, errp);
+ }
if (*errp) {
return;
}
pfl->storage = memory_region_get_ram_ptr(&pfl->mem);
- sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
+
+ if (!pfl->ram_mode) {
+ sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem);
+ }
if (pfl->blk) {
uint64_t perm;
@@ -879,7 +887,9 @@ static void pflash_cfi01_system_reset(DeviceState *dev)
*/
pfl->cmd = 0x00;
pfl->wcycle = 0;
- memory_region_rom_device_set_romd(&pfl->mem, true);
+ if (!pfl->ram_mode) {
+ memory_region_rom_device_set_romd(&pfl->mem, true);
+ }
/*
* The WSM ready timer occurs at most 150ns after system reset.
* This model deliberately ignores this delay.
@@ -924,6 +934,7 @@ static Property pflash_cfi01_properties[] = {
DEFINE_PROP_STRING("name", PFlashCFI01, name),
DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01,
old_multiple_chip_handling, false),
+ DEFINE_PROP_BOOL("ram-mode", PFlashCFI01, ram_mode, false),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 75b34d02cb4f..03c84b5aaa32 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -38,6 +38,7 @@
#include "hw/block/flash.h"
#include "sysemu/kvm.h"
#include "sev.h"
+#include "kvm/tdx.h"
#define FLASH_SECTOR_SIZE 4096
@@ -184,12 +185,19 @@ static void pc_system_flash_map(PCMachineState *pcms,
total_size += size;
qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks",
size / FLASH_SECTOR_SIZE);
+ qdev_prop_set_bit(DEVICE(system_flash), "ram-mode", is_tdx_vm());
sysbus_realize_and_unref(SYS_BUS_DEVICE(system_flash), &error_fatal);
- sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0,
- 0x100000000ULL - total_size);
+ flash_mem = pflash_cfi01_get_memory(system_flash);
+ if (is_tdx_vm()) {
+ memory_region_add_subregion(get_system_memory(),
+ 0x100000000ULL - total_size,
+ flash_mem);
+ } else {
+ sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0,
+ 0x100000000ULL - total_size);
+ }
if (i == 0) {
- flash_mem = pflash_cfi01_get_memory(system_flash);
pc_isa_bios_init(rom_memory, flash_mem, size);
/* Encrypt the pflash boot ROM */
--
2.27.0
Hi, On 17/3/22 14:58, Xiaoyao Li wrote: > TDX VM needs to boot with Trust Domain Virtual Firmware (TDVF). Unlike > that OVMF is mapped as rom device, TDVF needs to be mapped as private > memory. This is because TDX architecture doesn't provide read-only > capability for VMM, and it doesn't support instruction emulation due > to guest memory and registers are not accessible for VMM. > > On the other hand, OVMF can work as TDVF, which is usually configured > as pflash device in QEMU. To keep the same usage (QEMU parameter), > introduce ram_mode to pflash for TDVF. When it's creating a TDX VM, > ram_mode will be enabled automatically that map the firmware as RAM. > > Note, this implies two things: > 1. TDVF (OVMF) is not read-only (write-protected). > > 2. It doesn't support non-volatile UEFI variables as what pflash > supports that the change to non-volatile UEFI variables won't get > synced back to backend vars.fd file. > > Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com> > --- > hw/block/pflash_cfi01.c | 25 ++++++++++++++++++------- > hw/i386/pc_sysfw.c | 14 +++++++++++--- > 2 files changed, 29 insertions(+), 10 deletions(-) If you don't need a pflash device, don't use it: simply map your nvram region as ram in your machine. No need to clutter the pflash model like that. NAcked-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
On 3/18/2022 10:07 PM, Philippe Mathieu-Daudé wrote:
> Hi,
>
> On 17/3/22 14:58, Xiaoyao Li wrote:
>> TDX VM needs to boot with Trust Domain Virtual Firmware (TDVF). Unlike
>> that OVMF is mapped as rom device, TDVF needs to be mapped as private
>> memory. This is because TDX architecture doesn't provide read-only
>> capability for VMM, and it doesn't support instruction emulation due
>> to guest memory and registers are not accessible for VMM.
>>
>> On the other hand, OVMF can work as TDVF, which is usually configured
>> as pflash device in QEMU. To keep the same usage (QEMU parameter),
>> introduce ram_mode to pflash for TDVF. When it's creating a TDX VM,
>> ram_mode will be enabled automatically that map the firmware as RAM.
>>
>> Note, this implies two things:
>> 1. TDVF (OVMF) is not read-only (write-protected).
>>
>> 2. It doesn't support non-volatile UEFI variables as what pflash
>> supports that the change to non-volatile UEFI variables won't get
>> synced back to backend vars.fd file.
>>
>> Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com>
>> ---
>> hw/block/pflash_cfi01.c | 25 ++++++++++++++++++-------
>> hw/i386/pc_sysfw.c | 14 +++++++++++---
>> 2 files changed, 29 insertions(+), 10 deletions(-)
>
> If you don't need a pflash device, don't use it: simply map your nvram
> region as ram in your machine. No need to clutter the pflash model like
> that.
I know it's dirty to hack the pflash device. The purpose is to make the
user interface unchanged that people can still use
-drive if=pflash,format=raw,unit=0,file=/path/to/OVMF_CODE.fd
-drive if=pflash,format=raw,unit=1,file=/path/to/OVMF_VARS.fd
to create TD guest.
I can go back to use generic loader[1] to load TDVF in v2.
[1]
https://lore.kernel.org/qemu-devel/acaf651389c3f407a9d6d0a2e943daf0a85bb5fc.1625704981.git.isaku.yamahata@intel.com/
> NAcked-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>
Hi, > > If you don't need a pflash device, don't use it: simply map your nvram > > region as ram in your machine. No need to clutter the pflash model like > > that. Using the pflash device for something which isn't actually flash looks a bit silly indeed. > > I know it's dirty to hack the pflash device. The purpose is to make the user > interface unchanged that people can still use > > -drive if=pflash,format=raw,unit=0,file=/path/to/OVMF_CODE.fd > -drive if=pflash,format=raw,unit=1,file=/path/to/OVMF_VARS.fd > > to create TD guest. Well, if persistent vars are not supported anyway there is little reason to split the firmware into CODE and VARS files. You can use just use OVMF.fd with a single pflash device. libvirt recently got support for that. Just using -bios OVMF.fd might work too. Daniel tried that recently for sev, but ran into problems with wiring up ovmf metadata parsing for -bios. Don't remember the details though. take care, Gerd
On Tue, Mar 22, 2022 at 10:21:41AM +0100, Gerd Hoffmann wrote: > Hi, > > > > If you don't need a pflash device, don't use it: simply map your nvram > > > region as ram in your machine. No need to clutter the pflash model like > > > that. > > Using the pflash device for something which isn't actually flash looks a > bit silly indeed. > > > > > I know it's dirty to hack the pflash device. The purpose is to make the user > > interface unchanged that people can still use > > > > -drive if=pflash,format=raw,unit=0,file=/path/to/OVMF_CODE.fd > > -drive if=pflash,format=raw,unit=1,file=/path/to/OVMF_VARS.fd > > > > to create TD guest. > > Well, if persistent vars are not supported anyway there is little reason > to split the firmware into CODE and VARS files. You can use just use > OVMF.fd with a single pflash device. libvirt recently got support for > that. Agreed. > Just using -bios OVMF.fd might work too. Daniel tried that recently for > sev, but ran into problems with wiring up ovmf metadata parsing for > -bios. Don't remember the details though. It was related to the BIOS shadowing, whereby QEMU loads it at one address, and then when CPUs start it is copied to another address. This was not compatible with the way AMD SEV wants to do measurement of the firmware. May or may not be relevant for TDX, I don't know enough about TDX to say. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Hi, > > Just using -bios OVMF.fd might work too. Daniel tried that recently for > > sev, but ran into problems with wiring up ovmf metadata parsing for > > -bios. Don't remember the details though. > > It was related to the BIOS shadowing, whereby QEMU loads it at one > address, and then when CPUs start it is copied to another address. Is this the top 128k of the firmware being copied below 1M so the firmware reset vector is available in real mode address space? > This was not compatible with the way AMD SEV wants to do measurement > of the firmware. May or may not be relevant for TDX, I don't know > enough about TDX to say. TDX boots in 32bit mode, so simply skipping any real mode compatibility stuff shouldn't cause any problems here. Not sure about SEV. There is this SevProcessorReset entry in the ovmf metadata block. Is that the SEV reset vector? If SEV cpu bringup doesn't go through real mode either we maybe can just skip the BIOS shadowing setup for confidential computing guests ... take care, Gerd
On Tue, Mar 22, 2022 at 11:35:18AM +0100, Gerd Hoffmann wrote: > Hi, > > > > Just using -bios OVMF.fd might work too. Daniel tried that recently for > > > sev, but ran into problems with wiring up ovmf metadata parsing for > > > -bios. Don't remember the details though. > > > > It was related to the BIOS shadowing, whereby QEMU loads it at one > > address, and then when CPUs start it is copied to another address. > > Is this the top 128k of the firmware being copied below 1M so the > firmware reset vector is available in real mode address space? It was the 'rom_reset' method in hw/core/loader.c that was involved in the root of the problem, copying the firmware from ROM to RAM. At the time I did try a gross hack that (IIRC) disabled the rom_reset logic, and munged x86_bios_rom_init so that it would force load it straight at the RAM location. I couldn't figure out an attractive way to make this into something supportable, so abandoned the whole idea. Messing with this area of code is a somewhat beyond my level of understanding of x86 boot process anyway. > > This was not compatible with the way AMD SEV wants to do measurement > > of the firmware. May or may not be relevant for TDX, I don't know > > enough about TDX to say. > > TDX boots in 32bit mode, so simply skipping any real mode compatibility > stuff shouldn't cause any problems here. > > Not sure about SEV. There is this SevProcessorReset entry in the ovmf > metadata block. Is that the SEV reset vector? If SEV cpu bringup > doesn't go through real mode either we maybe can just skip the BIOS > shadowing setup for confidential computing guests ... With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Hi,
> At the time I did try a gross hack that (IIRC) disabled the
> rom_reset logic, and munged x86_bios_rom_init so that it would
> force load it straight at the RAM location.
Sounds reasonable. The whole rom logic exists to handle resets,
but with confidential guests we don't need that, we can't change
guest state to perform a reset anyway ...
take care,
Gerd
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
index 4cf107baea34..169ef96682de 100644
--- a/hw/i386/x86.c
+++ b/hw/i386/x86.c
@@ -1115,15 +1115,26 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware,
goto bios_error;
}
bios = g_malloc(sizeof(*bios));
+
memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal);
- if (!isapc_ram_fw) {
- memory_region_set_readonly(bios, true);
- }
- ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
- if (ret != 0) {
- bios_error:
- fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
- exit(1);
+ if (1 /* confidential computing */) {
+ /*
+ * The concept of a "reset" simply doesn't exist for
+ * confidential computing guests, we have to destroy and
+ * re-launch them instead. So there is no need to register
+ * the firmware as rom to properly re-initialize on reset.
+ * Just go for a straight file load instead.
+ */
+ void *ptr = memory_region_get_ram_ptr(bios);
+ load_image_size(filename, ptr, bios_size);
+ } else {
+ if (!isapc_ram_fw) {
+ memory_region_set_readonly(bios, true);
+ }
+ ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
+ if (ret != 0) {
+ goto bios_error;
+ }
}
g_free(filename);
@@ -1144,6 +1155,11 @@ void x86_bios_rom_init(MachineState *ms, const char *default_firmware,
memory_region_add_subregion(rom_memory,
(uint32_t)(-bios_size),
bios);
+ return;
+
+bios_error:
+ fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
+ exit(1);
}
bool x86_machine_is_smm_enabled(const X86MachineState *x86ms)
On Tue, Mar 22, 2022 at 01:20:24PM +0100, Gerd Hoffmann wrote: > Hi, > > > At the time I did try a gross hack that (IIRC) disabled the > > rom_reset logic, and munged x86_bios_rom_init so that it would > > force load it straight at the RAM location. > > Sounds reasonable. The whole rom logic exists to handle resets, > but with confidential guests we don't need that, we can't change > guest state to perform a reset anyway ... Completed, cleaned up a bit, but untested: https://git.kraxel.org/cgit/qemu/log/?h=sirius/cc Any chance you can give this a try? thanks, Gerd
On 3/24/2022 4:35 PM, Gerd Hoffmann wrote: > On Tue, Mar 22, 2022 at 01:20:24PM +0100, Gerd Hoffmann wrote: >> Hi, >> >>> At the time I did try a gross hack that (IIRC) disabled the >>> rom_reset logic, and munged x86_bios_rom_init so that it would >>> force load it straight at the RAM location. >> >> Sounds reasonable. The whole rom logic exists to handle resets, >> but with confidential guests we don't need that, we can't change >> guest state to perform a reset anyway ... > > Completed, cleaned up a bit, but untested: > https://git.kraxel.org/cgit/qemu/log/?h=sirius/cc > > Any chance you can give this a try? Hi Gred, I refactor the TDX series to load TDVF via "-bios" option upon it. No issue hit. Thanks, -Xiaoyao > thanks, > Gerd >
On 3/22/2022 5:29 PM, Daniel P. Berrangé wrote: > On Tue, Mar 22, 2022 at 10:21:41AM +0100, Gerd Hoffmann wrote: >> Hi, >> >>>> If you don't need a pflash device, don't use it: simply map your nvram >>>> region as ram in your machine. No need to clutter the pflash model like >>>> that. >> >> Using the pflash device for something which isn't actually flash looks a >> bit silly indeed. >> >>> >>> I know it's dirty to hack the pflash device. The purpose is to make the user >>> interface unchanged that people can still use >>> >>> -drive if=pflash,format=raw,unit=0,file=/path/to/OVMF_CODE.fd >>> -drive if=pflash,format=raw,unit=1,file=/path/to/OVMF_VARS.fd >>> >>> to create TD guest. >> >> Well, if persistent vars are not supported anyway there is little reason >> to split the firmware into CODE and VARS files. You can use just use >> OVMF.fd with a single pflash device. libvirt recently got support for >> that. > > Agreed. The purpose of using split firmware is that people can share the same code.fd while using different vars.fd
On Thu, Mar 24, 2022 at 02:13:53PM +0800, Xiaoyao Li wrote: > On 3/22/2022 5:29 PM, Daniel P. Berrangé wrote: > > On Tue, Mar 22, 2022 at 10:21:41AM +0100, Gerd Hoffmann wrote: > > > Hi, > > > > > > > > If you don't need a pflash device, don't use it: simply map your nvram > > > > > region as ram in your machine. No need to clutter the pflash model like > > > > > that. > > > > > > Using the pflash device for something which isn't actually flash looks a > > > bit silly indeed. > > > > > > > > > > > I know it's dirty to hack the pflash device. The purpose is to make the user > > > > interface unchanged that people can still use > > > > > > > > -drive if=pflash,format=raw,unit=0,file=/path/to/OVMF_CODE.fd > > > > -drive if=pflash,format=raw,unit=1,file=/path/to/OVMF_VARS.fd > > > > > > > > to create TD guest. > > > > > > Well, if persistent vars are not supported anyway there is little reason > > > to split the firmware into CODE and VARS files. You can use just use > > > OVMF.fd with a single pflash device. libvirt recently got support for > > > that. > > > > Agreed. > > The purpose of using split firmware is that people can share the same > code.fd while using different vars.fd That's fine for firmware that writes to vars.fd, but it was said earlier that changes aren't written with TDX (nor are they written with SEV), so a separate vars.fd serves no pupose in these cases. With regards, Daniel -- |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :| |: https://libvirt.org -o- https://fstop138.berrange.com :| |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
Hi, > > > Well, if persistent vars are not supported anyway there is little reason > > > to split the firmware into CODE and VARS files. You can use just use > > > OVMF.fd with a single pflash device. libvirt recently got support for > > > that. > > > > Agreed. > > The purpose of using split firmware is that people can share the same > code.fd while using different vars.fd Using different vars.fd files is pointless though when changes are never written back ... take care, Gerd
On 3/24/2022 3:58 PM, Gerd Hoffmann wrote: > Hi, > >>>> Well, if persistent vars are not supported anyway there is little reason >>>> to split the firmware into CODE and VARS files. You can use just use >>>> OVMF.fd with a single pflash device. libvirt recently got support for >>>> that. >>> >>> Agreed. >> >> The purpose of using split firmware is that people can share the same >> code.fd while using different vars.fd > > Using different vars.fd files is pointless though when changes are never > written back ... Yes, I agree on this. Off the topic. If we really want to NVRAM capability to TDX guest, 1) we can use the PV interface issue MMIO write in OVMF, like what SEV does in OVMF. 2) map OVMF as shared, thus existing pflash works well. However, both options will expose the content to VMM, which loses confidentiality. > take care, > Gerd >
On Mon, Mar 21, 2022 at 04:54:51PM +0800, Xiaoyao Li <xiaoyao.li@intel.com> wrote: > On 3/18/2022 10:07 PM, Philippe Mathieu-Daudé wrote: > > Hi, > > > > On 17/3/22 14:58, Xiaoyao Li wrote: > > > TDX VM needs to boot with Trust Domain Virtual Firmware (TDVF). Unlike > > > that OVMF is mapped as rom device, TDVF needs to be mapped as private > > > memory. This is because TDX architecture doesn't provide read-only > > > capability for VMM, and it doesn't support instruction emulation due > > > to guest memory and registers are not accessible for VMM. > > > > > > On the other hand, OVMF can work as TDVF, which is usually configured > > > as pflash device in QEMU. To keep the same usage (QEMU parameter), > > > introduce ram_mode to pflash for TDVF. When it's creating a TDX VM, > > > ram_mode will be enabled automatically that map the firmware as RAM. > > > > > > Note, this implies two things: > > > ?? 1. TDVF (OVMF) is not read-only (write-protected). > > > > > > ?? 2. It doesn't support non-volatile UEFI variables as what pflash > > > ???????? supports that the change to non-volatile UEFI variables won't get > > > ???????? synced back to backend vars.fd file. > > > > > > Signed-off-by: Xiaoyao Li <xiaoyao.li@intel.com> > > > --- > > > ?? hw/block/pflash_cfi01.c | 25 ++++++++++++++++++------- > > > ?? hw/i386/pc_sysfw.c?????????? | 14 +++++++++++--- > > > ?? 2 files changed, 29 insertions(+), 10 deletions(-) > > > > If you don't need a pflash device, don't use it: simply map your nvram > > region as ram in your machine. No need to clutter the pflash model like > > that. > > I know it's dirty to hack the pflash device. The purpose is to make the user > interface unchanged that people can still use > > -drive if=pflash,format=raw,unit=0,file=/path/to/OVMF_CODE.fd > -drive if=pflash,format=raw,unit=1,file=/path/to/OVMF_VARS.fd > > to create TD guest. For the compatibility for qemu command line, you don't have to modify pflash device. Don't instantiate pflash at pc_system_flash_create(), and at pc_system_firmware_init(), you can retrieve necessary parameters, and then populate memory. Although it's still hacky, it would be cleaner a bit. -- Isaku Yamahata <isaku.yamahata@gmail.com>
© 2016 - 2025 Red Hat, Inc.