mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* target/i386/kvm: Intel TDX support * target/i386/emulate: more lflags cleanups * meson: remove need for explicit listing of dependencies in hw_common_arch and target_common_arch * rust: small fixes * hpet: Reorganize register decoding to be more similar to Rust code * target/i386: fixes for AMD models * target/i386: new EPYC-Turin CPU model # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmg4BxwUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroP67gf+PEP4EDQP0AJUfxXYVsczGf5snGjz # ro8jYmKG+huBZcrS6uPK5zHYxtOI9bHr4ipTHJyHd61lyzN6Ys9amPbs/CRE2Q4x # Ky4AojPhCuaL2wHcYNcu41L+hweVQ3myj97vP3hWvkatulXYeMqW3/4JZgr4WZ69 # A9LGLtLabobTz5yLc8x6oHLn/BZ2y7gjd2LzTz8bqxx7C/kamjoDrF2ZHbX9DLQW # BKWQ3edSO6rorSNHWGZsy9BE20AEkW2LgJdlV9eXglFEuEs6cdPKwGEZepade4bQ # Rdt2gHTlQdUDTFmAbz8pttPxFGMC9Zpmb3nnicKJpKQAmkT/x4k9ncjyAQ== # =XmkU # -----END PGP SIGNATURE----- # gpg: Signature made Thu 29 May 2025 03:05:00 EDT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (77 commits) target/i386/tcg/helper-tcg: fix file references in comments target/i386: Add support for EPYC-Turin model target/i386: Update EPYC-Genoa for Cache property, perfmon-v2, RAS and SVM feature bits target/i386: Add couple of feature bits in CPUID_Fn80000021_EAX target/i386: Update EPYC-Milan CPU model for Cache property, RAS, SVM feature bits target/i386: Update EPYC-Rome CPU model for Cache property, RAS, SVM feature bits target/i386: Update EPYC CPU model for Cache property, RAS, SVM feature bits rust: make declaration of dependent crates more consistent docs: Add TDX documentation i386/tdx: Validate phys_bits against host value i386/tdx: Make invtsc default on i386/tdx: Don't treat SYSCALL as unavailable i386/tdx: Fetch and validate CPUID of TD guest target/i386: Print CPUID subleaf info for unsupported feature i386: Remove unused parameter "uint32_t bit" in feature_word_description() i386/cgs: Introduce x86_confidential_guest_check_features() i386/tdx: Define supported KVM features for TDX i386/tdx: Add XFD to supported bit of TDX i386/tdx: Add supported CPUID bits relates to XFAM i386/tdx: Add supported CPUID bits related to TD Attributes ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
+9
-2
@@ -471,7 +471,9 @@ int kvm_create_vcpu(CPUState *cpu)
|
||||
|
||||
cpu->kvm_fd = kvm_fd;
|
||||
cpu->kvm_state = s;
|
||||
cpu->vcpu_dirty = true;
|
||||
if (!s->guest_state_protected) {
|
||||
cpu->vcpu_dirty = true;
|
||||
}
|
||||
cpu->dirty_pages = 0;
|
||||
cpu->throttle_us_per_full = 0;
|
||||
|
||||
@@ -545,6 +547,11 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp)
|
||||
|
||||
trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu));
|
||||
|
||||
ret = kvm_arch_pre_create_vcpu(cpu, errp);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = kvm_create_vcpu(cpu);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret,
|
||||
@@ -2426,7 +2433,7 @@ static int kvm_recommended_vcpus(KVMState *s)
|
||||
|
||||
static int kvm_max_vcpus(KVMState *s)
|
||||
{
|
||||
int ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS);
|
||||
int ret = kvm_vm_check_extension(s, KVM_CAP_MAX_VCPUS);
|
||||
return (ret) ? ret : kvm_recommended_vcpus(s);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,15 @@ if get_option('plugins')
|
||||
tcg_ss.add(files('plugin-gen.c'))
|
||||
endif
|
||||
|
||||
libuser_ss.add_all(tcg_ss)
|
||||
libsystem_ss.add_all(tcg_ss)
|
||||
user_ss.add_all(tcg_ss)
|
||||
system_ss.add_all(tcg_ss)
|
||||
|
||||
libuser_ss.add(files(
|
||||
user_ss.add(files(
|
||||
'user-exec.c',
|
||||
'user-exec-stub.c',
|
||||
))
|
||||
|
||||
libsystem_ss.add(files(
|
||||
system_ss.add(files(
|
||||
'cputlb.c',
|
||||
'icount-common.c',
|
||||
'monitor.c',
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#CONFIG_QXL=n
|
||||
#CONFIG_SEV=n
|
||||
#CONFIG_SGA=n
|
||||
#CONFIG_TDX=n
|
||||
#CONFIG_TEST_DEVICES=n
|
||||
#CONFIG_TPM_CRB=n
|
||||
#CONFIG_TPM_TIS_ISA=n
|
||||
|
||||
@@ -38,6 +38,7 @@ Supported mechanisms
|
||||
Currently supported confidential guest mechanisms are:
|
||||
|
||||
* AMD Secure Encrypted Virtualization (SEV) (see :doc:`i386/amd-memory-encryption`)
|
||||
* Intel Trust Domain Extension (TDX) (see :doc:`i386/tdx`)
|
||||
* POWER Protected Execution Facility (PEF) (see :ref:`power-papr-protected-execution-facility-pef`)
|
||||
* s390x Protected Virtualization (PV) (see :doc:`s390x/protvirt`)
|
||||
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
Intel Trusted Domain eXtension (TDX)
|
||||
====================================
|
||||
|
||||
Intel Trusted Domain eXtensions (TDX) refers to an Intel technology that extends
|
||||
Virtual Machine Extensions (VMX) and Multi-Key Total Memory Encryption (MKTME)
|
||||
with a new kind of virtual machine guest called a Trust Domain (TD). A TD runs
|
||||
in a CPU mode that is designed to protect the confidentiality of its memory
|
||||
contents and its CPU state from any other software, including the hosting
|
||||
Virtual Machine Monitor (VMM), unless explicitly shared by the TD itself.
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
To run TD, the physical machine needs to have TDX module loaded and initialized
|
||||
while KVM hypervisor has TDX support and has TDX enabled. If those requirements
|
||||
are met, the ``KVM_CAP_VM_TYPES`` will report the support of ``KVM_X86_TDX_VM``.
|
||||
|
||||
Trust Domain Virtual Firmware (TDVF)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Trust Domain Virtual Firmware (TDVF) is required to provide TD services to boot
|
||||
TD Guest OS. TDVF needs to be copied to guest private memory and measured before
|
||||
the TD boots.
|
||||
|
||||
KVM vcpu ioctl ``KVM_TDX_INIT_MEM_REGION`` can be used to populate the TDVF
|
||||
content into its private memory.
|
||||
|
||||
Since TDX doesn't support readonly memslot, TDVF cannot be mapped as pflash
|
||||
device and it actually works as RAM. "-bios" option is chosen to load TDVF.
|
||||
|
||||
OVMF is the opensource firmware that implements the TDVF support. Thus the
|
||||
command line to specify and load TDVF is ``-bios OVMF.fd``
|
||||
|
||||
Feature Configuration
|
||||
---------------------
|
||||
|
||||
Unlike non-TDX VM, the CPU features (enumerated by CPU or MSR) of a TD are not
|
||||
under full control of VMM. VMM can only configure part of features of a TD on
|
||||
``KVM_TDX_INIT_VM`` command of VM scope ``MEMORY_ENCRYPT_OP`` ioctl.
|
||||
|
||||
The configurable features have three types:
|
||||
|
||||
- Attributes:
|
||||
- PKS (bit 30) controls whether Supervisor Protection Keys is exposed to TD,
|
||||
which determines related CPUID bit and CR4 bit;
|
||||
- PERFMON (bit 63) controls whether PMU is exposed to TD.
|
||||
|
||||
- XSAVE related features (XFAM):
|
||||
XFAM is a 64b mask, which has the same format as XCR0 or IA32_XSS MSR. It
|
||||
determines the set of extended features available for use by the guest TD.
|
||||
|
||||
- CPUID features:
|
||||
Only some bits of some CPUID leaves are directly configurable by VMM.
|
||||
|
||||
What features can be configured is reported via TDX capabilities.
|
||||
|
||||
TDX capabilities
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The VM scope ``MEMORY_ENCRYPT_OP`` ioctl provides command ``KVM_TDX_CAPABILITIES``
|
||||
to get the TDX capabilities from KVM. It returns a data structure of
|
||||
``struct kvm_tdx_capabilities``, which tells the supported configuration of
|
||||
attributes, XFAM and CPUIDs.
|
||||
|
||||
TD attributes
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
QEMU supports configuring raw 64-bit TD attributes directly via "attributes"
|
||||
property of "tdx-guest" object. Note, it's users' responsibility to provide a
|
||||
valid value because some bits may not supported by current QEMU or KVM yet.
|
||||
|
||||
QEMU also supports the configuration of individual attribute bits that are
|
||||
supported by it, via properties of "tdx-guest" object.
|
||||
E.g., "sept-ve-disable" (bit 28).
|
||||
|
||||
MSR based features
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Current KVM doesn't support MSR based feature (e.g., MSR_IA32_ARCH_CAPABILITIES)
|
||||
configuration for TDX, and it's a future work to enable it in QEMU when KVM adds
|
||||
support of it.
|
||||
|
||||
Feature check
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
QEMU checks if the final (CPU) features, determined by given cpu model and
|
||||
explicit feature adjustment of "+featureA/-featureB", can be supported or not.
|
||||
It can produce feature not supported warning like
|
||||
|
||||
"warning: host doesn't support requested feature: CPUID.07H:EBX.intel-pt [bit 25]"
|
||||
|
||||
It can also produce warning like
|
||||
|
||||
"warning: TDX forcibly sets the feature: CPUID.80000007H:EDX.invtsc [bit 8]"
|
||||
|
||||
if the fixed-1 feature is requested to be disabled explicitly. This is newly
|
||||
added to QEMU for TDX because TDX has fixed-1 features that are forcibly enabled
|
||||
by TDX module and VMM cannot disable them.
|
||||
|
||||
Launching a TD (TDX VM)
|
||||
-----------------------
|
||||
|
||||
To launch a TD, the necessary command line options are tdx-guest object and
|
||||
split kernel-irqchip, as below:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system_x86| \\
|
||||
-accel kvm \\
|
||||
-cpu host \\
|
||||
-object tdx-guest,id=tdx0 \\
|
||||
-machine ...,confidential-guest-support=tdx0 \\
|
||||
-bios OVMF.fd \\
|
||||
|
||||
Restrictions
|
||||
------------
|
||||
|
||||
- kernel-irqchip must be split;
|
||||
|
||||
This is set by default for TDX guest if kernel-irqchip is left on its default
|
||||
'auto' setting.
|
||||
|
||||
- No readonly support for private memory;
|
||||
|
||||
- No SMM support: SMM support requires manipulating the guest register states
|
||||
which is not allowed;
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
Bit 0 of TD attributes, is DEBUG bit, which decides if the TD runs in off-TD
|
||||
debug mode. When in off-TD debug mode, TD's VCPU state and private memory are
|
||||
accessible via given SEAMCALLs. This requires KVM to expose APIs to invoke those
|
||||
SEAMCALLs and corresonponding QEMU change.
|
||||
|
||||
It's targeted as future work.
|
||||
|
||||
TD attestation
|
||||
--------------
|
||||
|
||||
In TD guest, the attestation process is used to verify the TDX guest
|
||||
trustworthiness to other entities before provisioning secrets to the guest.
|
||||
|
||||
TD attestation is initiated first by calling TDG.MR.REPORT inside TD to get the
|
||||
REPORT. Then the REPORT data needs to be converted into a remotely verifiable
|
||||
Quote by SGX Quoting Enclave (QE).
|
||||
|
||||
It's a future work in QEMU to add support of TD attestation since it lacks
|
||||
support in current KVM.
|
||||
|
||||
Live Migration
|
||||
--------------
|
||||
|
||||
Future work.
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
- `TDX Homepage <https://www.intel.com/content/www/us/en/developer/articles/technical/intel-trust-domain-extensions.html>`__
|
||||
|
||||
- `SGX QE <https://github.com/intel/SGXDataCenterAttestationPrimitives/tree/master/QuoteGeneration>`__
|
||||
@@ -31,6 +31,7 @@ Architectural features
|
||||
i386/kvm-pv
|
||||
i386/sgx
|
||||
i386/amd-memory-encryption
|
||||
i386/tdx
|
||||
|
||||
OS requirements
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
+2
-2
@@ -5,13 +5,13 @@
|
||||
#
|
||||
|
||||
# We build two versions of gdbstub, one for each mode
|
||||
libuser_ss.add(files(
|
||||
user_ss.add(files(
|
||||
'gdbstub.c',
|
||||
'syscalls.c',
|
||||
'user.c'
|
||||
))
|
||||
|
||||
libsystem_ss.add(files(
|
||||
system_ss.add(files(
|
||||
'gdbstub.c',
|
||||
'syscalls.c',
|
||||
'system.c'
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ arm_common_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_MUSICPAL', if_true: [pixman, files('musicpal.c')])
|
||||
arm_common_ss.add(when: 'CONFIG_MUSICPAL', if_true: [files('musicpal.c')])
|
||||
arm_common_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c'))
|
||||
@@ -79,7 +79,7 @@ arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c'))
|
||||
arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c'))
|
||||
|
||||
arm_common_ss.add(fdt, files('boot.c'))
|
||||
arm_common_ss.add(files('boot.c'))
|
||||
|
||||
hw_arch += {'arm': arm_ss}
|
||||
hw_common_arch += {'arm': arm_common_ss}
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c'))
|
||||
system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c'))
|
||||
system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls])
|
||||
|
||||
libsystem_ss.add(files(
|
||||
system_ss.add(files(
|
||||
'cpu-system.c',
|
||||
'fw-path-provider.c',
|
||||
'gpio.c',
|
||||
@@ -46,7 +46,7 @@ libsystem_ss.add(files(
|
||||
'vm-change-state-handler.c',
|
||||
'clock-vmstate.c',
|
||||
))
|
||||
libuser_ss.add(files(
|
||||
user_ss.add(files(
|
||||
'cpu-user.c',
|
||||
'qdev-user.c',
|
||||
))
|
||||
|
||||
@@ -10,6 +10,11 @@ config SGX
|
||||
bool
|
||||
depends on KVM
|
||||
|
||||
config TDX
|
||||
bool
|
||||
select X86_FW_OVMF
|
||||
depends on KVM
|
||||
|
||||
config PC
|
||||
bool
|
||||
imply APPLESMC
|
||||
@@ -26,6 +31,7 @@ config PC
|
||||
imply QXL
|
||||
imply SEV
|
||||
imply SGX
|
||||
imply TDX
|
||||
imply TEST_DEVICES
|
||||
imply TPM_CRB
|
||||
imply TPM_TIS_ISA
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "system/hw_accel.h"
|
||||
#include "system/kvm.h"
|
||||
#include "kvm/kvm_i386.h"
|
||||
#include "kvm/tdx.h"
|
||||
|
||||
static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic,
|
||||
int reg_id, uint32_t val)
|
||||
@@ -141,6 +142,10 @@ static void kvm_apic_put(CPUState *cs, run_on_cpu_data data)
|
||||
struct kvm_lapic_state kapic;
|
||||
int ret;
|
||||
|
||||
if (is_tdx_vm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_put_apicbase(s->cpu, s->apicbase);
|
||||
kvm_put_apic_state(s, &kapic);
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ i386_ss.add(when: 'CONFIG_PC', if_true: files(
|
||||
'port92.c'))
|
||||
i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'),
|
||||
if_false: files('pc_sysfw_ovmf-stubs.c'))
|
||||
i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c'))
|
||||
|
||||
subdir('kvm')
|
||||
subdir('xen')
|
||||
|
||||
+16
-13
@@ -44,6 +44,7 @@
|
||||
#include "system/xen.h"
|
||||
#include "system/reset.h"
|
||||
#include "kvm/kvm_i386.h"
|
||||
#include "kvm/tdx.h"
|
||||
#include "hw/xen/xen.h"
|
||||
#include "qobject/qlist.h"
|
||||
#include "qemu/error-report.h"
|
||||
@@ -976,21 +977,23 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
/* Initialize PC system firmware */
|
||||
pc_system_firmware_init(pcms, rom_memory);
|
||||
|
||||
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
|
||||
if (machine_require_guest_memfd(machine)) {
|
||||
memory_region_init_ram_guest_memfd(option_rom_mr, NULL, "pc.rom",
|
||||
PC_ROM_SIZE, &error_fatal);
|
||||
} else {
|
||||
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE,
|
||||
&error_fatal);
|
||||
if (pcmc->pci_enabled) {
|
||||
memory_region_set_readonly(option_rom_mr, true);
|
||||
if (!is_tdx_vm()) {
|
||||
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
|
||||
if (machine_require_guest_memfd(machine)) {
|
||||
memory_region_init_ram_guest_memfd(option_rom_mr, NULL, "pc.rom",
|
||||
PC_ROM_SIZE, &error_fatal);
|
||||
} else {
|
||||
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE,
|
||||
&error_fatal);
|
||||
if (pcmc->pci_enabled) {
|
||||
memory_region_set_readonly(option_rom_mr, true);
|
||||
}
|
||||
}
|
||||
memory_region_add_subregion_overlap(rom_memory,
|
||||
PC_ROM_MIN_VGA,
|
||||
option_rom_mr,
|
||||
1);
|
||||
}
|
||||
memory_region_add_subregion_overlap(rom_memory,
|
||||
PC_ROM_MIN_VGA,
|
||||
option_rom_mr,
|
||||
1);
|
||||
|
||||
fw_cfg = fw_cfg_arch_create(machine,
|
||||
x86ms->boot_cpus, x86ms->apic_id_limit);
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "hw/block/flash.h"
|
||||
#include "system/kvm.h"
|
||||
#include "target/i386/sev.h"
|
||||
#include "kvm/tdx.h"
|
||||
|
||||
#define FLASH_SECTOR_SIZE 4096
|
||||
|
||||
@@ -280,5 +281,11 @@ void x86_firmware_configure(hwaddr gpa, void *ptr, int size)
|
||||
}
|
||||
|
||||
sev_encrypt_flash(gpa, ptr, size, &error_fatal);
|
||||
} else if (is_tdx_vm()) {
|
||||
ret = tdx_parse_tdvf(ptr, size);
|
||||
if (ret) {
|
||||
error_report("failed to parse TDVF for TDX VM");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Intel Corporation
|
||||
* Author: Isaku Yamahata <isaku.yamahata at gmail.com>
|
||||
* <isaku.yamahata at intel.com>
|
||||
* Xiaoyao Li <xiaoyao.li@intel.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "standard-headers/uefi/uefi.h"
|
||||
#include "hw/pci/pcie_host.h"
|
||||
#include "tdvf-hob.h"
|
||||
|
||||
typedef struct TdvfHob {
|
||||
hwaddr hob_addr;
|
||||
void *ptr;
|
||||
int size;
|
||||
|
||||
/* working area */
|
||||
void *current;
|
||||
void *end;
|
||||
} TdvfHob;
|
||||
|
||||
static uint64_t tdvf_current_guest_addr(const TdvfHob *hob)
|
||||
{
|
||||
return hob->hob_addr + (hob->current - hob->ptr);
|
||||
}
|
||||
|
||||
static void tdvf_align(TdvfHob *hob, size_t align)
|
||||
{
|
||||
hob->current = QEMU_ALIGN_PTR_UP(hob->current, align);
|
||||
}
|
||||
|
||||
static void *tdvf_get_area(TdvfHob *hob, uint64_t size)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (hob->current + size > hob->end) {
|
||||
error_report("TD_HOB overrun, size = 0x%" PRIx64, size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = hob->current;
|
||||
hob->current += size;
|
||||
tdvf_align(hob, 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tdvf_hob_add_memory_resources(TdxGuest *tdx, TdvfHob *hob)
|
||||
{
|
||||
EFI_HOB_RESOURCE_DESCRIPTOR *region;
|
||||
EFI_RESOURCE_ATTRIBUTE_TYPE attr;
|
||||
EFI_RESOURCE_TYPE resource_type;
|
||||
|
||||
TdxRamEntry *e;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tdx->nr_ram_entries; i++) {
|
||||
e = &tdx->ram_entries[i];
|
||||
|
||||
if (e->type == TDX_RAM_UNACCEPTED) {
|
||||
resource_type = EFI_RESOURCE_MEMORY_UNACCEPTED;
|
||||
attr = EFI_RESOURCE_ATTRIBUTE_TDVF_UNACCEPTED;
|
||||
} else if (e->type == TDX_RAM_ADDED) {
|
||||
resource_type = EFI_RESOURCE_SYSTEM_MEMORY;
|
||||
attr = EFI_RESOURCE_ATTRIBUTE_TDVF_PRIVATE;
|
||||
} else {
|
||||
error_report("unknown TDX_RAM_ENTRY type %d", e->type);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
region = tdvf_get_area(hob, sizeof(*region));
|
||||
*region = (EFI_HOB_RESOURCE_DESCRIPTOR) {
|
||||
.Header = {
|
||||
.HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR,
|
||||
.HobLength = cpu_to_le16(sizeof(*region)),
|
||||
.Reserved = cpu_to_le32(0),
|
||||
},
|
||||
.Owner = EFI_HOB_OWNER_ZERO,
|
||||
.ResourceType = cpu_to_le32(resource_type),
|
||||
.ResourceAttribute = cpu_to_le32(attr),
|
||||
.PhysicalStart = cpu_to_le64(e->address),
|
||||
.ResourceLength = cpu_to_le64(e->length),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void tdvf_hob_create(TdxGuest *tdx, TdxFirmwareEntry *td_hob)
|
||||
{
|
||||
TdvfHob hob = {
|
||||
.hob_addr = td_hob->address,
|
||||
.size = td_hob->size,
|
||||
.ptr = td_hob->mem_ptr,
|
||||
|
||||
.current = td_hob->mem_ptr,
|
||||
.end = td_hob->mem_ptr + td_hob->size,
|
||||
};
|
||||
|
||||
EFI_HOB_GENERIC_HEADER *last_hob;
|
||||
EFI_HOB_HANDOFF_INFO_TABLE *hit;
|
||||
|
||||
/* Note, Efi{Free}Memory{Bottom,Top} are ignored, leave 'em zeroed. */
|
||||
hit = tdvf_get_area(&hob, sizeof(*hit));
|
||||
*hit = (EFI_HOB_HANDOFF_INFO_TABLE) {
|
||||
.Header = {
|
||||
.HobType = EFI_HOB_TYPE_HANDOFF,
|
||||
.HobLength = cpu_to_le16(sizeof(*hit)),
|
||||
.Reserved = cpu_to_le32(0),
|
||||
},
|
||||
.Version = cpu_to_le32(EFI_HOB_HANDOFF_TABLE_VERSION),
|
||||
.BootMode = cpu_to_le32(0),
|
||||
.EfiMemoryTop = cpu_to_le64(0),
|
||||
.EfiMemoryBottom = cpu_to_le64(0),
|
||||
.EfiFreeMemoryTop = cpu_to_le64(0),
|
||||
.EfiFreeMemoryBottom = cpu_to_le64(0),
|
||||
.EfiEndOfHobList = cpu_to_le64(0), /* initialized later */
|
||||
};
|
||||
|
||||
tdvf_hob_add_memory_resources(tdx, &hob);
|
||||
|
||||
last_hob = tdvf_get_area(&hob, sizeof(*last_hob));
|
||||
*last_hob = (EFI_HOB_GENERIC_HEADER) {
|
||||
.HobType = EFI_HOB_TYPE_END_OF_HOB_LIST,
|
||||
.HobLength = cpu_to_le16(sizeof(*last_hob)),
|
||||
.Reserved = cpu_to_le32(0),
|
||||
};
|
||||
hit->EfiEndOfHobList = tdvf_current_guest_addr(&hob);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef HW_I386_TD_HOB_H
|
||||
#define HW_I386_TD_HOB_H
|
||||
|
||||
#include "hw/i386/tdvf.h"
|
||||
#include "target/i386/kvm/tdx.h"
|
||||
|
||||
void tdvf_hob_create(TdxGuest *tdx, TdxFirmwareEntry *td_hob);
|
||||
|
||||
#define EFI_RESOURCE_ATTRIBUTE_TDVF_PRIVATE \
|
||||
(EFI_RESOURCE_ATTRIBUTE_PRESENT | \
|
||||
EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \
|
||||
EFI_RESOURCE_ATTRIBUTE_TESTED)
|
||||
|
||||
#define EFI_RESOURCE_ATTRIBUTE_TDVF_UNACCEPTED \
|
||||
(EFI_RESOURCE_ATTRIBUTE_PRESENT | \
|
||||
EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \
|
||||
EFI_RESOURCE_ATTRIBUTE_TESTED)
|
||||
|
||||
#define EFI_RESOURCE_ATTRIBUTE_TDVF_MMIO \
|
||||
(EFI_RESOURCE_ATTRIBUTE_PRESENT | \
|
||||
EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \
|
||||
EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE)
|
||||
|
||||
#endif
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Intel Corporation
|
||||
* Author: Isaku Yamahata <isaku.yamahata at gmail.com>
|
||||
* <isaku.yamahata at intel.com>
|
||||
* Xiaoyao Li <xiaoyao.li@intel.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/tdvf.h"
|
||||
#include "system/kvm.h"
|
||||
|
||||
#define TDX_METADATA_OFFSET_GUID "e47a6535-984a-4798-865e-4685a7bf8ec2"
|
||||
#define TDX_METADATA_VERSION 1
|
||||
#define TDVF_SIGNATURE 0x46564454 /* TDVF as little endian */
|
||||
#define TDVF_ALIGNMENT 4096
|
||||
|
||||
/*
|
||||
* the raw structs read from TDVF keeps the name convention in
|
||||
* TDVF Design Guide spec.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t DataOffset;
|
||||
uint32_t RawDataSize;
|
||||
uint64_t MemoryAddress;
|
||||
uint64_t MemoryDataSize;
|
||||
uint32_t Type;
|
||||
uint32_t Attributes;
|
||||
} TdvfSectionEntry;
|
||||
|
||||
typedef struct {
|
||||
uint32_t Signature;
|
||||
uint32_t Length;
|
||||
uint32_t Version;
|
||||
uint32_t NumberOfSectionEntries;
|
||||
TdvfSectionEntry SectionEntries[];
|
||||
} TdvfMetadata;
|
||||
|
||||
struct tdx_metadata_offset {
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
static TdvfMetadata *tdvf_get_metadata(void *flash_ptr, int size)
|
||||
{
|
||||
TdvfMetadata *metadata;
|
||||
uint32_t offset = 0;
|
||||
uint8_t *data;
|
||||
|
||||
if ((uint32_t) size != size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pc_system_ovmf_table_find(TDX_METADATA_OFFSET_GUID, &data, NULL)) {
|
||||
offset = size - le32_to_cpu(((struct tdx_metadata_offset *)data)->offset);
|
||||
|
||||
if (offset + sizeof(*metadata) > size) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
error_report("Cannot find TDX_METADATA_OFFSET_GUID");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
metadata = flash_ptr + offset;
|
||||
|
||||
/* Finally, verify the signature to determine if this is a TDVF image. */
|
||||
metadata->Signature = le32_to_cpu(metadata->Signature);
|
||||
if (metadata->Signature != TDVF_SIGNATURE) {
|
||||
error_report("Invalid TDVF signature in metadata!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Sanity check that the TDVF doesn't overlap its own metadata. */
|
||||
metadata->Length = le32_to_cpu(metadata->Length);
|
||||
if (offset + metadata->Length > size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Only version 1 is supported/defined. */
|
||||
metadata->Version = le32_to_cpu(metadata->Version);
|
||||
if (metadata->Version != TDX_METADATA_VERSION) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src,
|
||||
TdxFirmwareEntry *entry)
|
||||
{
|
||||
entry->data_offset = le32_to_cpu(src->DataOffset);
|
||||
entry->data_len = le32_to_cpu(src->RawDataSize);
|
||||
entry->address = le64_to_cpu(src->MemoryAddress);
|
||||
entry->size = le64_to_cpu(src->MemoryDataSize);
|
||||
entry->type = le32_to_cpu(src->Type);
|
||||
entry->attributes = le32_to_cpu(src->Attributes);
|
||||
|
||||
/* sanity check */
|
||||
if (entry->size < entry->data_len) {
|
||||
error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%lx",
|
||||
entry->data_len, entry->size);
|
||||
return -1;
|
||||
}
|
||||
if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) {
|
||||
error_report("MemoryAddress 0x%lx not page aligned", entry->address);
|
||||
return -1;
|
||||
}
|
||||
if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) {
|
||||
error_report("MemoryDataSize 0x%lx not page aligned", entry->size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (entry->type) {
|
||||
case TDVF_SECTION_TYPE_BFV:
|
||||
case TDVF_SECTION_TYPE_CFV:
|
||||
/* The sections that must be copied from firmware image to TD memory */
|
||||
if (entry->data_len == 0) {
|
||||
error_report("%d section with RawDataSize == 0", entry->type);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case TDVF_SECTION_TYPE_TD_HOB:
|
||||
case TDVF_SECTION_TYPE_TEMP_MEM:
|
||||
/* The sections that no need to be copied from firmware image */
|
||||
if (entry->data_len != 0) {
|
||||
error_report("%d section with RawDataSize 0x%x != 0",
|
||||
entry->type, entry->data_len);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error_report("TDVF contains unsupported section type %d", entry->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size)
|
||||
{
|
||||
g_autofree TdvfSectionEntry *sections = NULL;
|
||||
TdvfMetadata *metadata;
|
||||
ssize_t entries_size;
|
||||
int i;
|
||||
|
||||
metadata = tdvf_get_metadata(flash_ptr, size);
|
||||
if (!metadata) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* load and parse metadata entries */
|
||||
fw->nr_entries = le32_to_cpu(metadata->NumberOfSectionEntries);
|
||||
if (fw->nr_entries < 2) {
|
||||
error_report("Invalid number of fw entries (%u) in TDVF Metadata",
|
||||
fw->nr_entries);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
entries_size = fw->nr_entries * sizeof(TdvfSectionEntry);
|
||||
if (metadata->Length != sizeof(*metadata) + entries_size) {
|
||||
error_report("TDVF metadata len (0x%x) mismatch, expected (0x%x)",
|
||||
metadata->Length,
|
||||
(uint32_t)(sizeof(*metadata) + entries_size));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fw->entries = g_new(TdxFirmwareEntry, fw->nr_entries);
|
||||
sections = g_new(TdvfSectionEntry, fw->nr_entries);
|
||||
|
||||
memcpy(sections, (void *)metadata + sizeof(*metadata), entries_size);
|
||||
|
||||
for (i = 0; i < fw->nr_entries; i++) {
|
||||
if (tdvf_parse_and_check_section_entry(§ions[i], &fw->entries[i])) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
fw->mem_ptr = flash_ptr;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
fw->entries = 0;
|
||||
g_free(fw->entries);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "standard-headers/asm-x86/bootparam.h"
|
||||
#include CONFIG_DEVICES
|
||||
#include "kvm/kvm_i386.h"
|
||||
#include "kvm/tdx.h"
|
||||
|
||||
#ifdef CONFIG_XEN_EMU
|
||||
#include "hw/xen/xen.h"
|
||||
@@ -1035,11 +1036,14 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware,
|
||||
if (machine_require_guest_memfd(MACHINE(x86ms))) {
|
||||
memory_region_init_ram_guest_memfd(&x86ms->bios, NULL, "pc.bios",
|
||||
bios_size, &error_fatal);
|
||||
if (is_tdx_vm()) {
|
||||
tdx_set_tdvf_region(&x86ms->bios);
|
||||
}
|
||||
} else {
|
||||
memory_region_init_ram(&x86ms->bios, NULL, "pc.bios",
|
||||
bios_size, &error_fatal);
|
||||
}
|
||||
if (sev_enabled()) {
|
||||
if (sev_enabled() || is_tdx_vm()) {
|
||||
/*
|
||||
* The concept of a "reset" simply doesn't exist for
|
||||
* confidential computing guests, we have to destroy and
|
||||
|
||||
+3
-11
@@ -36,15 +36,7 @@ static inline G_GNUC_PRINTF(1, 2) int DPRINTF(const char *fmt, ...)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define __le16 uint16_t
|
||||
#define __le32 uint32_t
|
||||
#define __le64 uint64_t
|
||||
|
||||
#define __be16 uint16_t
|
||||
#define __be32 uint32_t
|
||||
#define __be64 uint64_t
|
||||
|
||||
static inline bool ipv4_addr_is_multicast(__be32 addr)
|
||||
static inline bool ipv4_addr_is_multicast(uint32_t addr)
|
||||
{
|
||||
return (addr & htonl(0xf0000000)) == htonl(0xe0000000);
|
||||
}
|
||||
@@ -52,8 +44,8 @@ static inline bool ipv4_addr_is_multicast(__be32 addr)
|
||||
typedef struct ipv6_addr {
|
||||
union {
|
||||
uint8_t addr8[16];
|
||||
__be16 addr16[8];
|
||||
__be32 addr32[4];
|
||||
uint16_t addr16[8];
|
||||
uint32_t addr32[4];
|
||||
};
|
||||
} Ipv6Addr;
|
||||
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
#ifndef ROCKER_HW_H
|
||||
#define ROCKER_HW_H
|
||||
|
||||
#define __le16 uint16_t
|
||||
#define __le32 uint32_t
|
||||
#define __le64 uint64_t
|
||||
|
||||
/*
|
||||
* Return codes
|
||||
*/
|
||||
@@ -124,12 +120,12 @@ enum {
|
||||
*/
|
||||
|
||||
typedef struct rocker_desc {
|
||||
__le64 buf_addr;
|
||||
uint64_t buf_addr;
|
||||
uint64_t cookie;
|
||||
__le16 buf_size;
|
||||
__le16 tlv_size;
|
||||
__le16 rsvd[5]; /* pad to 32 bytes */
|
||||
__le16 comp_err;
|
||||
uint16_t buf_size;
|
||||
uint16_t tlv_size;
|
||||
uint16_t rsvd[5]; /* pad to 32 bytes */
|
||||
uint16_t comp_err;
|
||||
} __attribute__((packed, aligned(8))) RockerDesc;
|
||||
|
||||
/*
|
||||
@@ -137,9 +133,9 @@ typedef struct rocker_desc {
|
||||
*/
|
||||
|
||||
typedef struct rocker_tlv {
|
||||
__le32 type;
|
||||
__le16 len;
|
||||
__le16 rsvd;
|
||||
uint32_t type;
|
||||
uint16_t len;
|
||||
uint16_t rsvd;
|
||||
} __attribute__((packed, aligned(8))) RockerTlv;
|
||||
|
||||
/* cmd msg */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user