* 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-----

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:
Stefan Hajnoczi 2025-05-30 11:41:07 -04:00
commit 98721058d6
66 changed files with 3231 additions and 346 deletions

View file

@ -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);
}

View file

@ -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',

View file

@ -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

View file

@ -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`)

161
docs/system/i386/tdx.rst Normal file
View file

@ -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>`__

View file

@ -31,6 +31,7 @@ Architectural features
i386/kvm-pv
i386/sgx
i386/amd-memory-encryption
i386/tdx
OS requirements
~~~~~~~~~~~~~~~

View file

@ -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'

View file

@ -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}

View file

@ -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',
))

View file

@ -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

View file

@ -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);

View file

@ -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')

View file

@ -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);

View file

@ -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);
}
}
}

130
hw/i386/tdvf-hob.c Normal file
View file

@ -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);
}

26
hw/i386/tdvf-hob.h Normal file
View file

@ -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
hw/i386/tdvf.c Normal file
View file

@ -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(&sections[i], &fw->entries[i])) {
goto err;
}
}
fw->mem_ptr = flash_ptr;
return 0;
err:
fw->entries = 0;
g_free(fw->entries);
return -EINVAL;
}

View file

@ -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

View file

@ -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;

View file

@ -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 */

View file

@ -52,10 +52,10 @@ typedef struct of_dpa_flow_key {
uint32_t tunnel_id; /* overlay tunnel id */
uint32_t tbl_id; /* table id */
struct {
__be16 vlan_id; /* 0 if no VLAN */
uint16_t vlan_id; /* 0 if no VLAN */
MACAddr src; /* ethernet source address */
MACAddr dst; /* ethernet destination address */
__be16 type; /* ethernet frame type */
uint16_t type; /* ethernet frame type */
} eth;
struct {
uint8_t proto; /* IP protocol or ARP opcode */
@ -66,14 +66,14 @@ typedef struct of_dpa_flow_key {
union {
struct {
struct {
__be32 src; /* IP source address */
__be32 dst; /* IP destination address */
uint32_t src; /* IP source address */
uint32_t dst; /* IP destination address */
} addr;
union {
struct {
__be16 src; /* TCP/UDP/SCTP source port */
__be16 dst; /* TCP/UDP/SCTP destination port */
__be16 flags; /* TCP flags */
uint16_t src; /* TCP/UDP/SCTP source port */
uint16_t dst; /* TCP/UDP/SCTP destination port */
uint16_t flags; /* TCP flags */
} tp;
struct {
MACAddr sha; /* ARP source hardware address */
@ -86,11 +86,11 @@ typedef struct of_dpa_flow_key {
Ipv6Addr src; /* IPv6 source address */
Ipv6Addr dst; /* IPv6 destination address */
} addr;
__be32 label; /* IPv6 flow label */
uint32_t label; /* IPv6 flow label */
struct {
__be16 src; /* TCP/UDP/SCTP source port */
__be16 dst; /* TCP/UDP/SCTP destination port */
__be16 flags; /* TCP flags */
uint16_t src; /* TCP/UDP/SCTP source port */
uint16_t dst; /* TCP/UDP/SCTP destination port */
uint16_t flags; /* TCP flags */
} tp;
struct {
Ipv6Addr target; /* ND target address */
@ -112,13 +112,13 @@ typedef struct of_dpa_flow_action {
struct {
uint32_t group_id;
uint32_t tun_log_lport;
__be16 vlan_id;
uint16_t vlan_id;
} write;
struct {
__be16 new_vlan_id;
uint16_t new_vlan_id;
uint32_t out_pport;
uint8_t copy_to_cpu;
__be16 vlan_id;
uint16_t vlan_id;
} apply;
} OfDpaFlowAction;
@ -143,7 +143,7 @@ typedef struct of_dpa_flow {
typedef struct of_dpa_flow_pkt_fields {
uint32_t tunnel_id;
struct eth_header *ethhdr;
__be16 *h_proto;
uint16_t *h_proto;
struct vlan_header *vlanhdr;
struct ip_header *ipv4hdr;
struct ip6_header *ipv6hdr;
@ -180,7 +180,7 @@ typedef struct of_dpa_group {
uint32_t group_id;
MACAddr src_mac;
MACAddr dst_mac;
__be16 vlan_id;
uint16_t vlan_id;
} l2_rewrite;
struct {
uint16_t group_count;
@ -190,13 +190,13 @@ typedef struct of_dpa_group {
uint32_t group_id;
MACAddr src_mac;
MACAddr dst_mac;
__be16 vlan_id;
uint16_t vlan_id;
uint8_t ttl_check;
} l3_unicast;
};
} OfDpaGroup;
static int of_dpa_mask2prefix(__be32 mask)
static int of_dpa_mask2prefix(uint32_t mask)
{
int i;
int count = 32;
@ -451,7 +451,7 @@ static void of_dpa_flow_pkt_parse(OfDpaFlowContext *fc,
fc->iovcnt = iovcnt + 2;
}
static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, __be16 vlan_id)
static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, uint16_t vlan_id)
{
OfDpaFlowPktFields *fields = &fc->fields;
uint16_t h_proto = fields->ethhdr->h_proto;
@ -486,7 +486,7 @@ static void of_dpa_flow_pkt_strip_vlan(OfDpaFlowContext *fc)
static void of_dpa_flow_pkt_hdr_rewrite(OfDpaFlowContext *fc,
uint8_t *src_mac, uint8_t *dst_mac,
__be16 vlan_id)
uint16_t vlan_id)
{
OfDpaFlowPktFields *fields = &fc->fields;

View file

@ -426,30 +426,11 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
uint64_t cur_tick;
trace_hpet_ram_read(addr);
addr &= ~4;
/*address range of all TN regs*/
if (addr >= 0x100 && addr <= 0x3ff) {
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
if (timer_id > s->num_timers) {
trace_hpet_timer_id_out_of_range(timer_id);
return 0;
}
switch (addr & 0x18) {
case HPET_TN_CFG: // including interrupt capabilities
return timer->config >> shift;
case HPET_TN_CMP: // comparator register
return timer->cmp >> shift;
case HPET_TN_ROUTE:
return timer->fsb >> shift;
default:
trace_hpet_ram_read_invalid();
break;
}
} else {
switch (addr & ~4) {
/*address range of all global regs*/
if (addr <= 0xff) {
switch (addr) {
case HPET_ID: // including HPET_PERIOD
return s->capability >> shift;
case HPET_CFG:
@ -468,6 +449,26 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr,
trace_hpet_ram_read_invalid();
break;
}
} else {
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
if (timer_id > s->num_timers) {
trace_hpet_timer_id_out_of_range(timer_id);
return 0;
}
switch (addr & 0x1f) {
case HPET_TN_CFG: // including interrupt capabilities
return timer->config >> shift;
case HPET_TN_CMP: // comparator register
return timer->cmp >> shift;
case HPET_TN_ROUTE:
return timer->fsb >> shift;
default:
trace_hpet_ram_read_invalid();
break;
}
}
return 0;
}
@ -482,9 +483,67 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
uint64_t old_val, new_val, cleared;
trace_hpet_ram_write(addr, value);
addr &= ~4;
/*address range of all TN regs*/
if (addr >= 0x100 && addr <= 0x3ff) {
/*address range of all global regs*/
if (addr <= 0xff) {
switch (addr) {
case HPET_ID:
return;
case HPET_CFG:
old_val = s->config;
new_val = deposit64(old_val, shift, len, value);
new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
s->config = new_val;
if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Enable main counter and interrupt generation. */
s->hpet_offset =
ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
for (i = 0; i < s->num_timers; i++) {
if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) {
update_irq(&s->timer[i], 1);
}
hpet_set_timer(&s->timer[i]);
}
} else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Halt main counter and disable interrupt generation. */
s->hpet_counter = hpet_get_ticks(s);
for (i = 0; i < s->num_timers; i++) {
hpet_del_timer(&s->timer[i]);
}
}
/* i8254 and RTC output pins are disabled
* when HPET is in legacy mode */
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
qemu_set_irq(s->pit_enabled, 0);
qemu_irq_lower(s->irqs[0]);
qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
qemu_irq_lower(s->irqs[0]);
qemu_set_irq(s->pit_enabled, 1);
qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
}
break;
case HPET_STATUS:
new_val = value << shift;
cleared = new_val & s->isr;
for (i = 0; i < s->num_timers; i++) {
if (cleared & (1 << i)) {
update_irq(&s->timer[i], 0);
}
}
break;
case HPET_COUNTER:
if (hpet_enabled(s)) {
trace_hpet_ram_write_counter_write_while_enabled();
}
s->hpet_counter = deposit64(s->hpet_counter, shift, len, value);
break;
default:
trace_hpet_ram_write_invalid();
break;
}
} else {
uint8_t timer_id = (addr - 0x100) / 0x20;
HPETTimer *timer = &s->timer[timer_id];
@ -550,63 +609,6 @@ static void hpet_ram_write(void *opaque, hwaddr addr,
break;
}
return;
} else {
switch (addr & ~4) {
case HPET_ID:
return;
case HPET_CFG:
old_val = s->config;
new_val = deposit64(old_val, shift, len, value);
new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK);
s->config = new_val;
if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Enable main counter and interrupt generation. */
s->hpet_offset =
ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
for (i = 0; i < s->num_timers; i++) {
if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) {
update_irq(&s->timer[i], 1);
}
hpet_set_timer(&s->timer[i]);
}
} else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) {
/* Halt main counter and disable interrupt generation. */
s->hpet_counter = hpet_get_ticks(s);
for (i = 0; i < s->num_timers; i++) {
hpet_del_timer(&s->timer[i]);
}
}
/* i8254 and RTC output pins are disabled
* when HPET is in legacy mode */
if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
qemu_set_irq(s->pit_enabled, 0);
qemu_irq_lower(s->irqs[0]);
qemu_irq_lower(s->irqs[RTC_ISA_IRQ]);
} else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) {
qemu_irq_lower(s->irqs[0]);
qemu_set_irq(s->pit_enabled, 1);
qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level);
}
break;
case HPET_STATUS:
new_val = value << shift;
cleared = new_val & s->isr;
for (i = 0; i < s->num_timers; i++) {
if (cleared & (1 << i)) {
update_irq(&s->timer[i], 0);
}
}
break;
case HPET_COUNTER:
if (hpet_enabled(s)) {
trace_hpet_ram_write_counter_write_while_enabled();
}
s->hpet_counter = deposit64(s->hpet_counter, shift, len, value);
break;
default:
trace_hpet_ram_write_invalid();
break;
}
}
}

45
include/hw/i386/tdvf.h Normal file
View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2025 Intel Corporation
* Author: Isaku Yamahata <isaku.yamahata at gmail.com>
* <isaku.yamahata at intel.com>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef HW_I386_TDVF_H
#define HW_I386_TDVF_H
#include "qemu/osdep.h"
#define TDVF_SECTION_TYPE_BFV 0
#define TDVF_SECTION_TYPE_CFV 1
#define TDVF_SECTION_TYPE_TD_HOB 2
#define TDVF_SECTION_TYPE_TEMP_MEM 3
#define TDVF_SECTION_ATTRIBUTES_MR_EXTEND (1U << 0)
#define TDVF_SECTION_ATTRIBUTES_PAGE_AUG (1U << 1)
typedef struct TdxFirmwareEntry {
uint32_t data_offset;
uint32_t data_len;
uint64_t address;
uint64_t size;
uint32_t type;
uint32_t attributes;
void *mem_ptr;
} TdxFirmwareEntry;
typedef struct TdxFirmware {
void *mem_ptr;
uint32_t nr_entries;
TdxFirmwareEntry *entries;
} TdxFirmware;
#define for_each_tdx_fw_entry(fw, e) \
for (e = (fw)->entries; e != (fw)->entries + (fw)->nr_entries; e++)
int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size);
#endif /* HW_I386_TDVF_H */

View file

@ -0,0 +1,187 @@
/*
* 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
*/
#ifndef HW_I386_UEFI_H
#define HW_I386_UEFI_H
/***************************************************************************/
/*
* basic EFI definitions
* supplemented with UEFI Specification Version 2.8 (Errata A)
* released February 2020
*/
/* UEFI integer is little endian */
typedef struct {
uint32_t Data1;
uint16_t Data2;
uint16_t Data3;
uint8_t Data4[8];
} EFI_GUID;
typedef enum {
EfiReservedMemoryType,
EfiLoaderCode,
EfiLoaderData,
EfiBootServicesCode,
EfiBootServicesData,
EfiRuntimeServicesCode,
EfiRuntimeServicesData,
EfiConventionalMemory,
EfiUnusableMemory,
EfiACPIReclaimMemory,
EfiACPIMemoryNVS,
EfiMemoryMappedIO,
EfiMemoryMappedIOPortSpace,
EfiPalCode,
EfiPersistentMemory,
EfiUnacceptedMemoryType,
EfiMaxMemoryType
} EFI_MEMORY_TYPE;
#define EFI_HOB_HANDOFF_TABLE_VERSION 0x0009
#define EFI_HOB_TYPE_HANDOFF 0x0001
#define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002
#define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003
#define EFI_HOB_TYPE_GUID_EXTENSION 0x0004
#define EFI_HOB_TYPE_FV 0x0005
#define EFI_HOB_TYPE_CPU 0x0006
#define EFI_HOB_TYPE_MEMORY_POOL 0x0007
#define EFI_HOB_TYPE_FV2 0x0009
#define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A
#define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B
#define EFI_HOB_TYPE_FV3 0x000C
#define EFI_HOB_TYPE_UNUSED 0xFFFE
#define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF
typedef struct {
uint16_t HobType;
uint16_t HobLength;
uint32_t Reserved;
} EFI_HOB_GENERIC_HEADER;
typedef uint64_t EFI_PHYSICAL_ADDRESS;
typedef uint32_t EFI_BOOT_MODE;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
uint32_t Version;
EFI_BOOT_MODE BootMode;
EFI_PHYSICAL_ADDRESS EfiMemoryTop;
EFI_PHYSICAL_ADDRESS EfiMemoryBottom;
EFI_PHYSICAL_ADDRESS EfiFreeMemoryTop;
EFI_PHYSICAL_ADDRESS EfiFreeMemoryBottom;
EFI_PHYSICAL_ADDRESS EfiEndOfHobList;
} EFI_HOB_HANDOFF_INFO_TABLE;
#define EFI_RESOURCE_SYSTEM_MEMORY 0x00000000
#define EFI_RESOURCE_MEMORY_MAPPED_IO 0x00000001
#define EFI_RESOURCE_IO 0x00000002
#define EFI_RESOURCE_FIRMWARE_DEVICE 0x00000003
#define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004
#define EFI_RESOURCE_MEMORY_RESERVED 0x00000005
#define EFI_RESOURCE_IO_RESERVED 0x00000006
#define EFI_RESOURCE_MEMORY_UNACCEPTED 0x00000007
#define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000008
#define EFI_RESOURCE_ATTRIBUTE_PRESENT 0x00000001
#define EFI_RESOURCE_ATTRIBUTE_INITIALIZED 0x00000002
#define EFI_RESOURCE_ATTRIBUTE_TESTED 0x00000004
#define EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC 0x00000008
#define EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC 0x00000010
#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 0x00000020
#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 0x00000040
#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED 0x00000080
#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED 0x00000100
#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED 0x00000200
#define EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE 0x00000400
#define EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE 0x00000800
#define EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE 0x00001000
#define EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE 0x00002000
#define EFI_RESOURCE_ATTRIBUTE_16_BIT_IO 0x00004000
#define EFI_RESOURCE_ATTRIBUTE_32_BIT_IO 0x00008000
#define EFI_RESOURCE_ATTRIBUTE_64_BIT_IO 0x00010000
#define EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED 0x00020000
#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED 0x00040000
#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00080000
#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE 0x00100000
#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE 0x00200000
#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE 0x00400000
#define EFI_RESOURCE_ATTRIBUTE_PERSISTENT 0x00800000
#define EFI_RESOURCE_ATTRIBUTE_PERSISTABLE 0x01000000
#define EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE 0x02000000
typedef uint32_t EFI_RESOURCE_TYPE;
typedef uint32_t EFI_RESOURCE_ATTRIBUTE_TYPE;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_GUID Owner;
EFI_RESOURCE_TYPE ResourceType;
EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute;
EFI_PHYSICAL_ADDRESS PhysicalStart;
uint64_t ResourceLength;
} EFI_HOB_RESOURCE_DESCRIPTOR;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_GUID Name;
/* guid specific data follows */
} EFI_HOB_GUID_TYPE;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_PHYSICAL_ADDRESS BaseAddress;
uint64_t Length;
} EFI_HOB_FIRMWARE_VOLUME;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_PHYSICAL_ADDRESS BaseAddress;
uint64_t Length;
EFI_GUID FvName;
EFI_GUID FileName;
} EFI_HOB_FIRMWARE_VOLUME2;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_PHYSICAL_ADDRESS BaseAddress;
uint64_t Length;
uint32_t AuthenticationStatus;
bool ExtractedFv;
EFI_GUID FvName;
EFI_GUID FileName;
} EFI_HOB_FIRMWARE_VOLUME3;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
uint8_t SizeOfMemorySpace;
uint8_t SizeOfIoSpace;
uint8_t Reserved[6];
} EFI_HOB_CPU;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
} EFI_HOB_MEMORY_POOL;
typedef struct {
EFI_HOB_GENERIC_HEADER Header;
EFI_PHYSICAL_ADDRESS BaseAddress;
uint64_t Length;
} EFI_HOB_UEFI_CAPSULE;
#define EFI_HOB_OWNER_ZERO \
((EFI_GUID){ 0x00000000, 0x0000, 0x0000, \
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } })
#endif

View file

@ -376,6 +376,7 @@ int kvm_arch_get_default_type(MachineState *ms);
int kvm_arch_init(MachineState *ms, KVMState *s);
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp);
int kvm_arch_init_vcpu(CPUState *cpu);
int kvm_arch_destroy_vcpu(CPUState *cpu);

View file

@ -3272,6 +3272,7 @@ config_devices_mak_list = []
config_devices_h = {}
config_target_h = {}
config_target_mak = {}
config_base_arch_mak = {}
disassemblers = {
'alpha' : ['CONFIG_ALPHA_DIS'],
@ -3463,6 +3464,11 @@ foreach target : target_dirs
config_all_devices += config_devices
endif
config_target_mak += {target: config_target}
# build a merged config for all targets with the same TARGET_BASE_ARCH
target_base_arch = config_target['TARGET_BASE_ARCH']
config_base_arch = config_base_arch_mak.get(target_base_arch, {}) + config_target
config_base_arch_mak += {target_base_arch: config_base_arch}
endforeach
target_dirs = actual_target_dirs
@ -3718,14 +3724,12 @@ io_ss = ss.source_set()
qmp_ss = ss.source_set()
qom_ss = ss.source_set()
system_ss = ss.source_set()
libsystem_ss = ss.source_set()
specific_fuzz_ss = ss.source_set()
specific_ss = ss.source_set()
rust_devices_ss = ss.source_set()
stub_ss = ss.source_set()
trace_ss = ss.source_set()
user_ss = ss.source_set()
libuser_ss = ss.source_set()
util_ss = ss.source_set()
# accel modules
@ -4102,30 +4106,20 @@ common_ss.add(hwcore)
system_ss.add(authz, blockdev, chardev, crypto, io, qmp)
common_ss.add(qom, qemuutil)
common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss])
common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss)
libuser_ss = libuser_ss.apply({})
libuser = static_library('user',
libuser_ss.sources() + genh,
user_ss.all_sources() + genh,
c_args: ['-DCONFIG_USER_ONLY',
'-DCOMPILING_SYSTEM_VS_USER'],
dependencies: libuser_ss.dependencies(),
include_directories: common_user_inc,
dependencies: user_ss.all_dependencies(),
build_by_default: false)
libuser = declare_dependency(objects: libuser.extract_all_objects(recursive: false),
dependencies: libuser_ss.dependencies())
common_ss.add(when: 'CONFIG_USER_ONLY', if_true: libuser)
libsystem_ss = libsystem_ss.apply({})
libsystem = static_library('system',
libsystem_ss.sources() + genh,
system_ss.all_sources() + genh,
c_args: ['-DCONFIG_SOFTMMU',
'-DCOMPILING_SYSTEM_VS_USER'],
dependencies: libsystem_ss.dependencies(),
dependencies: system_ss.all_dependencies(),
build_by_default: false)
libsystem = declare_dependency(objects: libsystem.extract_all_objects(recursive: false),
dependencies: libsystem_ss.dependencies())
common_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: libsystem)
# Note that this library is never used directly (only through extract_objects)
# and is not built by default; therefore, source files not used by the build
@ -4133,65 +4127,71 @@ common_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: libsystem)
common_all = static_library('common',
build_by_default: false,
sources: common_ss.all_sources() + genh,
include_directories: common_user_inc,
implicit_include_directories: false,
dependencies: common_ss.all_dependencies())
# construct common libraries per base architecture
hw_common_arch_libs = {}
target_common_arch_libs = {}
target_common_system_arch_libs = {}
foreach target : target_dirs
foreach target_base_arch, config_base_arch : config_base_arch_mak
config_target = config_target_mak[target]
target_base_arch = config_target['TARGET_BASE_ARCH']
target_inc = [include_directories('target' / target_base_arch)]
inc = [common_user_inc + target_inc]
target_common = common_ss.apply(config_target, strict: false)
target_system = system_ss.apply(config_target, strict: false)
target_user = user_ss.apply(config_target, strict: false)
common_deps = []
system_deps = []
user_deps = []
foreach dep: target_common.dependencies()
common_deps += dep.partial_dependency(compile_args: true, includes: true)
endforeach
foreach dep: target_system.dependencies()
system_deps += dep.partial_dependency(compile_args: true, includes: true)
endforeach
foreach dep: target_user.dependencies()
user_deps += dep.partial_dependency(compile_args: true, includes: true)
endforeach
# prevent common code to access cpu compile time definition,
# but still allow access to cpu.h
target_c_args = ['-DCPU_DEFS_H']
target_system_c_args = target_c_args + ['-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU']
if target_base_arch in hw_common_arch
if target_base_arch not in hw_common_arch_libs
src = hw_common_arch[target_base_arch]
lib = static_library(
'hw_' + target_base_arch,
build_by_default: false,
sources: src.all_sources() + genh,
include_directories: inc,
c_args: target_system_c_args,
dependencies: src.all_dependencies())
hw_common_arch_libs += {target_base_arch: lib}
endif
if target_base_arch in target_common_arch
src = target_common_arch[target_base_arch]
lib = static_library(
'common_' + target_base_arch,
build_by_default: false,
sources: src.all_sources() + genh,
include_directories: inc,
c_args: target_c_args,
dependencies: src.all_dependencies() + common_deps +
system_deps + user_deps)
target_common_arch_libs += {target_base_arch: lib}
endif
if target_base_arch in target_common_arch
if target_base_arch not in target_common_arch_libs
src = target_common_arch[target_base_arch]
lib = static_library(
'target_' + target_base_arch,
build_by_default: false,
sources: src.all_sources() + genh,
include_directories: inc,
c_args: target_c_args,
dependencies: src.all_dependencies())
target_common_arch_libs += {target_base_arch: lib}
# merge hw_common_arch in target_common_system_arch
if target_base_arch in hw_common_arch
hw_src = hw_common_arch[target_base_arch]
if target_base_arch in target_common_system_arch
target_common_system_arch[target_base_arch].add_all(hw_src)
else
target_common_system_arch += {target_base_arch: hw_src}
endif
endif
if target_base_arch in target_common_system_arch
if target_base_arch not in target_common_system_arch_libs
src = target_common_system_arch[target_base_arch]
lib = static_library(
'target_system_' + target_base_arch,
build_by_default: false,
sources: src.all_sources() + genh,
include_directories: inc,
c_args: target_system_c_args,
dependencies: src.all_dependencies())
target_common_system_arch_libs += {target_base_arch: lib}
endif
src = target_common_system_arch[target_base_arch]
lib = static_library(
'system_' + target_base_arch,
build_by_default: false,
sources: src.all_sources() + genh,
include_directories: inc,
c_args: target_system_c_args,
dependencies: src.all_dependencies() + common_deps + system_deps)
target_common_system_arch_libs += {target_base_arch: lib}
endif
endforeach
@ -4368,10 +4368,14 @@ foreach target : target_dirs
objects += lib.extract_objects(src.sources())
arch_deps += src.dependencies()
endif
if target_type == 'system' and target_base_arch in hw_common_arch_libs
src = hw_common_arch[target_base_arch].apply(config_target, strict: false)
lib = hw_common_arch_libs[target_base_arch]
objects += lib.extract_objects(src.sources())
if target_type == 'system'
src = system_ss.apply(config_target, strict: false)
objects += libsystem.extract_objects(src.sources())
arch_deps += src.dependencies()
endif
if target_type == 'user'
src = user_ss.apply(config_target, strict: false)
objects += libuser.extract_objects(src.sources())
arch_deps += src.dependencies()
endif
if target_type == 'system' and target_base_arch in target_common_system_arch_libs

View file

@ -61,8 +61,8 @@ endif
user_ss.add(files('user.c', 'api-user.c'))
system_ss.add(files('system.c', 'api-system.c'))
libuser_ss.add(files('api.c', 'core.c'))
libsystem_ss.add(files('api.c', 'core.c'))
user_ss.add(files('api.c', 'core.c'))
system_ss.add(files('api.c', 'core.c'))
common_ss.add(files('loader.c'))

View file

@ -1047,6 +1047,39 @@
'*host-data': 'str',
'*vcek-disabled': 'bool' } }
##
# @TdxGuestProperties:
#
# Properties for tdx-guest objects.
#
# @attributes: The 'attributes' of a TD guest that is passed to
# KVM_TDX_INIT_VM
#
# @sept-ve-disable: toggle bit 28 of TD attributes to control disabling
# of EPT violation conversion to #VE on guest TD access of PENDING
# pages. Some guest OS (e.g., Linux TD guest) may require this to
# be set, otherwise they refuse to boot.
#
# @mrconfigid: ID for non-owner-defined configuration of the guest TD,
# e.g., run-time or OS configuration (base64 encoded SHA384 digest).
# Defaults to all zeros.
#
# @mrowner: ID for the guest TDs owner (base64 encoded SHA384 digest).
# Defaults to all zeros.
#
# @mrownerconfig: ID for owner-defined configuration of the guest TD,
# e.g., specific to the workload rather than the run-time or OS
# (base64 encoded SHA384 digest). Defaults to all zeros.
#
# Since: 10.1
##
{ 'struct': 'TdxGuestProperties',
'data': { '*attributes': 'uint64',
'*sept-ve-disable': 'bool',
'*mrconfigid': 'str',
'*mrowner': 'str',
'*mrownerconfig': 'str' } }
##
# @ThreadContextProperties:
#
@ -1132,6 +1165,7 @@
'sev-snp-guest',
'thread-context',
's390-pv-guest',
'tdx-guest',
'throttle-group',
'tls-creds-anon',
'tls-creds-psk',
@ -1204,6 +1238,7 @@
'if': 'CONFIG_SECRET_KEYRING' },
'sev-guest': 'SevGuestProperties',
'sev-snp-guest': 'SevSnpGuestProperties',
'tdx-guest': 'TdxGuestProperties',
'thread-context': 'ThreadContextProperties',
'throttle-group': 'ThrottleGroupProperties',
'tls-creds-anon': 'TlsCredsAnonProperties',

View file

@ -501,10 +501,12 @@
#
# @s390: s390 guest panic information type (Since: 2.12)
#
# @tdx: tdx guest panic information type (Since: 10.1)
#
# Since: 2.9
##
{ 'enum': 'GuestPanicInformationType',
'data': [ 'hyper-v', 's390' ] }
'data': [ 'hyper-v', 's390', 'tdx' ] }
##
# @GuestPanicInformation:
@ -519,7 +521,8 @@
'base': {'type': 'GuestPanicInformationType'},
'discriminator': 'type',
'data': {'hyper-v': 'GuestPanicInformationHyperV',
's390': 'GuestPanicInformationS390'}}
's390': 'GuestPanicInformationS390',
'tdx' : 'GuestPanicInformationTdx'}}
##
# @GuestPanicInformationHyperV:
@ -598,6 +601,30 @@
'psw-addr': 'uint64',
'reason': 'S390CrashReason'}}
##
# @GuestPanicInformationTdx:
#
# TDX Guest panic information specific to TDX, as specified in the
# "Guest-Hypervisor Communication Interface (GHCI) Specification",
# section TDG.VP.VMCALL<ReportFatalError>.
#
# @error-code: TD-specific error code
#
# @message: Human-readable error message provided by the guest. Not
# to be trusted.
#
# @gpa: guest-physical address of a page that contains more verbose
# error information, as zero-terminated string. Present when the
# "GPA valid" bit (bit 63) is set in @error-code.
#
#
# Since: 10.1
##
{'struct': 'GuestPanicInformationTdx',
'data': {'error-code': 'uint32',
'message': 'str',
'*gpa': 'uint64'}}
##
# @MEMORY_FAILURE:
#

View file

@ -1,17 +1,11 @@
subproject('bilge-0.2-rs', required: true)
subproject('bilge-impl-0.2-rs', required: true)
bilge_dep = dependency('bilge-0.2-rs')
bilge_impl_dep = dependency('bilge-impl-0.2-rs')
_libpl011_rs = static_library(
'pl011',
files('src/lib.rs'),
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
dependencies: [
bilge_dep,
bilge_impl_dep,
bilge_rs,
bilge_impl_rs,
qemu_api,
qemu_api_macros,
],
@ -21,6 +15,6 @@ rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
link_whole: [_libpl011_rs],
# Putting proc macro crates in `dependencies` is necessary for Meson to find
# them when compiling the root per-target static rust lib.
dependencies: [bilge_impl_dep, qemu_api_macros],
dependencies: [bilge_impl_rs, qemu_api_macros],
variables: {'crate': 'pl011'},
)])

View file

@ -480,13 +480,13 @@ impl PL011Registers {
}
impl PL011State {
/// Initializes a pre-allocated, unitialized instance of `PL011State`.
/// Initializes a pre-allocated, uninitialized instance of `PL011State`.
///
/// # Safety
///
/// `self` must point to a correctly sized and aligned location for the
/// `PL011State` type. It must not be called more than once on the same
/// location/instance. All its fields are expected to hold unitialized
/// location/instance. All its fields are expected to hold uninitialized
/// values with the sole exception of `parent_obj`.
unsafe fn init(&mut self) {
static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()

View file

@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{

View file

@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::ptr::addr_of_mut;

View file

@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
//! # HPET QEMU Device Model
@ -7,7 +7,7 @@
//! This library implements a device model for the IA-PC HPET (High
//! Precision Event Timers) device in QEMU.
pub mod device;
pub mod fw_cfg;
pub mod hpet;
pub const TYPE_HPET: &::std::ffi::CStr = c"hpet";

View file

@ -1,3 +1,19 @@
subproject('bilge-0.2-rs', required: true)
subproject('bilge-impl-0.2-rs', required: true)
subproject('libc-0.2-rs', required: true)
bilge_rs = dependency('bilge-0.2-rs')
bilge_impl_rs = dependency('bilge-impl-0.2-rs')
libc_rs = dependency('libc-0.2-rs')
subproject('proc-macro2-1-rs', required: true)
subproject('quote-1-rs', required: true)
subproject('syn-2-rs', required: true)
quote_rs_native = dependency('quote-1-rs', native: true)
syn_rs_native = dependency('syn-2-rs', native: true)
proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true)
subdir('qemu-api-macros')
subdir('qemu-api')

View file

@ -1,11 +1,3 @@
subproject('proc-macro2-1-rs', required: true)
subproject('quote-1-rs', required: true)
subproject('syn-2-rs', required: true)
quote_dep = dependency('quote-1-rs', native: true)
syn_dep = dependency('syn-2-rs', native: true)
proc_macro2_dep = dependency('proc-macro2-1-rs', native: true)
_qemu_api_macros_rs = rust.proc_macro(
'qemu_api_macros',
files('src/lib.rs'),
@ -16,9 +8,9 @@ _qemu_api_macros_rs = rust.proc_macro(
'--cfg', 'feature="proc-macro"',
],
dependencies: [
proc_macro2_dep,
quote_dep,
syn_dep,
proc_macro2_rs_native,
quote_rs_native,
syn_rs_native,
],
)

View file

@ -2,8 +2,6 @@ _qemu_api_cfg = run_command(rustc_args,
'--config-headers', config_host_h, '--features', files('Cargo.toml'),
capture: true, check: true).stdout().strip().splitlines()
libc_dep = dependency('libc-0.2-rs')
# _qemu_api_cfg += ['--cfg', 'feature="allocator"']
if get_option('debug_mutex')
_qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
@ -37,7 +35,7 @@ _qemu_api_rs = static_library(
override_options: ['rust_std=2021', 'build.rust_std=2021'],
rust_abi: 'rust',
rust_args: _qemu_api_cfg,
dependencies: [libc_dep, qemu_api_macros],
dependencies: [libc_rs, qemu_api_macros],
)
rust.test('rust-qemu-api-tests', _qemu_api_rs,

View file

@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
//! This module provides bit operation extensions to integer types.

View file

@ -291,7 +291,7 @@ pub unsafe trait ObjectType: Sized {
}
/// Return the receiver as a const raw pointer to Object.
/// This is preferrable to `as_object_mut_ptr()` if a C
/// This is preferable to `as_object_mut_ptr()` if a C
/// function only needs a `const Object *`.
fn as_object_ptr(&self) -> *const bindings::Object {
self.as_object().as_ptr()
@ -485,7 +485,7 @@ pub trait ObjectImpl: ObjectType + IsA<Object> {
/// `INSTANCE_INIT` functions have been called.
const INSTANCE_POST_INIT: Option<fn(&Self)> = None;
/// Called on descendent classes after all parent class initialization
/// Called on descendant classes after all parent class initialization
/// has occurred, but before the class itself is initialized. This
/// is only useful if a class is not a leaf, and can be used to undo
/// the effects of copying the contents of the parent's class struct

View file

@ -1,5 +1,5 @@
// Copyright (C) 2024 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{

View file

@ -9,7 +9,7 @@
//! * [`vmstate_unused!`](crate::vmstate_unused) and
//! [`vmstate_of!`](crate::vmstate_of), which are used to express the
//! migration format for a struct. This is based on the [`VMState`] trait,
//! which is defined by all migrateable types.
//! which is defined by all migratable types.
//!
//! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and
//! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with

View file

@ -1,5 +1,5 @@
// Copyright (C) 2025 Intel Corporation.
// Author(s): Zhao Liu <zhai1.liu@intel.com>
// Author(s): Zhao Liu <zhao1.liu@intel.com>
// SPDX-License-Identifier: GPL-2.0-or-later
use std::{

View file

@ -7,7 +7,7 @@ system_ss.add(files(
'vl.c',
), sdl, libpmem, libdaxctl)
libsystem_ss.add(files(
system_ss.add(files(
'balloon.c',
'bootdevice.c',
'cpus.c',

View file

@ -590,6 +590,58 @@ static void qemu_system_wakeup(void)
}
}
static char *tdx_parse_panic_message(char *message)
{
bool printable = false;
char *buf = NULL;
int len = 0, i;
/*
* Although message is defined as a json string, we shouldn't
* unconditionally treat it as is because the guest generated it and
* it's not necessarily trustable.
*/
if (message) {
/* The caller guarantees the NULL-terminated string. */
len = strlen(message);
printable = len > 0;
for (i = 0; i < len; i++) {
if (!(0x20 <= message[i] && message[i] <= 0x7e)) {
printable = false;
break;
}
}
}
if (len == 0) {
buf = g_malloc(1);
buf[0] = '\0';
} else {
if (!printable) {
/* 3 = length of "%02x " */
buf = g_malloc(len * 3);
for (i = 0; i < len; i++) {
if (message[i] == '\0') {
break;
} else {
sprintf(buf + 3 * i, "%02x ", message[i]);
}
}
if (i > 0) {
/* replace the last ' '(space) to NULL */
buf[i * 3 - 1] = '\0';
} else {
buf[0] = '\0';
}
} else {
buf = g_strdup(message);
}
}
return buf;
}
void qemu_system_guest_panicked(GuestPanicInformation *info)
{
qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed");
@ -631,7 +683,20 @@ void qemu_system_guest_panicked(GuestPanicInformation *info)
S390CrashReason_str(info->u.s390.reason),
info->u.s390.psw_mask,
info->u.s390.psw_addr);
} else if (info->type == GUEST_PANIC_INFORMATION_TYPE_TDX) {
char *message = tdx_parse_panic_message(info->u.tdx.message);
qemu_log_mask(LOG_GUEST_ERROR,
"\nTDX guest reports fatal error."
" error code: 0x%" PRIx32 " error message:\"%s\"\n",
info->u.tdx.error_code, message);
g_free(message);
if (info->u.tdx.gpa != -1ull) {
qemu_log_mask(LOG_GUEST_ERROR, "Additional error information "
"can be found at gpa page: 0x%" PRIx64 "\n",
info->u.tdx.gpa);
}
}
qapi_free_GuestPanicInformation(info);
}
}

View file

@ -1846,6 +1846,11 @@ static int kvm_arm_sve_set_vls(ARMCPU *cpu)
#define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
int ret;

View file

@ -28,7 +28,7 @@ arm_user_ss.add(files(
'vfp_fpscr.c',
))
arm_common_system_ss.add(files('cpu.c'), capstone)
arm_common_system_ss.add(files('cpu.c'))
arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files(
'cpu32-stubs.c'))
arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c'))

View file

@ -39,8 +39,10 @@ struct X86ConfidentialGuestClass {
/* <public> */
int (*kvm_type)(X86ConfidentialGuest *cg);
uint32_t (*mask_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index,
int reg, uint32_t value);
void (*cpu_instance_init)(X86ConfidentialGuest *cg, CPUState *cpu);
uint32_t (*adjust_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature,
uint32_t index, int reg, uint32_t value);
int (*check_features)(X86ConfidentialGuest *cg, CPUState *cs);
};
/**
@ -59,25 +61,47 @@ static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg)
}
}
static inline void x86_confidential_guest_cpu_instance_init(X86ConfidentialGuest *cg,
CPUState *cpu)
{
X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg);
if (klass->cpu_instance_init) {
klass->cpu_instance_init(cg, cpu);
}
}
/**
* x86_confidential_guest_mask_cpuid_features:
* x86_confidential_guest_adjust_cpuid_features:
*
* Removes unsupported features from a confidential guest's CPUID values, returns
* the value with the bits removed. The bits removed should be those that KVM
* provides independent of host-supported CPUID features, but are not supported by
* the confidential computing firmware.
* Adjust the supported features from a confidential guest's CPUID values,
* returns the adjusted value. There are bits being removed that are not
* supported by the confidential computing firmware or bits being added that
* are forcibly exposed to guest by the confidential computing firmware.
*/
static inline int x86_confidential_guest_mask_cpuid_features(X86ConfidentialGuest *cg,
static inline int x86_confidential_guest_adjust_cpuid_features(X86ConfidentialGuest *cg,
uint32_t feature, uint32_t index,
int reg, uint32_t value)
{
X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg);
if (klass->mask_cpuid_features) {
return klass->mask_cpuid_features(cg, feature, index, reg, value);
if (klass->adjust_cpuid_features) {
return klass->adjust_cpuid_features(cg, feature, index, reg, value);
} else {
return value;
}
}
static inline int x86_confidential_guest_check_features(X86ConfidentialGuest *cg,
CPUState *cs)
{
X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg);
if (klass->check_features) {
return klass->check_features(cg, cs);
}
return 0;
}
#endif

View file

@ -37,6 +37,7 @@
#include "hw/i386/topology.h"
#include "exec/watchpoint.h"
#ifndef CONFIG_USER_ONLY
#include "confidential-guest.h"
#include "system/reset.h"
#include "qapi/qapi-commands-machine.h"
#include "system/address-spaces.h"
@ -1252,12 +1253,12 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
[FEAT_8000_0021_EAX] = {
.type = CPUID_FEATURE_WORD,
.feat_names = {
"no-nested-data-bp", NULL, "lfence-always-serializing", NULL,
"no-nested-data-bp", "fs-gs-base-ns", "lfence-always-serializing", NULL,
NULL, NULL, "null-sel-clr-base", NULL,
"auto-ibrs", NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
"prefetchi", NULL, NULL, NULL,
"eraps", NULL, NULL, "sbpb",
"ibpb-brtype", "srso-no", "srso-user-kernel-no", NULL,
},
@ -1677,14 +1678,21 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
};
typedef struct FeatureMask {
FeatureWord index;
uint64_t mask;
} FeatureMask;
bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg)
{
FeatureWordInfo *wi;
FeatureWord w;
typedef struct FeatureDep {
FeatureMask from, to;
} FeatureDep;
for (w = 0; w < FEATURE_WORDS; w++) {
wi = &feature_word_info[w];
if (wi->type == CPUID_FEATURE_WORD && wi->cpuid.eax == feature &&
(!wi->cpuid.needs_ecx || wi->cpuid.ecx == index) &&
wi->cpuid.reg == reg) {
return true;
}
}
return false;
}
static FeatureDep feature_dependencies[] = {
{
@ -1854,9 +1862,6 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
};
#undef REGISTER
/* CPUID feature bits available in XSS */
#define CPUID_XSTATE_XSS_MASK (XSTATE_ARCH_LBR_MASK)
ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = {
[XSTATE_FP_BIT] = {
/* x87 FP state component is always enabled if XSAVE is supported */
@ -2206,6 +2211,60 @@ static CPUCaches epyc_v4_cache_info = {
},
};
static CPUCaches epyc_v5_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
.level = 1,
.size = 64 * KiB,
.line_size = 64,
.associativity = 4,
.partitions = 1,
.sets = 256,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 2,
.size = 512 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.self_init = true,
.inclusive = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 3,
.size = 8 * MiB,
.line_size = 64,
.associativity = 16,
.partitions = 1,
.sets = 8192,
.lines_per_tag = 1,
.self_init = true,
.no_invd_sharing = true,
.complex_indexing = false,
.share_level = CPU_TOPOLOGY_LEVEL_DIE,
},
};
static const CPUCaches epyc_rome_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
@ -2314,6 +2373,60 @@ static const CPUCaches epyc_rome_v3_cache_info = {
},
};
static const CPUCaches epyc_rome_v5_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 2,
.size = 512 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.self_init = true,
.inclusive = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 3,
.size = 16 * MiB,
.line_size = 64,
.associativity = 16,
.partitions = 1,
.sets = 16384,
.lines_per_tag = 1,
.self_init = true,
.no_invd_sharing = true,
.complex_indexing = false,
.share_level = CPU_TOPOLOGY_LEVEL_DIE,
},
};
static const CPUCaches epyc_milan_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
@ -2422,6 +2535,60 @@ static const CPUCaches epyc_milan_v2_cache_info = {
},
};
static const CPUCaches epyc_milan_v3_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 2,
.size = 512 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.self_init = true,
.inclusive = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 3,
.size = 32 * MiB,
.line_size = 64,
.associativity = 16,
.partitions = 1,
.sets = 32768,
.lines_per_tag = 1,
.self_init = true,
.no_invd_sharing = true,
.complex_indexing = false,
.share_level = CPU_TOPOLOGY_LEVEL_DIE,
},
};
static const CPUCaches epyc_genoa_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
@ -2476,6 +2643,114 @@ static const CPUCaches epyc_genoa_cache_info = {
},
};
static const CPUCaches epyc_genoa_v2_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 2,
.size = 1 * MiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 2048,
.lines_per_tag = 1,
.self_init = true,
.inclusive = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 3,
.size = 32 * MiB,
.line_size = 64,
.associativity = 16,
.partitions = 1,
.sets = 32768,
.lines_per_tag = 1,
.self_init = true,
.no_invd_sharing = true,
.complex_indexing = false,
.share_level = CPU_TOPOLOGY_LEVEL_DIE,
},
};
static const CPUCaches epyc_turin_cache_info = {
.l1d_cache = &(CPUCacheInfo) {
.type = DATA_CACHE,
.level = 1,
.size = 48 * KiB,
.line_size = 64,
.associativity = 12,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l1i_cache = &(CPUCacheInfo) {
.type = INSTRUCTION_CACHE,
.level = 1,
.size = 32 * KiB,
.line_size = 64,
.associativity = 8,
.partitions = 1,
.sets = 64,
.lines_per_tag = 1,
.self_init = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l2_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 2,
.size = 1 * MiB,
.line_size = 64,
.associativity = 16,
.partitions = 1,
.sets = 1024,
.lines_per_tag = 1,
.self_init = true,
.inclusive = true,
.share_level = CPU_TOPOLOGY_LEVEL_CORE,
},
.l3_cache = &(CPUCacheInfo) {
.type = UNIFIED_CACHE,
.level = 3,
.size = 32 * MiB,
.line_size = 64,
.associativity = 16,
.partitions = 1,
.sets = 32768,
.lines_per_tag = 1,
.self_init = true,
.no_invd_sharing = true,
.complex_indexing = false,
.share_level = CPU_TOPOLOGY_LEVEL_DIE,
},
};
/* The following VMX features are not supported by KVM and are left out in the
* CPU definitions:
*
@ -5233,6 +5508,25 @@ static const X86CPUDefinition builtin_x86_defs[] = {
},
.cache_info = &epyc_v4_cache_info
},
{
.version = 5,
.props = (PropValue[]) {
{ "overflow-recov", "on" },
{ "succor", "on" },
{ "lbrv", "on" },
{ "tsc-scale", "on" },
{ "vmcb-clean", "on" },
{ "flushbyasid", "on" },
{ "pause-filter", "on" },
{ "pfthreshold", "on" },
{ "v-vmsave-vmload", "on" },
{ "vgif", "on" },
{ "model-id",
"AMD EPYC-v5 Processor" },
{ /* end of list */ }
},
.cache_info = &epyc_v5_cache_info
},
{ /* end of list */ }
}
},
@ -5371,6 +5665,25 @@ static const X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
},
},
{
.version = 5,
.props = (PropValue[]) {
{ "overflow-recov", "on" },
{ "succor", "on" },
{ "lbrv", "on" },
{ "tsc-scale", "on" },
{ "vmcb-clean", "on" },
{ "flushbyasid", "on" },
{ "pause-filter", "on" },
{ "pfthreshold", "on" },
{ "v-vmsave-vmload", "on" },
{ "vgif", "on" },
{ "model-id",
"AMD EPYC-Rome-v5 Processor" },
{ /* end of list */ }
},
.cache_info = &epyc_rome_v5_cache_info
},
{ /* end of list */ }
}
},
@ -5446,6 +5759,25 @@ static const X86CPUDefinition builtin_x86_defs[] = {
},
.cache_info = &epyc_milan_v2_cache_info
},
{
.version = 3,
.props = (PropValue[]) {
{ "overflow-recov", "on" },
{ "succor", "on" },
{ "lbrv", "on" },
{ "tsc-scale", "on" },
{ "vmcb-clean", "on" },
{ "flushbyasid", "on" },
{ "pause-filter", "on" },
{ "pfthreshold", "on" },
{ "v-vmsave-vmload", "on" },
{ "vgif", "on" },
{ "model-id",
"AMD EPYC-Milan-v3 Processor" },
{ /* end of list */ }
},
.cache_info = &epyc_milan_v3_cache_info
},
{ /* end of list */ }
}
},
@ -5520,6 +5852,31 @@ static const X86CPUDefinition builtin_x86_defs[] = {
.xlevel = 0x80000022,
.model_id = "AMD EPYC-Genoa Processor",
.cache_info = &epyc_genoa_cache_info,
.versions = (X86CPUVersionDefinition[]) {
{ .version = 1 },
{
.version = 2,
.props = (PropValue[]) {
{ "overflow-recov", "on" },
{ "succor", "on" },
{ "lbrv", "on" },
{ "tsc-scale", "on" },
{ "vmcb-clean", "on" },
{ "flushbyasid", "on" },
{ "pause-filter", "on" },
{ "pfthreshold", "on" },
{ "v-vmsave-vmload", "on" },
{ "vgif", "on" },
{ "fs-gs-base-ns", "on" },
{ "perfmon-v2", "on" },
{ "model-id",
"AMD EPYC-Genoa-v2 Processor" },
{ /* end of list */ }
},
.cache_info = &epyc_genoa_v2_cache_info
},
{ /* end of list */ }
}
},
{
.name = "YongFeng",
@ -5657,6 +6014,89 @@ static const X86CPUDefinition builtin_x86_defs[] = {
{ /* end of list */ }
}
},
{
.name = "EPYC-Turin",
.level = 0xd,
.vendor = CPUID_VENDOR_AMD,
.family = 26,
.model = 0,
.stepping = 0,
.features[FEAT_1_ECX] =
CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX |
CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT |
CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 |
CPUID_EXT_PCID | CPUID_EXT_CX16 | CPUID_EXT_FMA |
CPUID_EXT_SSSE3 | CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ |
CPUID_EXT_SSE3,
.features[FEAT_1_EDX] =
CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH |
CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE |
CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE |
CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE |
CPUID_VME | CPUID_FP87,
.features[FEAT_6_EAX] =
CPUID_6_EAX_ARAT,
.features[FEAT_7_0_EBX] =
CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 |
CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS |
CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_AVX512F |
CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_AVX512IFMA |
CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB |
CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_SHA_NI |
CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL,
.features[FEAT_7_0_ECX] =
CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU |
CPUID_7_0_ECX_AVX512_VBMI2 | CPUID_7_0_ECX_GFNI |
CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ |
CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG |
CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57 |
CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_MOVDIRI |
CPUID_7_0_ECX_MOVDIR64B,
.features[FEAT_7_0_EDX] =
CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_AVX512_VP2INTERSECT,
.features[FEAT_7_1_EAX] =
CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16,
.features[FEAT_8000_0001_ECX] =
CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH |
CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM |
CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM |
CPUID_EXT3_TOPOEXT | CPUID_EXT3_PERFCORE,
.features[FEAT_8000_0001_EDX] =
CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB |
CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX |
CPUID_EXT2_SYSCALL,
.features[FEAT_8000_0007_EBX] =
CPUID_8000_0007_EBX_OVERFLOW_RECOV | CPUID_8000_0007_EBX_SUCCOR,
.features[FEAT_8000_0008_EBX] =
CPUID_8000_0008_EBX_CLZERO | CPUID_8000_0008_EBX_XSAVEERPTR |
CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_IBPB |
CPUID_8000_0008_EBX_IBRS | CPUID_8000_0008_EBX_STIBP |
CPUID_8000_0008_EBX_STIBP_ALWAYS_ON |
CPUID_8000_0008_EBX_AMD_SSBD | CPUID_8000_0008_EBX_AMD_PSFD,
.features[FEAT_8000_0021_EAX] =
CPUID_8000_0021_EAX_NO_NESTED_DATA_BP |
CPUID_8000_0021_EAX_FS_GS_BASE_NS |
CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING |
CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE |
CPUID_8000_0021_EAX_AUTO_IBRS | CPUID_8000_0021_EAX_PREFETCHI |
CPUID_8000_0021_EAX_SBPB | CPUID_8000_0021_EAX_IBPB_BRTYPE |
CPUID_8000_0021_EAX_SRSO_USER_KERNEL_NO,
.features[FEAT_8000_0022_EAX] =
CPUID_8000_0022_EAX_PERFMON_V2,
.features[FEAT_XSAVE] =
CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC |
CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES,
.features[FEAT_SVM] =
CPUID_SVM_NPT | CPUID_SVM_LBRV | CPUID_SVM_NRIPSAVE |
CPUID_SVM_TSCSCALE | CPUID_SVM_VMCBCLEAN | CPUID_SVM_FLUSHASID |
CPUID_SVM_PAUSEFILTER | CPUID_SVM_PFTHRESHOLD |
CPUID_SVM_V_VMSAVE_VMLOAD | CPUID_SVM_VGIF |
CPUID_SVM_VNMI | CPUID_SVM_SVME_ADDR_CHK,
.xlevel = 0x80000022,
.model_id = "AMD EPYC-Turin Processor",
.cache_info = &epyc_turin_cache_info,
},
};
/*
@ -5766,7 +6206,7 @@ static const TypeInfo max_x86_cpu_type_info = {
.class_init = max_x86_cpu_class_init,
};
static char *feature_word_description(FeatureWordInfo *f, uint32_t bit)
static char *feature_word_description(FeatureWordInfo *f)
{
assert(f->type == CPUID_FEATURE_WORD || f->type == MSR_FEATURE_WORD);
@ -5775,11 +6215,15 @@ static char *feature_word_description(FeatureWordInfo *f, uint32_t bit)
{
const char *reg = get_register_name_32(f->cpuid.reg);
assert(reg);
return g_strdup_printf("CPUID.%02XH:%s",
f->cpuid.eax, reg);
if (!f->cpuid.needs_ecx) {
return g_strdup_printf("CPUID[eax=%02Xh].%s", f->cpuid.eax, reg);
} else {
return g_strdup_printf("CPUID[eax=%02Xh,ecx=%02Xh].%s",
f->cpuid.eax, f->cpuid.ecx, reg);
}
}
case MSR_FEATURE_WORD:
return g_strdup_printf("MSR(%02XH)",
return g_strdup_printf("MSR(%02Xh)",
f->msr.index);
}
@ -5799,12 +6243,13 @@ static bool x86_cpu_have_filtered_features(X86CPU *cpu)
return false;
}
static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask,
const char *verbose_prefix)
void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask,
const char *verbose_prefix)
{
CPUX86State *env = &cpu->env;
FeatureWordInfo *f = &feature_word_info[w];
int i;
g_autofree char *feat_word_str = feature_word_description(f);
if (!cpu->force_features) {
env->features[w] &= ~mask;
@ -5817,7 +6262,35 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask,
for (i = 0; i < 64; ++i) {
if ((1ULL << i) & mask) {
g_autofree char *feat_word_str = feature_word_description(f, i);
warn_report("%s: %s%s%s [bit %d]",
verbose_prefix,
feat_word_str,
f->feat_names[i] ? "." : "",
f->feat_names[i] ? f->feat_names[i] : "", i);
}
}
}
void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask,
const char *verbose_prefix)
{
CPUX86State *env = &cpu->env;
FeatureWordInfo *f = &feature_word_info[w];
int i;
if (!cpu->force_features) {
env->features[w] |= mask;
}
cpu->forced_on_features[w] |= mask;
if (!verbose_prefix) {
return;
}
for (i = 0; i < 64; ++i) {
if ((1ULL << i) & mask) {
g_autofree char *feat_word_str = feature_word_description(f);
warn_report("%s: %s%s%s [bit %d]",
verbose_prefix,
feat_word_str,
@ -7044,7 +7517,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
break;
case 0x1F:
/* V2 Extended Topology Enumeration Leaf */
if (!x86_has_extended_topo(env->avail_cpu_topo)) {
if (!x86_has_cpuid_0x1f(cpu)) {
*eax = *ebx = *ecx = *edx = 0;
break;
}
@ -7908,7 +8381,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
* cpu->vendor_cpuid_only has been unset for compatibility with older
* machine types.
*/
if (x86_has_extended_topo(env->avail_cpu_topo) &&
if (x86_has_cpuid_0x1f(cpu) &&
(IS_INTEL_CPU(env) || !cpu->vendor_cpuid_only)) {
x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F);
}
@ -8543,6 +9016,13 @@ static void x86_cpu_post_initfn(Object *obj)
}
accel_cpu_instance_init(CPU(obj));
#ifndef CONFIG_USER_ONLY
if (current_machine && current_machine->cgs) {
x86_confidential_guest_cpu_instance_init(
X86_CONFIDENTIAL_GUEST(current_machine->cgs), (CPU(obj)));
}
#endif
}
static void x86_cpu_init_default_topo(X86CPU *cpu)

View file

@ -584,6 +584,7 @@ typedef enum X86Seg {
#define XSTATE_OPMASK_BIT 5
#define XSTATE_ZMM_Hi256_BIT 6
#define XSTATE_Hi16_ZMM_BIT 7
#define XSTATE_PT_BIT 8
#define XSTATE_PKRU_BIT 9
#define XSTATE_ARCH_LBR_BIT 15
#define XSTATE_XTILE_CFG_BIT 17
@ -597,6 +598,7 @@ typedef enum X86Seg {
#define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT)
#define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT)
#define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT)
#define XSTATE_PT_MASK (1ULL << XSTATE_PT_BIT)
#define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT)
#define XSTATE_ARCH_LBR_MASK (1ULL << XSTATE_ARCH_LBR_BIT)
#define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT)
@ -619,6 +621,11 @@ typedef enum X86Seg {
XSTATE_Hi16_ZMM_MASK | XSTATE_PKRU_MASK | \
XSTATE_XTILE_CFG_MASK | XSTATE_XTILE_DATA_MASK)
/* CPUID feature bits available in XSS */
#define CPUID_XSTATE_XSS_MASK (XSTATE_ARCH_LBR_MASK)
#define CPUID_XSTATE_MASK (CPUID_XSTATE_XCR0_MASK | CPUID_XSTATE_XSS_MASK)
/* CPUID feature words */
typedef enum FeatureWord {
FEAT_1_EDX, /* CPUID[1].EDX */
@ -667,6 +674,15 @@ typedef enum FeatureWord {
FEATURE_WORDS,
} FeatureWord;
typedef struct FeatureMask {
FeatureWord index;
uint64_t mask;
} FeatureMask;
typedef struct FeatureDep {
FeatureMask from, to;
} FeatureDep;
typedef uint64_t FeatureWordArray[FEATURE_WORDS];
uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
@ -899,6 +915,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_7_0_ECX_LA57 (1U << 16)
/* Read Processor ID */
#define CPUID_7_0_ECX_RDPID (1U << 22)
/* KeyLocker */
#define CPUID_7_0_ECX_KeyLocker (1U << 23)
/* Bus Lock Debug Exception */
#define CPUID_7_0_ECX_BUS_LOCK_DETECT (1U << 24)
/* Cache Line Demote Instruction */
@ -920,6 +938,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_7_0_EDX_FSRM (1U << 4)
/* AVX512 Vector Pair Intersection to a Pair of Mask Registers */
#define CPUID_7_0_EDX_AVX512_VP2INTERSECT (1U << 8)
/* "md_clear" VERW clears CPU buffers */
#define CPUID_7_0_EDX_MD_CLEAR (1U << 10)
/* SERIALIZE instruction */
#define CPUID_7_0_EDX_SERIALIZE (1U << 14)
/* TSX Suspend Load Address Tracking instruction */
@ -957,6 +977,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_7_1_EAX_AVX_VNNI (1U << 4)
/* AVX512 BFloat16 Instruction */
#define CPUID_7_1_EAX_AVX512_BF16 (1U << 5)
/* Linear address space separation */
#define CPUID_7_1_EAX_LASS (1U << 6)
/* CMPCCXADD Instructions */
#define CPUID_7_1_EAX_CMPCCXADD (1U << 7)
/* Fast Zero REP MOVS */
@ -1070,12 +1092,16 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
/* Processor ignores nested data breakpoints */
#define CPUID_8000_0021_EAX_NO_NESTED_DATA_BP (1U << 0)
/* WRMSR to FS_BASE, GS_BASE, or KERNEL_GS_BASE is non-serializing */
#define CPUID_8000_0021_EAX_FS_GS_BASE_NS (1U << 1)
/* LFENCE is always serializing */
#define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING (1U << 2)
/* Null Selector Clears Base */
#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6)
/* Automatic IBRS */
#define CPUID_8000_0021_EAX_AUTO_IBRS (1U << 8)
/* Indicates support for IC prefetch */
#define CPUID_8000_0021_EAX_PREFETCHI (1U << 20)
/* Enhanced Return Address Predictor Scurity */
#define CPUID_8000_0021_EAX_ERAPS (1U << 24)
/* Selective Branch Predictor Barrier */
@ -1100,6 +1126,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w);
#define CPUID_XSAVE_XSAVEC (1U << 1)
#define CPUID_XSAVE_XGETBV1 (1U << 2)
#define CPUID_XSAVE_XSAVES (1U << 3)
#define CPUID_XSAVE_XFD (1U << 4)
#define CPUID_6_EAX_ARAT (1U << 2)
@ -2192,6 +2219,9 @@ struct ArchCPU {
/* Features that were filtered out because of missing host capabilities */
FeatureWordArray filtered_features;
/* Features that are forced enabled by underlying hypervisor, e.g., TDX */
FeatureWordArray forced_on_features;
/* Enable PMU CPUID bits. This can't be enabled by default yet because
* it doesn't have ABI stability guarantees, as it passes all PMU CPUID
* bits returned by GET_SUPPORTED_CPUID (that depend on host CPU and kernel
@ -2239,6 +2269,9 @@ struct ArchCPU {
/* Compatibility bits for old machine types: */
bool enable_cpuid_0xb;
/* Force to enable cpuid 0x1f */
bool enable_cpuid_0x1f;
/* Enable auto level-increase for all CPUID leaves */
bool full_cpuid_auto_level;
@ -2499,6 +2532,17 @@ void cpu_set_apic_feature(CPUX86State *env);
void host_cpuid(uint32_t function, uint32_t count,
uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
bool cpu_has_x2apic_feature(CPUX86State *env);
bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg);
void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask,
const char *verbose_prefix);
void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask,
const char *verbose_prefix);
static inline bool x86_has_cpuid_0x1f(X86CPU *cpu)
{
return cpu->enable_cpuid_0x1f ||
x86_has_extended_topo(cpu->env.avail_cpu_topo);
}
/* helper.c */
void x86_cpu_set_a20(X86CPU *cpu, int a20_state);

View file

@ -255,19 +255,19 @@ void lflags_to_rflags(CPUX86State *env)
void rflags_to_lflags(CPUX86State *env)
{
target_ulong cf_xor_of;
target_ulong cf_af, cf_xor_of;
/* Leave the low byte zero so that parity is always even... */
env->cc_dst = !(env->eflags & CC_Z) << 8;
/* ... and therefore cc_src always uses opposite polarity. */
env->cc_src = CC_P;
env->cc_src ^= env->eflags & (CC_S | CC_P);
/* rotate right by one to move CF and AF into the carry-out positions */
env->cc_src |= (
(env->eflags >> 1) |
(env->eflags << (TARGET_LONG_BITS - 1))) & (CC_C | CC_A);
cf_af = env->eflags & (CC_C | CC_A);
env->cc_src |= ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1)));
cf_xor_of = (env->eflags & (CC_C | CC_O)) + (CC_O - CC_C);
cf_xor_of = ((env->eflags & (CC_C | CC_O)) + (CC_O - CC_C)) & CC_O;
env->cc_src |= -cf_xor_of & LF_MASK_PO;
/* Leave the low byte zero so that parity is not affected. */
env->cc_dst = !(env->eflags & CC_Z) << 8;
}

View file

@ -15,7 +15,7 @@
#include "system/system.h"
/* Note: Only safe for use on x86(-64) hosts */
static uint32_t host_cpu_phys_bits(void)
uint32_t host_cpu_phys_bits(void)
{
uint32_t eax;
uint32_t host_phys_bits;

View file

@ -10,6 +10,7 @@
#ifndef HOST_CPU_H
#define HOST_CPU_H
uint32_t host_cpu_phys_bits(void);
void host_cpu_instance_init(X86CPU *cpu);
void host_cpu_max_instance_init(X86CPU *cpu);
bool host_cpu_realizefn(CPUState *cs, Error **errp);

View file

@ -38,6 +38,7 @@
#include "kvm_i386.h"
#include "../confidential-guest.h"
#include "sev.h"
#include "tdx.h"
#include "xen-emu.h"
#include "hyperv.h"
#include "hyperv-proto.h"
@ -192,6 +193,7 @@ static const char *vm_type_name[] = {
[KVM_X86_SEV_VM] = "SEV",
[KVM_X86_SEV_ES_VM] = "SEV-ES",
[KVM_X86_SNP_VM] = "SEV-SNP",
[KVM_X86_TDX_VM] = "TDX",
};
bool kvm_is_vm_type_supported(int type)
@ -326,7 +328,7 @@ void kvm_synchronize_all_tsc(void)
{
CPUState *cpu;
if (kvm_enabled()) {
if (kvm_enabled() && !is_tdx_vm()) {
CPU_FOREACH(cpu) {
run_on_cpu(cpu, do_kvm_synchronize_tsc, RUN_ON_CPU_NULL);
}
@ -392,7 +394,7 @@ static bool host_tsx_broken(void)
/* Returns the value for a specific register on the cpuid entry
*/
static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg)
uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg)
{
uint32_t ret = 0;
switch (reg) {
@ -414,9 +416,9 @@ static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg)
/* Find matching entry for function/index on kvm_cpuid2 struct
*/
static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
uint32_t function,
uint32_t index)
struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
uint32_t function,
uint32_t index)
{
int i;
for (i = 0; i < cpuid->nent; ++i) {
@ -572,7 +574,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function,
}
if (current_machine->cgs) {
ret = x86_confidential_guest_mask_cpuid_features(
ret = x86_confidential_guest_adjust_cpuid_features(
X86_CONFIDENTIAL_GUEST(current_machine->cgs),
function, index, reg, ret);
}
@ -868,6 +870,15 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
int r, cur_freq;
bool set_ioctl = false;
/*
* TSC of TD vcpu is immutable, it cannot be set/changed via vcpu scope
* VM_SET_TSC_KHZ, but only be initialized via VM scope VM_SET_TSC_KHZ
* before ioctl KVM_TDX_INIT_VM in tdx_pre_create_vcpu()
*/
if (is_tdx_vm()) {
return 0;
}
if (!env->tsc_khz) {
return 0;
}
@ -1779,8 +1790,6 @@ static int hyperv_init_vcpu(X86CPU *cpu)
static Error *invtsc_mig_blocker;
#define KVM_MAX_CPUID_ENTRIES 100
static void kvm_init_xsave(CPUX86State *env)
{
if (has_xsave2) {
@ -1823,9 +1832,8 @@ static void kvm_init_nested_state(CPUX86State *env)
}
}
static uint32_t kvm_x86_build_cpuid(CPUX86State *env,
struct kvm_cpuid_entry2 *entries,
uint32_t cpuid_i)
uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries,
uint32_t cpuid_i)
{
uint32_t limit, i, j;
uint32_t unused;
@ -1864,7 +1872,7 @@ static uint32_t kvm_x86_build_cpuid(CPUX86State *env,
break;
}
case 0x1f:
if (!x86_has_extended_topo(env->avail_cpu_topo)) {
if (!x86_has_cpuid_0x1f(env_archcpu(env))) {
cpuid_i--;
break;
}
@ -2052,6 +2060,15 @@ full:
abort();
}
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
if (is_tdx_vm()) {
return tdx_pre_create_vcpu(cpu, errp);
}
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
struct {
@ -2076,6 +2093,14 @@ int kvm_arch_init_vcpu(CPUState *cs)
int r;
Error *local_err = NULL;
if (current_machine->cgs) {
r = x86_confidential_guest_check_features(
X86_CONFIDENTIAL_GUEST(current_machine->cgs), cs);
if (r < 0) {
return r;
}
}
memset(&cpuid_data, 0, sizeof(cpuid_data));
cpuid_i = 0;
@ -3206,16 +3231,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
Error *local_err = NULL;
/*
* Initialize SEV context, if required
*
* If no memory encryption is requested (ms->cgs == NULL) this is
* a no-op.
*
* It's also a no-op if a non-SEV confidential guest support
* mechanism is selected. SEV is the only mechanism available to
* select on x86 at present, so this doesn't arise, but if new
* mechanisms are supported in future (e.g. TDX), they'll need
* their own initialization either here or elsewhere.
* Initialize confidential guest (SEV/TDX) context, if required
*/
if (ms->cgs) {
ret = confidential_guest_kvm_init(ms->cgs, &local_err);
@ -3856,32 +3872,34 @@ static void kvm_init_msrs(X86CPU *cpu)
CPUX86State *env = &cpu->env;
kvm_msr_buf_reset(cpu);
if (has_msr_arch_capabs) {
kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES,
env->features[FEAT_ARCH_CAPABILITIES]);
}
if (has_msr_core_capabs) {
kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY,
env->features[FEAT_CORE_CAPABILITY]);
}
if (!is_tdx_vm()) {
if (has_msr_arch_capabs) {
kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES,
env->features[FEAT_ARCH_CAPABILITIES]);
}
if (has_msr_perf_capabs && cpu->enable_pmu) {
kvm_msr_entry_add_perf(cpu, env->features);
if (has_msr_core_capabs) {
kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY,
env->features[FEAT_CORE_CAPABILITY]);
}
if (has_msr_perf_capabs && cpu->enable_pmu) {
kvm_msr_entry_add_perf(cpu, env->features);
}
/*
* Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but
* all kernels with MSR features should have them.
*/
if (kvm_feature_msrs && cpu_has_vmx(env)) {
kvm_msr_entry_add_vmx(cpu, env->features);
}
}
if (has_msr_ucode_rev) {
kvm_msr_entry_add(cpu, MSR_IA32_UCODE_REV, cpu->ucode_rev);
}
/*
* Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but
* all kernels with MSR features should have them.
*/
if (kvm_feature_msrs && cpu_has_vmx(env)) {
kvm_msr_entry_add_vmx(cpu, env->features);
}
assert(kvm_buf_set_msrs(cpu) == 0);
}
@ -6121,6 +6139,16 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
case KVM_EXIT_HYPERCALL:
ret = kvm_handle_hypercall(run);
break;
case KVM_EXIT_SYSTEM_EVENT:
switch (run->system_event.type) {
case KVM_SYSTEM_EVENT_TDX_FATAL:
ret = tdx_handle_report_fatal_error(cpu, run);
break;
default:
ret = -1;
break;
}
break;
default:
fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason);
ret = -1;

View file

@ -13,6 +13,8 @@
#include "system/kvm.h"
#define KVM_MAX_CPUID_ENTRIES 100
/* always false if !CONFIG_KVM */
#define kvm_pit_in_kernel() \
(kvm_irqchip_in_kernel() && !kvm_irqchip_is_split())
@ -42,6 +44,13 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask);
#ifdef CONFIG_KVM
#include <linux/kvm.h>
typedef struct KvmCpuidInfo {
struct kvm_cpuid2 cpuid;
struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
} KvmCpuidInfo;
bool kvm_is_vm_type_supported(int type);
bool kvm_has_adjust_clock_stable(void);
bool kvm_has_exception_payload(void);
@ -57,6 +66,12 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address);
void kvm_update_msi_routes_all(void *private, bool global,
uint32_t index, uint32_t mask);
struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid,
uint32_t function,
uint32_t index);
uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg);
uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries,
uint32_t cpuid_i);
#endif /* CONFIG_KVM */
void kvm_pc_setup_irq_routing(bool pci_enabled);

View file

@ -8,6 +8,8 @@ i386_kvm_ss.add(files(
i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c'))
i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c'), if_false: files('tdx-stub.c'))
i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c'))
i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss)

View file

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "qemu/osdep.h"
#include "tdx.h"
int tdx_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return -EINVAL;
}
int tdx_parse_tdvf(void *flash_ptr, int size)
{
return -EINVAL;
}
int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run)
{
return -EINVAL;
}

1289
target/i386/kvm/tdx.c Normal file

File diff suppressed because it is too large Load diff

65
target/i386/kvm/tdx.h Normal file
View file

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef QEMU_I386_TDX_H
#define QEMU_I386_TDX_H
#ifndef CONFIG_USER_ONLY
#include CONFIG_DEVICES /* CONFIG_TDX */
#endif
#include "confidential-guest.h"
#include "cpu.h"
#include "hw/i386/tdvf.h"
#define TYPE_TDX_GUEST "tdx-guest"
#define TDX_GUEST(obj) OBJECT_CHECK(TdxGuest, (obj), TYPE_TDX_GUEST)
typedef struct TdxGuestClass {
X86ConfidentialGuestClass parent_class;
} TdxGuestClass;
/* TDX requires bus frequency 25MHz */
#define TDX_APIC_BUS_CYCLES_NS 40
enum TdxRamType {
TDX_RAM_UNACCEPTED,
TDX_RAM_ADDED,
};
typedef struct TdxRamEntry {
uint64_t address;
uint64_t length;
enum TdxRamType type;
} TdxRamEntry;
typedef struct TdxGuest {
X86ConfidentialGuest parent_obj;
QemuMutex lock;
bool initialized;
uint64_t attributes; /* TD attributes */
uint64_t xfam;
char *mrconfigid; /* base64 encoded sha348 digest */
char *mrowner; /* base64 encoded sha348 digest */
char *mrownerconfig; /* base64 encoded sha348 digest */
MemoryRegion *tdvf_mr;
TdxFirmware tdvf;
uint32_t nr_ram_entries;
TdxRamEntry *ram_entries;
} TdxGuest;
#ifdef CONFIG_TDX
bool is_tdx_vm(void);
#else
#define is_tdx_vm() 0
#endif /* CONFIG_TDX */
int tdx_pre_create_vcpu(CPUState *cpu, Error **errp);
void tdx_set_tdvf_region(MemoryRegion *tdvf_mr);
int tdx_parse_tdvf(void *flash_ptr, int size);
int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run);
#endif /* QEMU_I386_TDX_H */

View file

@ -212,14 +212,6 @@ static const char *const sev_fw_errlist[] = {
#define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist)
/* <linux/kvm.h> doesn't expose this, so re-use the max from kvm.c */
#define KVM_MAX_CPUID_ENTRIES 100
typedef struct KvmCpuidInfo {
struct kvm_cpuid2 cpuid;
struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES];
} KvmCpuidInfo;
#define SNP_CPUID_FUNCTION_MAXCOUNT 64
#define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF
@ -947,7 +939,7 @@ out:
}
static uint32_t
sev_snp_mask_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index,
sev_snp_adjust_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index,
int reg, uint32_t value)
{
switch (feature) {
@ -2405,7 +2397,7 @@ sev_snp_guest_class_init(ObjectClass *oc, const void *data)
klass->launch_finish = sev_snp_launch_finish;
klass->launch_update_data = sev_snp_launch_update_data;
klass->kvm_init = sev_snp_kvm_init;
x86_klass->mask_cpuid_features = sev_snp_mask_cpuid_features;
x86_klass->adjust_cpuid_features = sev_snp_adjust_cpuid_features;
x86_klass->kvm_type = sev_snp_kvm_type;
object_class_property_add(oc, "policy", "uint64",

View file

@ -97,7 +97,7 @@ static inline unsigned int compute_pf(uint8_t x)
/* misc_helper.c */
void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask);
/* sysemu/svm_helper.c */
/* system/svm_helper.c */
#ifndef CONFIG_USER_ONLY
G_NORETURN void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code,
uint64_t exit_info_1, uintptr_t retaddr);
@ -115,7 +115,7 @@ int exception_has_error_code(int intno);
/* smm_helper.c */
void do_smm_enter(X86CPU *cpu);
/* sysemu/bpt_helper.c */
/* system/bpt_helper.c */
bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update);
/*

View file

@ -1071,7 +1071,11 @@ static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp)
env->pv_features |= BIT(KVM_FEATURE_VIRT_EXTIOI);
}
}
return 0;
}
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return 0;
}

View file

@ -61,6 +61,11 @@ int kvm_arch_irqchip_create(KVMState *s)
return 0;
}
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
CPUMIPSState *env = cpu_env(cs);

View file

@ -479,6 +479,11 @@ static void kvmppc_hw_debug_points_init(CPUPPCState *cenv)
}
}
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);

View file

@ -1472,6 +1472,11 @@ static int kvm_vcpu_enable_sbi_dbcn(RISCVCPU *cpu, CPUState *cs)
return kvm_set_one_reg(cs, kvm_sbi_dbcn.kvm_reg_id, &reg);
}
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
int ret = 0;

View file

@ -398,6 +398,11 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu)
return cpu->cpu_index;
}
int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp)
{
return 0;
}
int kvm_arch_init_vcpu(CPUState *cs)
{
unsigned int max_cpus = MACHINE(qdev_get_machine())->smp.max_cpus;

View file

@ -27,5 +27,5 @@ if host_os == 'linux'
tcg_ss.add(files('perf.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)