mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/bonzini-gitlab/tags/for-upstream' into staging
* SGX implementation for x86 * Miscellaneous bugfixes * Fix dependencies from ROMs to qtests # gpg: Signature made Thu 30 Sep 2021 14:30:35 BST # 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 * remotes/bonzini-gitlab/tags/for-upstream: (33 commits) meson_options.txt: Switch the default value for the vnc option to 'auto' build-sys: add HAVE_IPPROTO_MPTCP memory: Add tracepoint for dirty sync memory: Name all the memory listeners target/i386: Fix memory leak in sev_read_file_base64() tests: qtest: bios-tables-test depends on the unpacked edk2 ROMs meson: unpack edk2 firmware even if --disable-blobs target/i386: Add the query-sgx-capabilities QMP command target/i386: Add HMP and QMP interfaces for SGX docs/system: Add SGX documentation to the system manual sgx-epc: Add the fill_device_info() callback support i440fx: Add support for SGX EPC q35: Add support for SGX EPC i386: acpi: Add SGX EPC entry to ACPI tables i386/pc: Add e820 entry for SGX EPC section(s) hw/i386/pc: Account for SGX EPC sections when calculating device memory hw/i386/fw_cfg: Set SGX bits in feature control fw_cfg accordingly Adjust min CPUID level to 0x12 when SGX is enabled i386: Propagate SGX CPUID sub-leafs to KVM i386: kvm: Add support for exposing PROVISIONKEY to guest ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -295,6 +295,7 @@ static void hvf_region_del(MemoryListener *listener,
|
||||
}
|
||||
|
||||
static MemoryListener hvf_memory_listener = {
|
||||
.name = "hvf",
|
||||
.priority = 10,
|
||||
.region_add = hvf_region_add,
|
||||
.region_del = hvf_region_del,
|
||||
|
||||
+5
-2
@@ -1129,6 +1129,7 @@ static void kvm_coalesce_pio_del(MemoryListener *listener,
|
||||
}
|
||||
|
||||
static MemoryListener kvm_coalesced_pio_listener = {
|
||||
.name = "kvm-coalesced-pio",
|
||||
.coalesced_io_add = kvm_coalesce_pio_add,
|
||||
.coalesced_io_del = kvm_coalesce_pio_del,
|
||||
};
|
||||
@@ -1633,7 +1634,7 @@ static void kvm_io_ioeventfd_del(MemoryListener *listener,
|
||||
}
|
||||
|
||||
void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
AddressSpace *as, int as_id)
|
||||
AddressSpace *as, int as_id, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -1649,6 +1650,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
kml->listener.log_start = kvm_log_start;
|
||||
kml->listener.log_stop = kvm_log_stop;
|
||||
kml->listener.priority = 10;
|
||||
kml->listener.name = name;
|
||||
|
||||
if (s->kvm_dirty_ring_size) {
|
||||
kml->listener.log_sync_global = kvm_log_sync_global;
|
||||
@@ -1669,6 +1671,7 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
|
||||
}
|
||||
|
||||
static MemoryListener kvm_io_listener = {
|
||||
.name = "kvm-io",
|
||||
.eventfd_add = kvm_io_ioeventfd_add,
|
||||
.eventfd_del = kvm_io_ioeventfd_del,
|
||||
.priority = 10,
|
||||
@@ -2579,7 +2582,7 @@ static int kvm_init(MachineState *ms)
|
||||
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
|
||||
|
||||
kvm_memory_listener_register(s, &s->memory_listener,
|
||||
&address_space_memory, 0);
|
||||
&address_space_memory, 0, "kvm-memory");
|
||||
if (kvm_eventfds_allowed) {
|
||||
memory_listener_register(&kvm_io_listener,
|
||||
&address_space_io);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* QEMU host SGX EPC memory backend
|
||||
*
|
||||
* Copyright (C) 2019 Intel Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Sean Christopherson <sean.j.christopherson@intel.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "hw/i386/hostmem-epc.h"
|
||||
|
||||
static void
|
||||
sgx_epc_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
uint32_t ram_flags;
|
||||
char *name;
|
||||
int fd;
|
||||
|
||||
if (!backend->size) {
|
||||
error_setg(errp, "can't create backend with size 0");
|
||||
return;
|
||||
}
|
||||
|
||||
fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
|
||||
if (fd < 0) {
|
||||
error_setg_errno(errp, errno,
|
||||
"failed to open /dev/sgx_vepc to alloc SGX EPC");
|
||||
return;
|
||||
}
|
||||
|
||||
name = object_get_canonical_path(OBJECT(backend));
|
||||
ram_flags = (backend->share ? RAM_SHARED : 0) | RAM_PROTECTED;
|
||||
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
||||
name, backend->size, ram_flags,
|
||||
fd, 0, errp);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
static void sgx_epc_backend_instance_init(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *m = MEMORY_BACKEND(obj);
|
||||
|
||||
m->share = true;
|
||||
m->merge = false;
|
||||
m->dump = false;
|
||||
}
|
||||
|
||||
static void sgx_epc_backend_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc);
|
||||
|
||||
bc->alloc = sgx_epc_backend_memory_alloc;
|
||||
}
|
||||
|
||||
static const TypeInfo sgx_epc_backed_info = {
|
||||
.name = TYPE_MEMORY_BACKEND_EPC,
|
||||
.parent = TYPE_MEMORY_BACKEND,
|
||||
.instance_init = sgx_epc_backend_instance_init,
|
||||
.class_init = sgx_epc_backend_class_init,
|
||||
.instance_size = sizeof(HostMemoryBackendEpc),
|
||||
};
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
|
||||
type_register_static(&sgx_epc_backed_info);
|
||||
}
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -16,5 +16,6 @@ softmmu_ss.add(when: ['CONFIG_VHOST_USER', 'CONFIG_VIRTIO'], if_true: files('vho
|
||||
softmmu_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('cryptodev-vhost.c'))
|
||||
softmmu_ss.add(when: ['CONFIG_VIRTIO_CRYPTO', 'CONFIG_VHOST_CRYPTO'], if_true: files('cryptodev-vhost-user.c'))
|
||||
softmmu_ss.add(when: 'CONFIG_GIO', if_true: [files('dbus-vmstate.c'), gio])
|
||||
softmmu_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c'))
|
||||
|
||||
subdir('tpm')
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#CONFIG_TPM_CRB=n
|
||||
#CONFIG_TPM_TIS_ISA=n
|
||||
#CONFIG_VTD=n
|
||||
#CONFIG_SGX=n
|
||||
|
||||
# Boards:
|
||||
#
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
Software Guard eXtensions (SGX)
|
||||
===============================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Intel Software Guard eXtensions (SGX) is a set of instructions and mechanisms
|
||||
for memory accesses in order to provide security accesses for sensitive
|
||||
applications and data. SGX allows an application to use it's pariticular
|
||||
address space as an *enclave*, which is a protected area provides confidentiality
|
||||
and integrity even in the presence of privileged malware. Accesses to the
|
||||
enclave memory area from any software not resident in the enclave are prevented,
|
||||
including those from privileged software.
|
||||
|
||||
Virtual SGX
|
||||
-----------
|
||||
|
||||
SGX feature is exposed to guest via SGX CPUID. Looking at SGX CPUID, we can
|
||||
report the same CPUID info to guest as on host for most of SGX CPUID. With
|
||||
reporting the same CPUID guest is able to use full capacity of SGX, and KVM
|
||||
doesn't need to emulate those info.
|
||||
|
||||
The guest's EPC base and size are determined by Qemu, and KVM needs Qemu to
|
||||
notify such info to it before it can initialize SGX for guest.
|
||||
|
||||
Virtual EPC
|
||||
~~~~~~~~~~~
|
||||
|
||||
By default, Qemu does not assign EPC to a VM, i.e. fully enabling SGX in a VM
|
||||
requires explicit allocation of EPC to the VM. Similar to other specialized
|
||||
memory types, e.g. hugetlbfs, EPC is exposed as a memory backend.
|
||||
|
||||
SGX EPC is enumerated through CPUID, i.e. EPC "devices" need to be realized
|
||||
prior to realizing the vCPUs themselves, which occurs long before generic
|
||||
devices are parsed and realized. This limitation means that EPC does not
|
||||
require -maxmem as EPC is not treated as {cold,hot}plugged memory.
|
||||
|
||||
Qemu does not artificially restrict the number of EPC sections exposed to a
|
||||
guest, e.g. Qemu will happily allow you to create 64 1M EPC sections. Be aware
|
||||
that some kernels may not recognize all EPC sections, e.g. the Linux SGX driver
|
||||
is hardwired to support only 8 EPC sections.
|
||||
|
||||
The following Qemu snippet creates two EPC sections, with 64M pre-allocated
|
||||
to the VM and an additional 28M mapped but not allocated::
|
||||
|
||||
-object memory-backend-epc,id=mem1,size=64M,prealloc=on \
|
||||
-object memory-backend-epc,id=mem2,size=28M \
|
||||
-M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2
|
||||
|
||||
Note:
|
||||
|
||||
The size and location of the virtual EPC are far less restricted compared
|
||||
to physical EPC. Because physical EPC is protected via range registers,
|
||||
the size of the physical EPC must be a power of two (though software sees
|
||||
a subset of the full EPC, e.g. 92M or 128M) and the EPC must be naturally
|
||||
aligned. KVM SGX's virtual EPC is purely a software construct and only
|
||||
requires the size and location to be page aligned. Qemu enforces the EPC
|
||||
size is a multiple of 4k and will ensure the base of the EPC is 4k aligned.
|
||||
To simplify the implementation, EPC is always located above 4g in the guest
|
||||
physical address space.
|
||||
|
||||
Migration
|
||||
~~~~~~~~~
|
||||
|
||||
Qemu/KVM doesn't prevent live migrating SGX VMs, although from hardware's
|
||||
perspective, SGX doesn't support live migration, since both EPC and the SGX
|
||||
key hierarchy are bound to the physical platform. However live migration
|
||||
can be supported in the sense if guest software stack can support recreating
|
||||
enclaves when it suffers sudden lose of EPC; and if guest enclaves can detect
|
||||
SGX keys being changed, and handle gracefully. For instance, when ERESUME fails
|
||||
with #PF.SGX, guest software can gracefully detect it and recreate enclaves;
|
||||
and when enclave fails to unseal sensitive information from outside, it can
|
||||
detect such error and sensitive information can be provisioned to it again.
|
||||
|
||||
CPUID
|
||||
~~~~~
|
||||
|
||||
Due to its myriad dependencies, SGX is currently not listed as supported
|
||||
in any of Qemu's built-in CPU configuration. To expose SGX (and SGX Launch
|
||||
Control) to a guest, you must either use `-cpu host` to pass-through the
|
||||
host CPU model, or explicitly enable SGX when using a built-in CPU model,
|
||||
e.g. via `-cpu <model>,+sgx` or `-cpu <model>,+sgx,+sgxlc`.
|
||||
|
||||
All SGX sub-features enumerated through CPUID, e.g. SGX2, MISCSELECT,
|
||||
ATTRIBUTES, etc... can be restricted via CPUID flags. Be aware that enforcing
|
||||
restriction of MISCSELECT, ATTRIBUTES and XFRM requires intercepting ECREATE,
|
||||
i.e. may marginally reduce SGX performance in the guest. All SGX sub-features
|
||||
controlled via -cpu are prefixed with "sgx", e.g.::
|
||||
|
||||
$ qemu-system-x86_64 -cpu help | xargs printf "%s\n" | grep sgx
|
||||
sgx
|
||||
sgx-debug
|
||||
sgx-encls-c
|
||||
sgx-enclv
|
||||
sgx-exinfo
|
||||
sgx-kss
|
||||
sgx-mode64
|
||||
sgx-provisionkey
|
||||
sgx-tokenkey
|
||||
sgx1
|
||||
sgx2
|
||||
sgxlc
|
||||
|
||||
The following Qemu snippet passes through the host CPU but restricts access to
|
||||
the provision and EINIT token keys::
|
||||
|
||||
-cpu host,-sgx-provisionkey,-sgx-tokenkey
|
||||
|
||||
SGX sub-features cannot be emulated, i.e. sub-features that are not present
|
||||
in hardware cannot be forced on via '-cpu'.
|
||||
|
||||
Virtualize SGX Launch Control
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Qemu SGX support for Launch Control (LC) is passive, in the sense that it
|
||||
does not actively change the LC configuration. Qemu SGX provides the user
|
||||
the ability to set/clear the CPUID flag (and by extension the associated
|
||||
IA32_FEATURE_CONTROL MSR bit in fw_cfg) and saves/restores the LE Hash MSRs
|
||||
when getting/putting guest state, but Qemu does not add new controls to
|
||||
directly modify the LC configuration. Similar to hardware behavior, locking
|
||||
the LC configuration to a non-Intel value is left to guest firmware. Unlike
|
||||
host bios setting for SGX launch control(LC), there is no special bios setting
|
||||
for SGX guest by our design. If host is in locked mode, we can still allow
|
||||
creating VM with SGX.
|
||||
|
||||
Feature Control
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Qemu SGX updates the `etc/msr_feature_control` fw_cfg entry to set the SGX
|
||||
(bit 18) and SGX LC (bit 17) flags based on their respective CPUID support,
|
||||
i.e. existing guest firmware will automatically set SGX and SGX LC accordingly,
|
||||
assuming said firmware supports fw_cfg.msr_feature_control.
|
||||
|
||||
Launching a guest
|
||||
-----------------
|
||||
|
||||
To launch a SGX guest:
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
|qemu_system_x86| \\
|
||||
-cpu host,+sgx-provisionkey \\
|
||||
-object memory-backend-epc,id=mem1,size=64M,prealloc=on \\
|
||||
-object memory-backend-epc,id=mem2,size=28M \\
|
||||
-M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2
|
||||
|
||||
Utilizing SGX in the guest requires a kernel/OS with SGX support.
|
||||
The support can be determined in guest by::
|
||||
|
||||
$ grep sgx /proc/cpuinfo
|
||||
|
||||
and SGX epc info by::
|
||||
|
||||
$ dmesg | grep sgx
|
||||
[ 1.242142] sgx: EPC section 0x180000000-0x181bfffff
|
||||
[ 1.242319] sgx: EPC section 0x181c00000-0x1837fffff
|
||||
|
||||
References
|
||||
----------
|
||||
|
||||
- `SGX Homepage <https://software.intel.com/sgx>`__
|
||||
|
||||
- `SGX SDK <https://github.com/intel/linux-sgx.git>`__
|
||||
|
||||
- SGX specification: Intel SDM Volume 3
|
||||
@@ -26,6 +26,7 @@ Architectural features
|
||||
:maxdepth: 1
|
||||
|
||||
i386/cpu
|
||||
i386/sgx
|
||||
|
||||
.. _pcsys_005freq:
|
||||
|
||||
|
||||
@@ -877,3 +877,18 @@ SRST
|
||||
``info dirty_rate``
|
||||
Display the vcpu dirty rate information.
|
||||
ERST
|
||||
|
||||
#if defined(TARGET_I386)
|
||||
{
|
||||
.name = "sgx",
|
||||
.args_type = "",
|
||||
.params = "",
|
||||
.help = "show intel SGX information",
|
||||
.cmd = hmp_info_sgx,
|
||||
},
|
||||
#endif
|
||||
|
||||
SRST
|
||||
``info sgx``
|
||||
Show intel SGX information.
|
||||
ERST
|
||||
|
||||
@@ -6,6 +6,10 @@ config SEV
|
||||
select X86_FW_OVMF
|
||||
depends on KVM
|
||||
|
||||
config SGX
|
||||
bool
|
||||
depends on KVM
|
||||
|
||||
config PC
|
||||
bool
|
||||
imply APPLESMC
|
||||
@@ -21,6 +25,7 @@ config PC
|
||||
imply PVPANIC_ISA
|
||||
imply QXL
|
||||
imply SEV
|
||||
imply SGX
|
||||
imply SGA
|
||||
imply TEST_DEVICES
|
||||
imply TPM_CRB
|
||||
|
||||
@@ -1841,6 +1841,28 @@ build_dsdt(GArray *table_data, BIOSLinker *linker,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (pcms->sgx_epc.size != 0) {
|
||||
uint64_t epc_base = pcms->sgx_epc.base;
|
||||
uint64_t epc_size = pcms->sgx_epc.size;
|
||||
|
||||
dev = aml_device("EPC");
|
||||
aml_append(dev, aml_name_decl("_HID", aml_eisaid("INT0E0C")));
|
||||
aml_append(dev, aml_name_decl("_STR",
|
||||
aml_unicode("Enclave Page Cache 1.0")));
|
||||
crs = aml_resource_template();
|
||||
aml_append(crs,
|
||||
aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED,
|
||||
AML_MAX_FIXED, AML_NON_CACHEABLE,
|
||||
AML_READ_WRITE, 0, epc_base,
|
||||
epc_base + epc_size - 1, 0, epc_size));
|
||||
aml_append(dev, aml_name_decl("_CRS", crs));
|
||||
|
||||
method = aml_method("_STA", 0, AML_NOTSERIALIZED);
|
||||
aml_append(method, aml_return(aml_int(0x0f)));
|
||||
aml_append(dev, method);
|
||||
|
||||
aml_append(sb_scope, dev);
|
||||
}
|
||||
aml_append(dsdt, sb_scope);
|
||||
|
||||
/* copy AML table into ACPI tables blob and patch header there */
|
||||
|
||||
+11
-1
@@ -159,7 +159,7 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg)
|
||||
{
|
||||
X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu);
|
||||
CPUX86State *env = &cpu->env;
|
||||
uint32_t unused, ecx, edx;
|
||||
uint32_t unused, ebx, ecx, edx;
|
||||
uint64_t feature_control_bits = 0;
|
||||
uint64_t *val;
|
||||
|
||||
@@ -174,6 +174,16 @@ void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg)
|
||||
feature_control_bits |= FEATURE_CONTROL_LMCE;
|
||||
}
|
||||
|
||||
if (env->cpuid_level >= 7) {
|
||||
cpu_x86_cpuid(env, 0x7, 0, &unused, &ebx, &ecx, &unused);
|
||||
if (ebx & CPUID_7_0_EBX_SGX) {
|
||||
feature_control_bits |= FEATURE_CONTROL_SGX;
|
||||
}
|
||||
if (ecx & CPUID_7_0_ECX_SGX_LC) {
|
||||
feature_control_bits |= FEATURE_CONTROL_SGX_LC;
|
||||
}
|
||||
}
|
||||
|
||||
if (!feature_control_bits) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c'))
|
||||
i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c'))
|
||||
i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c'))
|
||||
i386_ss.add(when: 'CONFIG_VTD', if_true: files('intel_iommu.c'))
|
||||
i386_ss.add(when: 'CONFIG_SGX', if_true: files('sgx-epc.c','sgx.c'),
|
||||
if_false: files('sgx-stub.c'))
|
||||
|
||||
i386_ss.add(when: 'CONFIG_ACPI', if_true: files('acpi-common.c'))
|
||||
i386_ss.add(when: 'CONFIG_ACPI_HW_REDUCED', if_true: files('generic_event_device_x86.c'))
|
||||
|
||||
+14
-1
@@ -889,6 +889,10 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM);
|
||||
}
|
||||
|
||||
if (pcms->sgx_epc.size != 0) {
|
||||
e820_add_entry(pcms->sgx_epc.base, pcms->sgx_epc.size, E820_RESERVED);
|
||||
}
|
||||
|
||||
if (!pcmc->has_reserved_memory &&
|
||||
(machine->ram_slots ||
|
||||
(machine->maxram_size > machine->ram_size))) {
|
||||
@@ -919,8 +923,15 @@ void pc_memory_init(PCMachineState *pcms,
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (pcms->sgx_epc.size != 0) {
|
||||
machine->device_memory->base = sgx_epc_above_4g_end(&pcms->sgx_epc);
|
||||
} else {
|
||||
machine->device_memory->base =
|
||||
0x100000000ULL + x86ms->above_4g_mem_size;
|
||||
}
|
||||
|
||||
machine->device_memory->base =
|
||||
ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB);
|
||||
ROUND_UP(machine->device_memory->base, 1 * GiB);
|
||||
|
||||
if (pcmc->enforce_aligned_dimm) {
|
||||
/* size device region assuming 1G page max alignment per slot */
|
||||
@@ -1005,6 +1016,8 @@ uint64_t pc_pci_hole64_start(void)
|
||||
if (!pcmc->broken_reserved_end) {
|
||||
hole64_start += memory_region_size(&ms->device_memory->mr);
|
||||
}
|
||||
} else if (pcms->sgx_epc.size != 0) {
|
||||
hole64_start = sgx_epc_above_4g_end(&pcms->sgx_epc);
|
||||
} else {
|
||||
hole64_start = 0x100000000ULL + x86ms->above_4g_mem_size;
|
||||
}
|
||||
|
||||
@@ -153,6 +153,7 @@ static void pc_init1(MachineState *machine,
|
||||
}
|
||||
}
|
||||
|
||||
pc_machine_init_sgx_epc(pcms);
|
||||
x86_cpus_init(x86ms, pcmc->default_cpu_version);
|
||||
|
||||
if (pcmc->kvmclock_enabled) {
|
||||
|
||||
@@ -177,6 +177,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
x86ms->below_4g_mem_size = machine->ram_size;
|
||||
}
|
||||
|
||||
pc_machine_init_sgx_epc(pcms);
|
||||
x86_cpus_init(x86ms, pcmc->default_cpu_version);
|
||||
|
||||
kvmclock_create(pcmc->kvmclock_create_always);
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* SGX EPC device
|
||||
*
|
||||
* Copyright (C) 2019 Intel Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Sean Christopherson <sean.j.christopherson@intel.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/sgx-epc.h"
|
||||
#include "hw/mem/memory-device.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "target/i386/cpu.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
static Property sgx_epc_properties[] = {
|
||||
DEFINE_PROP_UINT64(SGX_EPC_ADDR_PROP, SGXEPCDevice, addr, 0),
|
||||
DEFINE_PROP_LINK(SGX_EPC_MEMDEV_PROP, SGXEPCDevice, hostmem,
|
||||
TYPE_MEMORY_BACKEND_EPC, HostMemoryBackendEpc *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void sgx_epc_get_size(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
uint64_t value;
|
||||
|
||||
value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
visit_type_uint64(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void sgx_epc_init(Object *obj)
|
||||
{
|
||||
object_property_add(obj, SGX_EPC_SIZE_PROP, "uint64", sgx_epc_get_size,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void sgx_epc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
X86MachineState *x86ms = X86_MACHINE(pcms);
|
||||
MemoryDeviceState *md = MEMORY_DEVICE(dev);
|
||||
SGXEPCState *sgx_epc = &pcms->sgx_epc;
|
||||
SGXEPCDevice *epc = SGX_EPC(dev);
|
||||
HostMemoryBackend *hostmem;
|
||||
const char *path;
|
||||
|
||||
if (x86ms->boot_cpus != 0) {
|
||||
error_setg(errp, "'" TYPE_SGX_EPC "' can't be created after vCPUs,"
|
||||
"e.g. via -device");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!epc->hostmem) {
|
||||
error_setg(errp, "'" SGX_EPC_MEMDEV_PROP "' property is not set");
|
||||
return;
|
||||
}
|
||||
hostmem = MEMORY_BACKEND(epc->hostmem);
|
||||
if (host_memory_backend_is_mapped(hostmem)) {
|
||||
path = object_get_canonical_path_component(OBJECT(hostmem));
|
||||
error_setg(errp, "can't use already busy memdev: %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
epc->addr = sgx_epc->base + sgx_epc->size;
|
||||
|
||||
memory_region_add_subregion(&sgx_epc->mr, epc->addr - sgx_epc->base,
|
||||
host_memory_backend_get_memory(hostmem));
|
||||
|
||||
host_memory_backend_set_mapped(hostmem, true);
|
||||
|
||||
sgx_epc->sections = g_renew(SGXEPCDevice *, sgx_epc->sections,
|
||||
sgx_epc->nr_sections + 1);
|
||||
sgx_epc->sections[sgx_epc->nr_sections++] = epc;
|
||||
|
||||
sgx_epc->size += memory_device_get_region_size(md, errp);
|
||||
}
|
||||
|
||||
static void sgx_epc_unrealize(DeviceState *dev)
|
||||
{
|
||||
SGXEPCDevice *epc = SGX_EPC(dev);
|
||||
HostMemoryBackend *hostmem = MEMORY_BACKEND(epc->hostmem);
|
||||
|
||||
host_memory_backend_set_mapped(hostmem, false);
|
||||
}
|
||||
|
||||
static uint64_t sgx_epc_md_get_addr(const MemoryDeviceState *md)
|
||||
{
|
||||
const SGXEPCDevice *epc = SGX_EPC(md);
|
||||
|
||||
return epc->addr;
|
||||
}
|
||||
|
||||
static void sgx_epc_md_set_addr(MemoryDeviceState *md, uint64_t addr,
|
||||
Error **errp)
|
||||
{
|
||||
object_property_set_uint(OBJECT(md), SGX_EPC_ADDR_PROP, addr, errp);
|
||||
}
|
||||
|
||||
static uint64_t sgx_epc_md_get_plugged_size(const MemoryDeviceState *md,
|
||||
Error **errp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static MemoryRegion *sgx_epc_md_get_memory_region(MemoryDeviceState *md,
|
||||
Error **errp)
|
||||
{
|
||||
SGXEPCDevice *epc = SGX_EPC(md);
|
||||
HostMemoryBackend *hostmem;
|
||||
|
||||
if (!epc->hostmem) {
|
||||
error_setg(errp, "'" SGX_EPC_MEMDEV_PROP "' property must be set");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hostmem = MEMORY_BACKEND(epc->hostmem);
|
||||
return host_memory_backend_get_memory(hostmem);
|
||||
}
|
||||
|
||||
static void sgx_epc_md_fill_device_info(const MemoryDeviceState *md,
|
||||
MemoryDeviceInfo *info)
|
||||
{
|
||||
SgxEPCDeviceInfo *se = g_new0(SgxEPCDeviceInfo, 1);
|
||||
SGXEPCDevice *epc = SGX_EPC(md);
|
||||
|
||||
se->memaddr = epc->addr;
|
||||
se->size = object_property_get_uint(OBJECT(epc), SGX_EPC_SIZE_PROP,
|
||||
NULL);
|
||||
se->memdev = object_get_canonical_path(OBJECT(epc->hostmem));
|
||||
|
||||
info->u.sgx_epc.data = se;
|
||||
info->type = MEMORY_DEVICE_INFO_KIND_SGX_EPC;
|
||||
}
|
||||
|
||||
static void sgx_epc_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
|
||||
|
||||
dc->hotpluggable = false;
|
||||
dc->realize = sgx_epc_realize;
|
||||
dc->unrealize = sgx_epc_unrealize;
|
||||
dc->desc = "SGX EPC section";
|
||||
device_class_set_props(dc, sgx_epc_properties);
|
||||
|
||||
mdc->get_addr = sgx_epc_md_get_addr;
|
||||
mdc->set_addr = sgx_epc_md_set_addr;
|
||||
mdc->get_plugged_size = sgx_epc_md_get_plugged_size;
|
||||
mdc->get_memory_region = sgx_epc_md_get_memory_region;
|
||||
mdc->fill_device_info = sgx_epc_md_fill_device_info;
|
||||
}
|
||||
|
||||
static TypeInfo sgx_epc_info = {
|
||||
.name = TYPE_SGX_EPC,
|
||||
.parent = TYPE_DEVICE,
|
||||
.instance_size = sizeof(SGXEPCDevice),
|
||||
.instance_init = sgx_epc_init,
|
||||
.class_init = sgx_epc_class_init,
|
||||
.class_size = sizeof(DeviceClass),
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_MEMORY_DEVICE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void sgx_epc_register_types(void)
|
||||
{
|
||||
type_register_static(&sgx_epc_info);
|
||||
}
|
||||
|
||||
type_init(sgx_epc_register_types)
|
||||
@@ -0,0 +1,26 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/sgx-epc.h"
|
||||
#include "hw/i386/sgx.h"
|
||||
|
||||
SGXInfo *sgx_get_info(Error **errp)
|
||||
{
|
||||
error_setg(errp, "SGX support is not compiled in");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SGXInfo *sgx_get_capabilities(Error **errp)
|
||||
{
|
||||
error_setg(errp, "SGX support is not compiled in");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pc_machine_init_sgx_epc(PCMachineState *pcms)
|
||||
{
|
||||
memset(&pcms->sgx_epc, 0, sizeof(SGXEPCState));
|
||||
}
|
||||
|
||||
int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* SGX common code
|
||||
*
|
||||
* Copyright (C) 2021 Intel Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Yang Zhong<yang.zhong@intel.com>
|
||||
* Sean Christopherson <sean.j.christopherson@intel.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/sgx-epc.h"
|
||||
#include "hw/mem/memory-device.h"
|
||||
#include "monitor/qdev.h"
|
||||
#include "qapi/error.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/i386/sgx.h"
|
||||
#include "sysemu/hw_accel.h"
|
||||
|
||||
#define SGX_MAX_EPC_SECTIONS 8
|
||||
#define SGX_CPUID_EPC_INVALID 0x0
|
||||
|
||||
/* A valid EPC section. */
|
||||
#define SGX_CPUID_EPC_SECTION 0x1
|
||||
#define SGX_CPUID_EPC_MASK 0xF
|
||||
|
||||
static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
|
||||
{
|
||||
return (low & MAKE_64BIT_MASK(12, 20)) +
|
||||
((high & MAKE_64BIT_MASK(0, 20)) << 32);
|
||||
}
|
||||
|
||||
static uint64_t sgx_calc_host_epc_section_size(void)
|
||||
{
|
||||
uint32_t i, type;
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
uint64_t size = 0;
|
||||
|
||||
for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
|
||||
host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
type = eax & SGX_CPUID_EPC_MASK;
|
||||
if (type == SGX_CPUID_EPC_INVALID) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != SGX_CPUID_EPC_SECTION) {
|
||||
break;
|
||||
}
|
||||
|
||||
size += sgx_calc_section_metric(ecx, edx);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
SGXInfo *sgx_get_capabilities(Error **errp)
|
||||
{
|
||||
SGXInfo *info = NULL;
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
|
||||
if (fd < 0) {
|
||||
error_setg(errp, "SGX is not enabled in KVM");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = g_new0(SGXInfo, 1);
|
||||
host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
info->sgx = ebx & (1U << 2) ? true : false;
|
||||
info->flc = ecx & (1U << 30) ? true : false;
|
||||
|
||||
host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
|
||||
info->sgx1 = eax & (1U << 0) ? true : false;
|
||||
info->sgx2 = eax & (1U << 1) ? true : false;
|
||||
|
||||
info->section_size = sgx_calc_host_epc_section_size();
|
||||
|
||||
close(fd);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
SGXInfo *sgx_get_info(Error **errp)
|
||||
{
|
||||
SGXInfo *info = NULL;
|
||||
X86MachineState *x86ms;
|
||||
PCMachineState *pcms =
|
||||
(PCMachineState *)object_dynamic_cast(qdev_get_machine(),
|
||||
TYPE_PC_MACHINE);
|
||||
if (!pcms) {
|
||||
error_setg(errp, "SGX is only supported on PC machines");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
x86ms = X86_MACHINE(pcms);
|
||||
if (!x86ms->sgx_epc_list) {
|
||||
error_setg(errp, "No EPC regions defined, SGX not available");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SGXEPCState *sgx_epc = &pcms->sgx_epc;
|
||||
info = g_new0(SGXInfo, 1);
|
||||
|
||||
info->sgx = true;
|
||||
info->sgx1 = true;
|
||||
info->sgx2 = true;
|
||||
info->flc = true;
|
||||
info->section_size = sgx_epc->size;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
|
||||
{
|
||||
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
|
||||
SGXEPCDevice *epc;
|
||||
|
||||
if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
epc = pcms->sgx_epc.sections[section_nr];
|
||||
|
||||
*addr = epc->addr;
|
||||
*size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pc_machine_init_sgx_epc(PCMachineState *pcms)
|
||||
{
|
||||
SGXEPCState *sgx_epc = &pcms->sgx_epc;
|
||||
X86MachineState *x86ms = X86_MACHINE(pcms);
|
||||
SgxEPCList *list = NULL;
|
||||
Object *obj;
|
||||
|
||||
memset(sgx_epc, 0, sizeof(SGXEPCState));
|
||||
if (!x86ms->sgx_epc_list) {
|
||||
return;
|
||||
}
|
||||
|
||||
sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size;
|
||||
|
||||
memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
|
||||
memory_region_add_subregion(get_system_memory(), sgx_epc->base,
|
||||
&sgx_epc->mr);
|
||||
|
||||
for (list = x86ms->sgx_epc_list; list; list = list->next) {
|
||||
obj = object_new("sgx-epc");
|
||||
|
||||
/* set the memdev link with memory backend */
|
||||
object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
|
||||
&error_fatal);
|
||||
object_property_set_bool(obj, "realized", true, &error_fatal);
|
||||
object_unref(obj);
|
||||
}
|
||||
|
||||
if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
|
||||
error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap",
|
||||
sgx_epc->size);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
|
||||
}
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qapi-visit-common.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-machine.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "sysemu/whpx.h"
|
||||
@@ -1263,6 +1265,27 @@ static void x86_machine_set_bus_lock_ratelimit(Object *obj, Visitor *v,
|
||||
visit_type_uint64(v, name, &x86ms->bus_lock_ratelimit, errp);
|
||||
}
|
||||
|
||||
static void machine_get_sgx_epc(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
X86MachineState *x86ms = X86_MACHINE(obj);
|
||||
SgxEPCList *list = x86ms->sgx_epc_list;
|
||||
|
||||
visit_type_SgxEPCList(v, name, &list, errp);
|
||||
}
|
||||
|
||||
static void machine_set_sgx_epc(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
X86MachineState *x86ms = X86_MACHINE(obj);
|
||||
SgxEPCList *list;
|
||||
|
||||
list = x86ms->sgx_epc_list;
|
||||
visit_type_SgxEPCList(v, name, &x86ms->sgx_epc_list, errp);
|
||||
|
||||
qapi_free_SgxEPCList(list);
|
||||
}
|
||||
|
||||
static void x86_machine_initfn(Object *obj)
|
||||
{
|
||||
X86MachineState *x86ms = X86_MACHINE(obj);
|
||||
@@ -1322,6 +1345,12 @@ static void x86_machine_class_init(ObjectClass *oc, void *data)
|
||||
x86_machine_set_bus_lock_ratelimit, NULL, NULL);
|
||||
object_class_property_set_description(oc, X86_MACHINE_BUS_LOCK_RATELIMIT,
|
||||
"Set the ratelimit for the bus locks acquired in VMs");
|
||||
|
||||
object_class_property_add(oc, "sgx-epc", "SgxEPC",
|
||||
machine_get_sgx_epc, machine_set_sgx_epc,
|
||||
NULL, NULL);
|
||||
object_class_property_set_description(oc, "sgx-epc",
|
||||
"SGX EPC device");
|
||||
}
|
||||
|
||||
static const TypeInfo x86_machine_info = {
|
||||
|
||||
@@ -721,6 +721,7 @@ static void xen_log_global_stop(MemoryListener *listener)
|
||||
}
|
||||
|
||||
static MemoryListener xen_memory_listener = {
|
||||
.name = "xen-memory",
|
||||
.region_add = xen_region_add,
|
||||
.region_del = xen_region_del,
|
||||
.log_start = xen_log_start,
|
||||
@@ -732,6 +733,7 @@ static MemoryListener xen_memory_listener = {
|
||||
};
|
||||
|
||||
static MemoryListener xen_io_listener = {
|
||||
.name = "xen-io",
|
||||
.region_add = xen_io_add,
|
||||
.region_del = xen_io_del,
|
||||
.priority = 10,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user