* SGX implementation for x86
* Miscellaneous bugfixes * Fix dependencies from ROMs to qtests -----BEGIN PGP SIGNATURE----- iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmFVu/sUHHBib256aW5p QHJlZGhhdC5jb20ACgkQv/vSX3jHroNFUgf+OexjKqJw4qzbDdQrxWqw3upoFblk y4OrmrhCyCKDwPghnjHUEVGHnNKqKpCLoIvtvFZ7xX/qezpMtZxVUliOVNQGmioR MZU/DbdlvVL/t8yKjfz1ljshk55hnSJ7rAv8LBA+B3uNzyJ+LZU9+Kbvmei5oyex nenCtXnoVNBJMvTBE/KfJbp0UasEb1OTvPBa0Y7mHyDub28FDPKr9WZbloCLUtE+ uXwbZ34VRDsxbLnXh+BJ+ljOQLdsJErAkiPKTnW1/3W8Ti7PzOzvLpbSIVdBv/9A U1qOEm48BjCrG/tFJvTUm0ZM7AHmqYfvmwpenDpL0FhReohMdUa3pycQ9g== =Hicy -----END PGP SIGNATURE----- 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:
		
						commit
						0021c4765a
					
				| @ -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, | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
							
								
								
									
										82
									
								
								backends/hostmem-epc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								backends/hostmem-epc.c
									
									
									
									
									
										Normal file
									
								
							| @ -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:
 | ||||
| #
 | ||||
|  | ||||
							
								
								
									
										165
									
								
								docs/system/i386/sgx.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								docs/system/i386/sgx.rst
									
									
									
									
									
										Normal file
									
								
							| @ -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 */ | ||||
|  | ||||
| @ -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')) | ||||
|  | ||||
							
								
								
									
										15
									
								
								hw/i386/pc.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								hw/i386/pc.c
									
									
									
									
									
								
							| @ -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); | ||||
|  | ||||
							
								
								
									
										184
									
								
								hw/i386/sgx-epc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								hw/i386/sgx-epc.c
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||
							
								
								
									
										26
									
								
								hw/i386/sgx-stub.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								hw/i386/sgx-stub.c
									
									
									
									
									
										Normal file
									
								
							| @ -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
									
								
								hw/i386/sgx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										170
									
								
								hw/i386/sgx.c
									
									
									
									
									
										Normal file
									
								
							| @ -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, | ||||
|  | ||||
| @ -234,6 +234,7 @@ static void kvm_openpic_realize(DeviceState *dev, Error **errp) | ||||
| 
 | ||||
|     opp->mem_listener.region_add = kvm_openpic_region_add; | ||||
|     opp->mem_listener.region_del = kvm_openpic_region_del; | ||||
|     opp->mem_listener.name = "openpic-kvm"; | ||||
|     memory_listener_register(&opp->mem_listener, &address_space_memory); | ||||
| 
 | ||||
|     /* indicate pic capabilities */ | ||||
|  | ||||
| @ -219,6 +219,7 @@ void proxy_memory_listener_configure(ProxyMemoryListener *proxy_listener, | ||||
|     proxy_listener->listener.region_add = proxy_memory_listener_region_addnop; | ||||
|     proxy_listener->listener.region_nop = proxy_memory_listener_region_addnop; | ||||
|     proxy_listener->listener.priority = 10; | ||||
|     proxy_listener->listener.name = "proxy"; | ||||
| 
 | ||||
|     memory_listener_register(&proxy_listener->listener, | ||||
|                              &address_space_memory); | ||||
|  | ||||
| @ -562,6 +562,7 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) | ||||
| { | ||||
|     return (!memory_region_is_ram(section->mr) && | ||||
|             !memory_region_is_iommu(section->mr)) || | ||||
|            memory_region_is_protected(section->mr) || | ||||
|            /*
 | ||||
|             * Sizing an enabled 64-bit BAR can cause spurious mappings to | ||||
|             * addresses in the upper part of the 64-bit address space.  These | ||||
| @ -1434,6 +1435,7 @@ static void vfio_listener_log_sync(MemoryListener *listener, | ||||
| } | ||||
| 
 | ||||
| static const MemoryListener vfio_memory_listener = { | ||||
|     .name = "vfio", | ||||
|     .region_add = vfio_listener_region_add, | ||||
|     .region_del = vfio_listener_region_del, | ||||
|     .log_global_start = vfio_listener_log_global_start, | ||||
|  | ||||
| @ -136,6 +136,7 @@ static void vfio_prereg_listener_region_del(MemoryListener *listener, | ||||
| } | ||||
| 
 | ||||
| const MemoryListener vfio_prereg_listener = { | ||||
|     .name = "vfio-pre-reg", | ||||
|     .region_add = vfio_prereg_listener_region_add, | ||||
|     .region_del = vfio_prereg_listener_region_del, | ||||
| }; | ||||
|  | ||||
| @ -246,6 +246,7 @@ static void vhost_vdpa_listener_region_del(MemoryListener *listener, | ||||
|  * depends on the addnop(). | ||||
|  */ | ||||
| static const MemoryListener vhost_vdpa_memory_listener = { | ||||
|     .name = "vhost-vdpa", | ||||
|     .commit = vhost_vdpa_listener_commit, | ||||
|     .region_add = vhost_vdpa_listener_region_add, | ||||
|     .region_del = vhost_vdpa_listener_region_del, | ||||
|  | ||||
| @ -1366,6 +1366,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, | ||||
|     hdev->features = features; | ||||
| 
 | ||||
|     hdev->memory_listener = (MemoryListener) { | ||||
|         .name = "vhost", | ||||
|         .begin = vhost_begin, | ||||
|         .commit = vhost_commit, | ||||
|         .region_add = vhost_region_addnop, | ||||
| @ -1381,6 +1382,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, | ||||
|     }; | ||||
| 
 | ||||
|     hdev->iommu_listener = (MemoryListener) { | ||||
|         .name = "vhost-iommu", | ||||
|         .region_add = vhost_iommu_region_add, | ||||
|         .region_del = vhost_iommu_region_del, | ||||
|     }; | ||||
|  | ||||
| @ -3670,6 +3670,7 @@ static void virtio_device_realize(DeviceState *dev, Error **errp) | ||||
|     } | ||||
| 
 | ||||
|     vdev->listener.commit = virtio_memory_listener_commit; | ||||
|     vdev->listener.name = "virtio"; | ||||
|     memory_listener_register(&vdev->listener, vdev->dma_as); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -689,12 +689,14 @@ static void xen_pt_io_region_del(MemoryListener *l, MemoryRegionSection *sec) | ||||
| } | ||||
| 
 | ||||
| static const MemoryListener xen_pt_memory_listener = { | ||||
|     .name = "xen-pt-mem", | ||||
|     .region_add = xen_pt_region_add, | ||||
|     .region_del = xen_pt_region_del, | ||||
|     .priority = 10, | ||||
| }; | ||||
| 
 | ||||
| static const MemoryListener xen_pt_io_listener = { | ||||
|     .name = "xen-pt-io", | ||||
|     .region_add = xen_pt_io_region_add, | ||||
|     .region_del = xen_pt_io_region_del, | ||||
|     .priority = 10, | ||||
|  | ||||
| @ -190,6 +190,9 @@ typedef struct IOMMUTLBEvent { | ||||
|  */ | ||||
| #define RAM_NORESERVE (1 << 7) | ||||
| 
 | ||||
| /* RAM that isn't accessible through normal means. */ | ||||
| #define RAM_PROTECTED (1 << 8) | ||||
| 
 | ||||
| static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, | ||||
|                                        IOMMUNotifierFlag flags, | ||||
|                                        hwaddr start, hwaddr end, | ||||
| @ -979,6 +982,14 @@ struct MemoryListener { | ||||
|      */ | ||||
|     unsigned priority; | ||||
| 
 | ||||
|     /**
 | ||||
|      * @name: | ||||
|      * | ||||
|      * Name of the listener.  It can be used in contexts where we'd like to | ||||
|      * identify one memory listener with the rest. | ||||
|      */ | ||||
|     const char *name; | ||||
| 
 | ||||
|     /* private: */ | ||||
|     AddressSpace *address_space; | ||||
|     QTAILQ_ENTRY(MemoryListener) link; | ||||
| @ -1267,7 +1278,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, | ||||
|  * @name: the name of the region. | ||||
|  * @size: size of the region. | ||||
|  * @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM, | ||||
|  *             RAM_NORESERVE. | ||||
|  *             RAM_NORESERVE, RAM_PROTECTED. | ||||
|  * @fd: the fd to mmap. | ||||
|  * @offset: offset within the file referenced by fd | ||||
|  * @errp: pointer to Error*, to store an error if it happens. | ||||
| @ -1568,6 +1579,16 @@ static inline bool memory_region_is_romd(MemoryRegion *mr) | ||||
|     return mr->rom_device && mr->romd_mode; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * memory_region_is_protected: check whether a memory region is protected | ||||
|  * | ||||
|  * Returns %true if a memory region is protected RAM and cannot be accessed | ||||
|  * via standard mechanisms, e.g. DMA. | ||||
|  * | ||||
|  * @mr: the memory region being queried | ||||
|  */ | ||||
| bool memory_region_is_protected(MemoryRegion *mr); | ||||
| 
 | ||||
| /**
 | ||||
|  * memory_region_get_iommu: check whether a memory region is an iommu | ||||
|  * | ||||
|  | ||||
							
								
								
									
										28
									
								
								include/hw/i386/hostmem-epc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								include/hw/i386/hostmem-epc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| /*
 | ||||
|  * SGX EPC 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. | ||||
|  */ | ||||
| #ifndef QEMU_HOSTMEM_EPC_H | ||||
| #define QEMU_HOSTMEM_EPC_H | ||||
| 
 | ||||
| #include "sysemu/hostmem.h" | ||||
| 
 | ||||
| #define TYPE_MEMORY_BACKEND_EPC "memory-backend-epc" | ||||
| 
 | ||||
| #define MEMORY_BACKEND_EPC(obj)                                        \ | ||||
|     OBJECT_CHECK(HostMemoryBackendEpc, (obj), TYPE_MEMORY_BACKEND_EPC) | ||||
| 
 | ||||
| typedef struct HostMemoryBackendEpc HostMemoryBackendEpc; | ||||
| 
 | ||||
| struct HostMemoryBackendEpc { | ||||
|     HostMemoryBackend parent_obj; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
| @ -12,6 +12,7 @@ | ||||
| #include "hw/acpi/acpi_dev_interface.h" | ||||
| #include "hw/hotplug.h" | ||||
| #include "qom/object.h" | ||||
| #include "hw/i386/sgx-epc.h" | ||||
| 
 | ||||
| #define HPET_INTCAP "hpet-intcap" | ||||
| 
 | ||||
| @ -49,6 +50,8 @@ typedef struct PCMachineState { | ||||
| 
 | ||||
|     /* ACPI Memory hotplug IO base address */ | ||||
|     hwaddr memhp_io_base; | ||||
| 
 | ||||
|     SGXEPCState sgx_epc; | ||||
| } PCMachineState; | ||||
| 
 | ||||
| #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" | ||||
| @ -192,6 +195,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); | ||||
| void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, | ||||
|                        const CPUArchIdList *apic_ids, GArray *entry); | ||||
| 
 | ||||
| /* sgx.c */ | ||||
| void pc_machine_init_sgx_epc(PCMachineState *pcms); | ||||
| 
 | ||||
| extern GlobalProperty pc_compat_6_1[]; | ||||
| extern const size_t pc_compat_6_1_len; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										67
									
								
								include/hw/i386/sgx-epc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								include/hw/i386/sgx-epc.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| /*
 | ||||
|  * 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. | ||||
|  */ | ||||
| #ifndef QEMU_SGX_EPC_H | ||||
| #define QEMU_SGX_EPC_H | ||||
| 
 | ||||
| #include "hw/i386/hostmem-epc.h" | ||||
| 
 | ||||
| #define TYPE_SGX_EPC "sgx-epc" | ||||
| #define SGX_EPC(obj) \ | ||||
|     OBJECT_CHECK(SGXEPCDevice, (obj), TYPE_SGX_EPC) | ||||
| #define SGX_EPC_CLASS(oc) \ | ||||
|     OBJECT_CLASS_CHECK(SGXEPCDeviceClass, (oc), TYPE_SGX_EPC) | ||||
| #define SGX_EPC_GET_CLASS(obj) \ | ||||
|     OBJECT_GET_CLASS(SGXEPCDeviceClass, (obj), TYPE_SGX_EPC) | ||||
| 
 | ||||
| #define SGX_EPC_ADDR_PROP "addr" | ||||
| #define SGX_EPC_SIZE_PROP "size" | ||||
| #define SGX_EPC_MEMDEV_PROP "memdev" | ||||
| 
 | ||||
| /**
 | ||||
|  * SGXEPCDevice: | ||||
|  * @addr: starting guest physical address, where @SGXEPCDevice is mapped. | ||||
|  *         Default value: 0, means that address is auto-allocated. | ||||
|  * @hostmem: host memory backend providing memory for @SGXEPCDevice | ||||
|  */ | ||||
| typedef struct SGXEPCDevice { | ||||
|     /* private */ | ||||
|     DeviceState parent_obj; | ||||
| 
 | ||||
|     /* public */ | ||||
|     uint64_t addr; | ||||
|     HostMemoryBackendEpc *hostmem; | ||||
| } SGXEPCDevice; | ||||
| 
 | ||||
| /*
 | ||||
|  * @base: address in guest physical address space where EPC regions start | ||||
|  * @mr: address space container for memory devices | ||||
|  */ | ||||
| typedef struct SGXEPCState { | ||||
|     uint64_t base; | ||||
|     uint64_t size; | ||||
| 
 | ||||
|     MemoryRegion mr; | ||||
| 
 | ||||
|     struct SGXEPCDevice **sections; | ||||
|     int nr_sections; | ||||
| } SGXEPCState; | ||||
| 
 | ||||
| int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size); | ||||
| 
 | ||||
| static inline uint64_t sgx_epc_above_4g_end(SGXEPCState *sgx_epc) | ||||
| { | ||||
|     assert(sgx_epc != NULL && sgx_epc->base >= 0x100000000ULL); | ||||
| 
 | ||||
|     return sgx_epc->base + sgx_epc->size; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										12
									
								
								include/hw/i386/sgx.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								include/hw/i386/sgx.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| #ifndef QEMU_SGX_H | ||||
| #define QEMU_SGX_H | ||||
| 
 | ||||
| #include "qom/object.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "qapi/qapi-types-misc-target.h" | ||||
| 
 | ||||
| SGXInfo *sgx_get_info(Error **errp); | ||||
| SGXInfo *sgx_get_capabilities(Error **errp); | ||||
| 
 | ||||
| #endif | ||||
| @ -62,6 +62,7 @@ struct X86MachineState { | ||||
|     unsigned pci_irq_mask; | ||||
|     unsigned apic_id_limit; | ||||
|     uint16_t boot_cpus; | ||||
|     SgxEPCList *sgx_epc_list; | ||||
| 
 | ||||
|     OnOffAuto smm; | ||||
|     OnOffAuto acpi; | ||||
|  | ||||
| @ -49,5 +49,6 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict); | ||||
| void hmp_mce(Monitor *mon, const QDict *qdict); | ||||
| void hmp_info_local_apic(Monitor *mon, const QDict *qdict); | ||||
| void hmp_info_io_apic(Monitor *mon, const QDict *qdict); | ||||
| void hmp_info_sgx(Monitor *mon, const QDict *qdict); | ||||
| 
 | ||||
| #endif /* MONITOR_HMP_TARGET_H */ | ||||
|  | ||||
| @ -37,7 +37,7 @@ typedef struct KVMMemoryListener { | ||||
| } KVMMemoryListener; | ||||
| 
 | ||||
| void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml, | ||||
|                                   AddressSpace *as, int as_id); | ||||
|                                   AddressSpace *as, int as_id, const char *name); | ||||
| 
 | ||||
| void kvm_set_max_memslot_size(hwaddr max_slot_size); | ||||
| 
 | ||||
|  | ||||
| @ -122,7 +122,7 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, | ||||
|             .ipv4 = iaddr->ipv4, | ||||
|             .has_ipv6 = iaddr->has_ipv6, | ||||
|             .ipv6 = iaddr->ipv6, | ||||
| #ifdef IPPROTO_MPTCP | ||||
| #ifdef HAVE_IPPROTO_MPTCP | ||||
|             .has_mptcp = iaddr->has_mptcp, | ||||
|             .mptcp = iaddr->mptcp, | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										18
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								meson.build
									
									
									
									
									
								
							| @ -106,14 +106,14 @@ if targetos != 'darwin' | ||||
| endif | ||||
| 
 | ||||
| edk2_targets = [ 'arm-softmmu', 'aarch64-softmmu', 'i386-softmmu', 'x86_64-softmmu' ] | ||||
| install_edk2_blobs = false | ||||
| if get_option('install_blobs') | ||||
|   foreach target : target_dirs | ||||
|     install_edk2_blobs = install_edk2_blobs or target in edk2_targets | ||||
|   endforeach | ||||
| endif | ||||
| 
 | ||||
| bzip2 = find_program('bzip2', required: install_edk2_blobs) | ||||
| unpack_edk2_blobs = false | ||||
| foreach target : edk2_targets | ||||
|   if target in target_dirs | ||||
|     bzip2 = find_program('bzip2', required: get_option('install_blobs')) | ||||
|     unpack_edk2_blobs = bzip2.found() | ||||
|     break | ||||
|   endif | ||||
| endforeach | ||||
| 
 | ||||
| ################## | ||||
| # Compiler flags # | ||||
| @ -1374,6 +1374,8 @@ config_host_data.set('HAVE_OPTRESET', | ||||
|                      cc.has_header_symbol('getopt.h', 'optreset')) | ||||
| config_host_data.set('HAVE_UTMPX', | ||||
|                      cc.has_header_symbol('utmpx.h', 'struct utmpx')) | ||||
| config_host_data.set('HAVE_IPPROTO_MPTCP', | ||||
|                      cc.has_header_symbol('netinet/in.h', 'IPPROTO_MPTCP')) | ||||
| 
 | ||||
| # has_member | ||||
| config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', | ||||
|  | ||||
| @ -120,7 +120,7 @@ option('usb_redir', type : 'feature', value : 'auto', | ||||
|        description: 'libusbredir support') | ||||
| option('virglrenderer', type : 'feature', value : 'auto', | ||||
|        description: 'virgl rendering support') | ||||
| option('vnc', type : 'feature', value : 'enabled', | ||||
| option('vnc', type : 'feature', value : 'auto', | ||||
|        description: 'VNC server') | ||||
| option('vnc_jpeg', type : 'feature', value : 'auto', | ||||
|        description: 'JPEG lossy compression for VNC server') | ||||
|  | ||||
| @ -1823,6 +1823,7 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) | ||||
|     VirtioMEMDeviceInfo *vmi; | ||||
|     MemoryDeviceInfo *value; | ||||
|     PCDIMMDeviceInfo *di; | ||||
|     SgxEPCDeviceInfo *se; | ||||
| 
 | ||||
|     for (info = info_list; info; info = info->next) { | ||||
|         value = info->value; | ||||
| @ -1870,6 +1871,15 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) | ||||
|                                vmi->block_size); | ||||
|                 monitor_printf(mon, "  memdev: %s\n", vmi->memdev); | ||||
|                 break; | ||||
|             case MEMORY_DEVICE_INFO_KIND_SGX_EPC: | ||||
|                 se = value->u.sgx_epc.data; | ||||
|                 monitor_printf(mon, "Memory device [%s]: \"%s\"\n", | ||||
|                                MemoryDeviceInfoKind_str(value->type), | ||||
|                                se->id ? se->id : ""); | ||||
|                 monitor_printf(mon, "  memaddr: 0x%" PRIx64 "\n", se->memaddr); | ||||
|                 monitor_printf(mon, "  size: %" PRIu64 "\n", se->size); | ||||
|                 monitor_printf(mon, "  memdev: %s\n", se->memdev); | ||||
|                 break; | ||||
|             default: | ||||
|                 g_assert_not_reached(); | ||||
|             } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| if install_edk2_blobs | ||||
| if unpack_edk2_blobs and get_option('install_blobs') | ||||
|   foreach f: [ | ||||
|     '50-edk2-i386-secure.json', | ||||
|     '50-edk2-x86_64-secure.json', | ||||
| @ -10,7 +10,7 @@ if install_edk2_blobs | ||||
|     configure_file(input: files(f), | ||||
|                    output: f, | ||||
|                    configuration: {'DATADIR': get_option('prefix') / qemu_datadir}, | ||||
|                    install: get_option('install_blobs'), | ||||
|                    install: true, | ||||
|                    install_dir: qemu_datadir / 'firmware') | ||||
|   endforeach | ||||
| endif | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| if install_edk2_blobs | ||||
| roms = [] | ||||
| if unpack_edk2_blobs | ||||
|   fds = [ | ||||
|     'edk2-aarch64-code.fd', | ||||
|     'edk2-arm-code.fd', | ||||
| @ -11,7 +12,7 @@ if install_edk2_blobs | ||||
|   ] | ||||
| 
 | ||||
|   foreach f : fds | ||||
|     custom_target(f, | ||||
|     roms += custom_target(f, | ||||
|                   build_by_default: have_system, | ||||
|                   output: f, | ||||
|                   input: '@0@.bz2'.format(f), | ||||
|  | ||||
| @ -1194,13 +1194,36 @@ | ||||
|           } | ||||
| } | ||||
| 
 | ||||
| ## | ||||
| # @SgxEPCDeviceInfo: | ||||
| # | ||||
| # Sgx EPC state information | ||||
| # | ||||
| # @id: device's ID | ||||
| # | ||||
| # @memaddr: physical address in memory, where device is mapped | ||||
| # | ||||
| # @size: size of memory that the device provides | ||||
| # | ||||
| # @memdev: memory backend linked with device | ||||
| # | ||||
| # Since: 6.2 | ||||
| ## | ||||
| { 'struct': 'SgxEPCDeviceInfo', | ||||
|   'data': { '*id': 'str', | ||||
|             'memaddr': 'size', | ||||
|             'size': 'size', | ||||
|             'memdev': 'str' | ||||
|           } | ||||
| } | ||||
| 
 | ||||
| ## | ||||
| # @MemoryDeviceInfoKind: | ||||
| # | ||||
| # Since: 2.1 | ||||
| ## | ||||
| { 'enum': 'MemoryDeviceInfoKind', | ||||
|   'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem' ] } | ||||
|   'data': [ 'dimm', 'nvdimm', 'virtio-pmem', 'virtio-mem', 'sgx-epc' ] } | ||||
| 
 | ||||
| ## | ||||
| # @PCDIMMDeviceInfoWrapper: | ||||
| @ -1226,13 +1249,21 @@ | ||||
| { 'struct': 'VirtioMEMDeviceInfoWrapper', | ||||
|   'data': { 'data': 'VirtioMEMDeviceInfo' } } | ||||
| 
 | ||||
| ## | ||||
| # @SgxEPCDeviceInfoWrapper: | ||||
| # | ||||
| # Since: 6.2 | ||||
| ## | ||||
| { 'struct': 'SgxEPCDeviceInfoWrapper', | ||||
|   'data': { 'data': 'SgxEPCDeviceInfo' } } | ||||
| 
 | ||||
| ## | ||||
| # @MemoryDeviceInfo: | ||||
| # | ||||
| # Union containing information about a memory device | ||||
| # | ||||
| # nvdimm is included since 2.12. virtio-pmem is included since 4.1. | ||||
| # virtio-mem is included since 5.1. | ||||
| # virtio-mem is included since 5.1. sgx-epc is included since 6.2. | ||||
| # | ||||
| # Since: 2.1 | ||||
| ## | ||||
| @ -1242,10 +1273,36 @@ | ||||
|   'data': { 'dimm': 'PCDIMMDeviceInfoWrapper', | ||||
|             'nvdimm': 'PCDIMMDeviceInfoWrapper', | ||||
|             'virtio-pmem': 'VirtioPMEMDeviceInfoWrapper', | ||||
|             'virtio-mem': 'VirtioMEMDeviceInfoWrapper' | ||||
|             'virtio-mem': 'VirtioMEMDeviceInfoWrapper', | ||||
|             'sgx-epc': 'SgxEPCDeviceInfoWrapper' | ||||
|           } | ||||
| } | ||||
| 
 | ||||
| ## | ||||
| # @SgxEPC: | ||||
| # | ||||
| # Sgx EPC cmdline information | ||||
| # | ||||
| # @memdev: memory backend linked with device | ||||
| # | ||||
| # Since: 6.2 | ||||
| ## | ||||
| { 'struct': 'SgxEPC', | ||||
|   'data': { 'memdev': 'str' } } | ||||
| 
 | ||||
| ## | ||||
| # @SgxEPCProperties: | ||||
| # | ||||
| # SGX properties of machine types. | ||||
| # | ||||
| # @sgx-epc: list of ids of memory-backend-epc objects. | ||||
| # | ||||
| # Since: 6.2 | ||||
| ## | ||||
| { 'struct': 'SgxEPCProperties', | ||||
|   'data': { 'sgx-epc': ['SgxEPC'] } | ||||
| } | ||||
| 
 | ||||
| ## | ||||
| # @query-memory-devices: | ||||
| # | ||||
|  | ||||
| @ -333,3 +333,64 @@ | ||||
| { 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, | ||||
|   'returns': 'SevAttestationReport', | ||||
|   'if': 'TARGET_I386' } | ||||
| 
 | ||||
| ## | ||||
| # @SGXInfo: | ||||
| # | ||||
| # Information about intel Safe Guard eXtension (SGX) support | ||||
| # | ||||
| # @sgx: true if SGX is supported | ||||
| # | ||||
| # @sgx1: true if SGX1 is supported | ||||
| # | ||||
| # @sgx2: true if SGX2 is supported | ||||
| # | ||||
| # @flc: true if FLC is supported | ||||
| # | ||||
| # @section-size: The EPC section size for guest | ||||
| # | ||||
| # Since: 6.2 | ||||
| ## | ||||
| { 'struct': 'SGXInfo', | ||||
|   'data': { 'sgx': 'bool', | ||||
|             'sgx1': 'bool', | ||||
|             'sgx2': 'bool', | ||||
|             'flc': 'bool', | ||||
|             'section-size': 'uint64'}, | ||||
|    'if': 'TARGET_I386' } | ||||
| 
 | ||||
| ## | ||||
| # @query-sgx: | ||||
| # | ||||
| # Returns information about SGX | ||||
| # | ||||
| # Returns: @SGXInfo | ||||
| # | ||||
| # Since: 6.2 | ||||
| # | ||||
| # Example: | ||||
| # | ||||
| # -> { "execute": "query-sgx" } | ||||
| # <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, | ||||
| #                  "flc": true, "section-size" : 0 } } | ||||
| # | ||||
| ## | ||||
| { 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } | ||||
| 
 | ||||
| ## | ||||
| # @query-sgx-capabilities: | ||||
| # | ||||
| # Returns information from host SGX capabilities | ||||
| # | ||||
| # Returns: @SGXInfo | ||||
| # | ||||
| # Since: 6.2 | ||||
| # | ||||
| # Example: | ||||
| # | ||||
| # -> { "execute": "query-sgx-capabilities" } | ||||
| # <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, | ||||
| #                  "flc": true, "section-size" : 0 } } | ||||
| # | ||||
| ## | ||||
| { 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } | ||||
|  | ||||
| @ -647,6 +647,23 @@ | ||||
|             '*hugetlbsize': 'size', | ||||
|             '*seal': 'bool' } } | ||||
| 
 | ||||
| ## | ||||
| # @MemoryBackendEpcProperties: | ||||
| # | ||||
| # Properties for memory-backend-epc objects. | ||||
| # | ||||
| # The @share boolean option is true by default with epc | ||||
| # | ||||
| # The @merge boolean option is false by default with epc | ||||
| # | ||||
| # The @dump boolean option is false by default with epc | ||||
| # | ||||
| # Since: 6.2 | ||||
| ## | ||||
| { 'struct': 'MemoryBackendEpcProperties', | ||||
|   'base': 'MemoryBackendProperties', | ||||
|   'data': {} } | ||||
| 
 | ||||
| ## | ||||
| # @PrManagerHelperProperties: | ||||
| # | ||||
| @ -797,6 +814,7 @@ | ||||
|     { 'name': 'memory-backend-memfd', | ||||
|       'if': 'CONFIG_LINUX' }, | ||||
|     'memory-backend-ram', | ||||
|     'memory-backend-epc', | ||||
|     'pef-guest', | ||||
|     'pr-manager-helper', | ||||
|     'qtest', | ||||
| @ -855,6 +873,7 @@ | ||||
|       'memory-backend-memfd':       { 'type': 'MemoryBackendMemfdProperties', | ||||
|                                       'if': 'CONFIG_LINUX' }, | ||||
|       'memory-backend-ram':         'MemoryBackendProperties', | ||||
|       'memory-backend-epc':         'MemoryBackendEpcProperties', | ||||
|       'pr-manager-helper':          'PrManagerHelperProperties', | ||||
|       'qtest':                      'QtestProperties', | ||||
|       'rng-builtin':                'RngProperties', | ||||
|  | ||||
| @ -69,7 +69,7 @@ | ||||
|     '*ipv4': 'bool', | ||||
|     '*ipv6': 'bool', | ||||
|     '*keep-alive': 'bool', | ||||
|     '*mptcp': { 'type': 'bool', 'if': 'IPPROTO_MPTCP' } } } | ||||
|     '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } | ||||
| 
 | ||||
| ## | ||||
| # @UnixSocketAddress: | ||||
|  | ||||
| @ -126,8 +126,14 @@ SRST | ||||
|             -m 512M | ||||
| ERST | ||||
| 
 | ||||
| HXCOMM Deprecated by -machine | ||||
| DEF("M", HAS_ARG, QEMU_OPTION_M, "", QEMU_ARCH_ALL) | ||||
| DEF("M", HAS_ARG, QEMU_OPTION_M, | ||||
|     "                sgx-epc.0.memdev=memid\n", | ||||
|     QEMU_ARCH_ALL) | ||||
| 
 | ||||
| SRST | ||||
| ``sgx-epc.0.memdev=@var{memid}`` | ||||
|     Define an SGX EPC section. | ||||
| ERST | ||||
| 
 | ||||
| DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, | ||||
|     "-cpu cpu        select CPU ('-cpu help' for list)\n", QEMU_ARCH_ALL) | ||||
|  | ||||
| @ -1811,6 +1811,11 @@ bool memory_region_is_ram_device(MemoryRegion *mr) | ||||
|     return mr->ram_device; | ||||
| } | ||||
| 
 | ||||
| bool memory_region_is_protected(MemoryRegion *mr) | ||||
| { | ||||
|     return mr->ram && (mr->ram_block->flags & RAM_PROTECTED); | ||||
| } | ||||
| 
 | ||||
| uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr) | ||||
| { | ||||
|     uint8_t mask = mr->dirty_log_mask; | ||||
| @ -2149,6 +2154,7 @@ static void memory_region_sync_dirty_bitmap(MemoryRegion *mr) | ||||
|                 } | ||||
|             } | ||||
|             flatview_unref(view); | ||||
|             trace_memory_region_sync_dirty(mr ? mr->name : "(all)", listener->name, 0); | ||||
|         } else if (listener->log_sync_global) { | ||||
|             /*
 | ||||
|              * No matter whether MR is specified, what we can do here | ||||
| @ -2156,6 +2162,7 @@ static void memory_region_sync_dirty_bitmap(MemoryRegion *mr) | ||||
|              * sync in a finer granularity. | ||||
|              */ | ||||
|             listener->log_sync_global(listener); | ||||
|             trace_memory_region_sync_dirty(mr ? mr->name : "(all)", listener->name, 1); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -756,6 +756,7 @@ void cpu_address_space_init(CPUState *cpu, int asidx, | ||||
|     if (tcg_enabled()) { | ||||
|         newas->tcg_as_listener.log_global_after_sync = tcg_log_global_after_sync; | ||||
|         newas->tcg_as_listener.commit = tcg_commit; | ||||
|         newas->tcg_as_listener.name = "tcg"; | ||||
|         memory_listener_register(&newas->tcg_as_listener, as); | ||||
|     } | ||||
| } | ||||
| @ -2055,7 +2056,8 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, | ||||
|     int64_t file_size, file_align; | ||||
| 
 | ||||
|     /* Just support these ram flags by now. */ | ||||
|     assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE)) == 0); | ||||
|     assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE | | ||||
|                           RAM_PROTECTED)) == 0); | ||||
| 
 | ||||
|     if (xen_enabled()) { | ||||
|         error_setg(errp, "-mem-path not supported with Xen"); | ||||
|  | ||||
| @ -15,6 +15,7 @@ memory_region_subpage_read(int cpu_index, void *mr, uint64_t offset, uint64_t va | ||||
| memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t value, unsigned size) "cpu %d mr %p offset 0x%"PRIx64" value 0x%"PRIx64" size %u" | ||||
| memory_region_ram_device_read(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" | ||||
| memory_region_ram_device_write(int cpu_index, void *mr, uint64_t addr, uint64_t value, unsigned size) "cpu %d mr %p addr 0x%"PRIx64" value 0x%"PRIx64" size %u" | ||||
| memory_region_sync_dirty(const char *mr, const char *listener, int global) "mr '%s' listener '%s' synced (global=%d)" | ||||
| flatview_new(void *view, void *root) "%p (root %p)" | ||||
| flatview_destroy(void *view, void *root) "%p (root %p)" | ||||
| flatview_destroy_rcu(void *view, void *root) "%p (root %p)" | ||||
|  | ||||
| @ -335,6 +335,7 @@ static void kvm_arm_devlistener_del(MemoryListener *listener, | ||||
| } | ||||
| 
 | ||||
| static MemoryListener devlistener = { | ||||
|     .name = "kvm-arm", | ||||
|     .region_add = kvm_arm_devlistener_add, | ||||
|     .region_del = kvm_arm_devlistener_del, | ||||
| }; | ||||
|  | ||||
| @ -36,6 +36,7 @@ | ||||
| #ifndef CONFIG_USER_ONLY | ||||
| #include "exec/address-spaces.h" | ||||
| #include "hw/boards.h" | ||||
| #include "hw/i386/sgx-epc.h" | ||||
| #endif | ||||
| 
 | ||||
| #include "disas/capstone.h" | ||||
| @ -654,6 +655,9 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, | ||||
|           /* missing:
 | ||||
|           CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ | ||||
| #define TCG_14_0_ECX_FEATURES 0 | ||||
| #define TCG_SGX_12_0_EAX_FEATURES 0 | ||||
| #define TCG_SGX_12_0_EBX_FEATURES 0 | ||||
| #define TCG_SGX_12_1_EAX_FEATURES 0 | ||||
| 
 | ||||
| FeatureWordInfo feature_word_info[FEATURE_WORDS] = { | ||||
|     [FEAT_1_EDX] = { | ||||
| @ -795,7 +799,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { | ||||
|     [FEAT_7_0_EBX] = { | ||||
|         .type = CPUID_FEATURE_WORD, | ||||
|         .feat_names = { | ||||
|             "fsgsbase", "tsc-adjust", NULL, "bmi1", | ||||
|             "fsgsbase", "tsc-adjust", "sgx", "bmi1", | ||||
|             "hle", "avx2", NULL, "smep", | ||||
|             "bmi2", "erms", "invpcid", "rtm", | ||||
|             NULL, NULL, "mpx", NULL, | ||||
| @ -821,7 +825,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { | ||||
|             "la57", NULL, NULL, NULL, | ||||
|             NULL, NULL, "rdpid", NULL, | ||||
|             "bus-lock-detect", "cldemote", NULL, "movdiri", | ||||
|             "movdir64b", NULL, NULL, "pks", | ||||
|             "movdir64b", NULL, "sgxlc", "pks", | ||||
|         }, | ||||
|         .cpuid = { | ||||
|             .eax = 7, | ||||
| @ -1182,6 +1186,65 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { | ||||
|         .tcg_features = TCG_14_0_ECX_FEATURES, | ||||
|      }, | ||||
| 
 | ||||
|     [FEAT_SGX_12_0_EAX] = { | ||||
|         .type = CPUID_FEATURE_WORD, | ||||
|         .feat_names = { | ||||
|             "sgx1", "sgx2", NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|         }, | ||||
|         .cpuid = { | ||||
|             .eax = 0x12, | ||||
|             .needs_ecx = true, .ecx = 0, | ||||
|             .reg = R_EAX, | ||||
|         }, | ||||
|         .tcg_features = TCG_SGX_12_0_EAX_FEATURES, | ||||
|     }, | ||||
| 
 | ||||
|     [FEAT_SGX_12_0_EBX] = { | ||||
|         .type = CPUID_FEATURE_WORD, | ||||
|         .feat_names = { | ||||
|             "sgx-exinfo" , NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|         }, | ||||
|         .cpuid = { | ||||
|             .eax = 0x12, | ||||
|             .needs_ecx = true, .ecx = 0, | ||||
|             .reg = R_EBX, | ||||
|         }, | ||||
|         .tcg_features = TCG_SGX_12_0_EBX_FEATURES, | ||||
|     }, | ||||
| 
 | ||||
|     [FEAT_SGX_12_1_EAX] = { | ||||
|         .type = CPUID_FEATURE_WORD, | ||||
|         .feat_names = { | ||||
|             NULL, "sgx-debug", "sgx-mode64", NULL, | ||||
|             "sgx-provisionkey", "sgx-tokenkey", NULL, "sgx-kss", | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|             NULL, NULL, NULL, NULL, | ||||
|         }, | ||||
|         .cpuid = { | ||||
|             .eax = 0x12, | ||||
|             .needs_ecx = true, .ecx = 1, | ||||
|             .reg = R_EAX, | ||||
|         }, | ||||
|         .tcg_features = TCG_SGX_12_1_EAX_FEATURES, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| typedef struct FeatureMask { | ||||
| @ -5272,6 +5335,25 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, | ||||
|                 *ecx |= CPUID_7_0_ECX_OSPKE; | ||||
|             } | ||||
|             *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ | ||||
| 
 | ||||
|             /*
 | ||||
|              * SGX cannot be emulated in software.  If hardware does not | ||||
|              * support enabling SGX and/or SGX flexible launch control, | ||||
|              * then we need to update the VM's CPUID values accordingly. | ||||
|              */ | ||||
|             if ((*ebx & CPUID_7_0_EBX_SGX) && | ||||
|                 (!kvm_enabled() || | ||||
|                  !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_EBX) & | ||||
|                     CPUID_7_0_EBX_SGX))) { | ||||
|                 *ebx &= ~CPUID_7_0_EBX_SGX; | ||||
|             } | ||||
| 
 | ||||
|             if ((*ecx & CPUID_7_0_ECX_SGX_LC) && | ||||
|                 (!(*ebx & CPUID_7_0_EBX_SGX) || !kvm_enabled() || | ||||
|                  !(kvm_arch_get_supported_cpuid(cs->kvm_state, 0x7, 0, R_ECX) & | ||||
|                     CPUID_7_0_ECX_SGX_LC))) { | ||||
|                 *ecx &= ~CPUID_7_0_ECX_SGX_LC; | ||||
|             } | ||||
|         } else if (count == 1) { | ||||
|             *eax = env->features[FEAT_7_1_EAX]; | ||||
|             *ebx = 0; | ||||
| @ -5407,6 +5489,66 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case 0x12: | ||||
| #ifndef CONFIG_USER_ONLY | ||||
|         if (!kvm_enabled() || | ||||
|             !(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SGX)) { | ||||
|             *eax = *ebx = *ecx = *edx = 0; | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         /*
 | ||||
|          * SGX sub-leafs CPUID.0x12.{0x2..N} enumerate EPC sections.  Retrieve | ||||
|          * the EPC properties, e.g. confidentiality and integrity, from the | ||||
|          * host's first EPC section, i.e. assume there is one EPC section or | ||||
|          * that all EPC sections have the same security properties. | ||||
|          */ | ||||
|         if (count > 1) { | ||||
|             uint64_t epc_addr, epc_size; | ||||
| 
 | ||||
|             if (sgx_epc_get_section(count - 2, &epc_addr, &epc_size)) { | ||||
|                 *eax = *ebx = *ecx = *edx = 0; | ||||
|                 break; | ||||
|             } | ||||
|             host_cpuid(index, 2, eax, ebx, ecx, edx); | ||||
|             *eax = (uint32_t)(epc_addr & 0xfffff000) | 0x1; | ||||
|             *ebx = (uint32_t)(epc_addr >> 32); | ||||
|             *ecx = (uint32_t)(epc_size & 0xfffff000) | (*ecx & 0xf); | ||||
|             *edx = (uint32_t)(epc_size >> 32); | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         /*
 | ||||
|          * SGX sub-leafs CPUID.0x12.{0x0,0x1} are heavily dependent on hardware | ||||
|          * and KVM, i.e. QEMU cannot emulate features to override what KVM | ||||
|          * supports.  Features can be further restricted by userspace, but not | ||||
|          * made more permissive. | ||||
|          */ | ||||
|         *eax = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EAX); | ||||
|         *ebx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EBX); | ||||
|         *ecx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_ECX); | ||||
|         *edx = kvm_arch_get_supported_cpuid(cs->kvm_state, 0x12, count, R_EDX); | ||||
| 
 | ||||
|         if (count == 0) { | ||||
|             *eax &= env->features[FEAT_SGX_12_0_EAX]; | ||||
|             *ebx &= env->features[FEAT_SGX_12_0_EBX]; | ||||
|         } else { | ||||
|             *eax &= env->features[FEAT_SGX_12_1_EAX]; | ||||
|             *ebx &= 0; /* ebx reserve */ | ||||
|             *ecx &= env->features[FEAT_XSAVE_COMP_LO]; | ||||
|             *edx &= env->features[FEAT_XSAVE_COMP_HI]; | ||||
| 
 | ||||
|             /* FP and SSE are always allowed regardless of XSAVE/XCR0. */ | ||||
|             *ecx |= XSTATE_FP_MASK | XSTATE_SSE_MASK; | ||||
| 
 | ||||
|             /* Access to PROVISIONKEY requires additional credentials. */ | ||||
|             if ((*eax & (1U << 4)) && | ||||
|                 !kvm_enable_sgx_provisioning(cs->kvm_state)) { | ||||
|                 *eax &= ~(1U << 4); | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|         break; | ||||
|     case 0x14: { | ||||
|         /* Intel Processor Trace Enumeration */ | ||||
|         *eax = 0; | ||||
| @ -5638,6 +5780,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void x86_cpu_set_sgxlepubkeyhash(CPUX86State *env) | ||||
| { | ||||
| #ifndef CONFIG_USER_ONLY | ||||
|     /* Those default values are defined in Skylake HW */ | ||||
|     env->msr_ia32_sgxlepubkeyhash[0] = 0xa6053e051270b7acULL; | ||||
|     env->msr_ia32_sgxlepubkeyhash[1] = 0x6cfbe8ba8b3b413dULL; | ||||
|     env->msr_ia32_sgxlepubkeyhash[2] = 0xc4916d99f2b3735dULL; | ||||
|     env->msr_ia32_sgxlepubkeyhash[3] = 0xd4f8c05909f9bb3bULL; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| static void x86_cpu_reset(DeviceState *dev) | ||||
| { | ||||
|     CPUState *s = CPU(dev); | ||||
| @ -5770,6 +5923,8 @@ static void x86_cpu_reset(DeviceState *dev) | ||||
|     if (kvm_enabled()) { | ||||
|         kvm_arch_reset_vcpu(cpu); | ||||
|     } | ||||
| 
 | ||||
|     x86_cpu_set_sgxlepubkeyhash(env); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| @ -5999,6 +6154,11 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) | ||||
|         if (sev_enabled()) { | ||||
|             x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000001F); | ||||
|         } | ||||
| 
 | ||||
|         /* SGX requires CPUID[0x12] for EPC enumeration */ | ||||
|         if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SGX) { | ||||
|             x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x12); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */ | ||||
| @ -6152,6 +6312,8 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) | ||||
|            & CPUID_EXT2_AMD_ALIASES); | ||||
|     } | ||||
| 
 | ||||
|     x86_cpu_set_sgxlepubkeyhash(env); | ||||
| 
 | ||||
|     /*
 | ||||
|      * note: the call to the framework needs to happen after feature expansion, | ||||
|      * but before the checks/modifications to ucode_rev, mwait, phys_bits. | ||||
| @ -6839,7 +7001,6 @@ static const TypeInfo x86_cpu_type_info = { | ||||
|     .class_init = x86_cpu_common_class_init, | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| /* "base" CPU model, used by query-cpu-model-expansion */ | ||||
| static void x86_cpu_base_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|  | ||||
| @ -389,9 +389,17 @@ typedef enum X86Seg { | ||||
| #define MSR_IA32_PKRS                   0x6e1 | ||||
| 
 | ||||
| #define FEATURE_CONTROL_LOCKED                    (1<<0) | ||||
| #define FEATURE_CONTROL_VMXON_ENABLED_INSIDE_SMX  (1ULL << 1) | ||||
| #define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2) | ||||
| #define FEATURE_CONTROL_SGX_LC                    (1ULL << 17) | ||||
| #define FEATURE_CONTROL_SGX                       (1ULL << 18) | ||||
| #define FEATURE_CONTROL_LMCE                      (1<<20) | ||||
| 
 | ||||
| #define MSR_IA32_SGXLEPUBKEYHASH0       0x8c | ||||
| #define MSR_IA32_SGXLEPUBKEYHASH1       0x8d | ||||
| #define MSR_IA32_SGXLEPUBKEYHASH2       0x8e | ||||
| #define MSR_IA32_SGXLEPUBKEYHASH3       0x8f | ||||
| 
 | ||||
| #define MSR_P6_PERFCTR0                 0xc1 | ||||
| 
 | ||||
| #define MSR_IA32_SMBASE                 0x9e | ||||
| @ -570,6 +578,9 @@ typedef enum FeatureWord { | ||||
|     FEAT_VMX_BASIC, | ||||
|     FEAT_VMX_VMFUNC, | ||||
|     FEAT_14_0_ECX, | ||||
|     FEAT_SGX_12_0_EAX,  /* CPUID[EAX=0x12,ECX=0].EAX (SGX) */ | ||||
|     FEAT_SGX_12_0_EBX,  /* CPUID[EAX=0x12,ECX=0].EBX (SGX MISCSELECT[31:0]) */ | ||||
|     FEAT_SGX_12_1_EAX,  /* CPUID[EAX=0x12,ECX=1].EAX (SGX ATTRIBUTES[31:0]) */ | ||||
|     FEATURE_WORDS, | ||||
| } FeatureWord; | ||||
| 
 | ||||
| @ -718,6 +729,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; | ||||
| 
 | ||||
| /* Support RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE */ | ||||
| #define CPUID_7_0_EBX_FSGSBASE          (1U << 0) | ||||
| /* Support SGX */ | ||||
| #define CPUID_7_0_EBX_SGX               (1U << 2) | ||||
| /* 1st Group of Advanced Bit Manipulation Extensions */ | ||||
| #define CPUID_7_0_EBX_BMI1              (1U << 3) | ||||
| /* Hardware Lock Elision */ | ||||
| @ -805,6 +818,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; | ||||
| #define CPUID_7_0_ECX_MOVDIRI           (1U << 27) | ||||
| /* Move 64 Bytes as Direct Store Instruction */ | ||||
| #define CPUID_7_0_ECX_MOVDIR64B         (1U << 28) | ||||
| /* Support SGX Launch Control */ | ||||
| #define CPUID_7_0_ECX_SGX_LC            (1U << 30) | ||||
| /* Protection Keys for Supervisor-mode Pages */ | ||||
| #define CPUID_7_0_ECX_PKS               (1U << 31) | ||||
| 
 | ||||
| @ -1501,6 +1516,7 @@ typedef struct CPUX86State { | ||||
|     uint64_t mcg_status; | ||||
|     uint64_t msr_ia32_misc_enable; | ||||
|     uint64_t msr_ia32_feature_control; | ||||
|     uint64_t msr_ia32_sgxlepubkeyhash[4]; | ||||
| 
 | ||||
|     uint64_t msr_fixed_ctr_ctrl; | ||||
|     uint64_t msr_global_ctrl; | ||||
|  | ||||
| @ -285,6 +285,7 @@ static void hax_log_sync(MemoryListener *listener, | ||||
| } | ||||
| 
 | ||||
| static MemoryListener hax_memory_listener = { | ||||
|     .name = "hax", | ||||
|     .begin = hax_transaction_begin, | ||||
|     .commit = hax_transaction_commit, | ||||
|     .region_add = hax_region_add, | ||||
|  | ||||
| @ -1703,6 +1703,25 @@ int kvm_arch_init_vcpu(CPUState *cs) | ||||
|             } | ||||
|             break; | ||||
|         case 0x7: | ||||
|         case 0x12: | ||||
|             for (j = 0; ; j++) { | ||||
|                 c->function = i; | ||||
|                 c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; | ||||
|                 c->index = j; | ||||
|                 cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); | ||||
| 
 | ||||
|                 if (j > 1 && (c->eax & 0xf) != 1) { | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { | ||||
|                     fprintf(stderr, "cpuid_data is full, no space for " | ||||
|                                 "cpuid(eax:0x12,ecx:0x%x)\n", j); | ||||
|                     abort(); | ||||
|                 } | ||||
|                 c = &cpuid_data.entries[cpuid_i++]; | ||||
|             } | ||||
|             break; | ||||
|         case 0x14: { | ||||
|             uint32_t times; | ||||
| 
 | ||||
| @ -1877,6 +1896,11 @@ int kvm_arch_init_vcpu(CPUState *cs) | ||||
|                                   !!(c->ecx & CPUID_EXT_SMX); | ||||
|     } | ||||
| 
 | ||||
|     c = cpuid_find_entry(&cpuid_data.cpuid, 7, 0); | ||||
|     if (c && (c->ebx & CPUID_7_0_EBX_SGX)) { | ||||
|         has_msr_feature_control = true; | ||||
|     } | ||||
| 
 | ||||
|     if (env->mcg_cap & MCG_LMCE_P) { | ||||
|         has_msr_mcg_ext_ctl = has_msr_feature_control = true; | ||||
|     } | ||||
| @ -2224,7 +2248,7 @@ static void register_smram_listener(Notifier *n, void *unused) | ||||
| 
 | ||||
|     address_space_init(&smram_address_space, &smram_as_root, "KVM-SMRAM"); | ||||
|     kvm_memory_listener_register(kvm_state, &smram_listener, | ||||
|                                  &smram_address_space, 1); | ||||
|                                  &smram_address_space, 1, "kvm-smram"); | ||||
| } | ||||
| 
 | ||||
| int kvm_arch_init(MachineState *ms, KVMState *s) | ||||
| @ -3107,6 +3131,17 @@ static int kvm_put_msrs(X86CPU *cpu, int level) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_SGX_LC) { | ||||
|             kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH0, | ||||
|                               env->msr_ia32_sgxlepubkeyhash[0]); | ||||
|             kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH1, | ||||
|                               env->msr_ia32_sgxlepubkeyhash[1]); | ||||
|             kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH2, | ||||
|                               env->msr_ia32_sgxlepubkeyhash[2]); | ||||
|             kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH3, | ||||
|                               env->msr_ia32_sgxlepubkeyhash[3]); | ||||
|         } | ||||
| 
 | ||||
|         /* Note: MSR_IA32_FEATURE_CONTROL is written separately, see
 | ||||
|          *       kvm_put_msr_feature_control. */ | ||||
|     } | ||||
| @ -3446,6 +3481,13 @@ static int kvm_get_msrs(X86CPU *cpu) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_SGX_LC) { | ||||
|         kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH0, 0); | ||||
|         kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH1, 0); | ||||
|         kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH2, 0); | ||||
|         kvm_msr_entry_add(cpu, MSR_IA32_SGXLEPUBKEYHASH3, 0); | ||||
|     } | ||||
| 
 | ||||
|     ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, cpu->kvm_msr_buf); | ||||
|     if (ret < 0) { | ||||
|         return ret; | ||||
| @ -3735,6 +3777,10 @@ static int kvm_get_msrs(X86CPU *cpu) | ||||
|         case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B: | ||||
|             env->msr_rtit_addrs[index - MSR_IA32_RTIT_ADDR0_A] = msrs[i].data; | ||||
|             break; | ||||
|         case MSR_IA32_SGXLEPUBKEYHASH0 ... MSR_IA32_SGXLEPUBKEYHASH3: | ||||
|             env->msr_ia32_sgxlepubkeyhash[index - MSR_IA32_SGXLEPUBKEYHASH0] = | ||||
|                            msrs[i].data; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -4617,6 +4663,35 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool has_sgx_provisioning; | ||||
| 
 | ||||
| static bool __kvm_enable_sgx_provisioning(KVMState *s) | ||||
| { | ||||
|     int fd, ret; | ||||
| 
 | ||||
|     if (!kvm_vm_check_extension(s, KVM_CAP_SGX_ATTRIBUTE)) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     fd = qemu_open_old("/dev/sgx_provision", O_RDONLY); | ||||
|     if (fd < 0) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     ret = kvm_vm_enable_cap(s, KVM_CAP_SGX_ATTRIBUTE, 0, fd); | ||||
|     if (ret) { | ||||
|         error_report("Could not enable SGX PROVISIONKEY: %s", strerror(-ret)); | ||||
|         exit(1); | ||||
|     } | ||||
|     close(fd); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool kvm_enable_sgx_provisioning(KVMState *s) | ||||
| { | ||||
|     return MEMORIZE(__kvm_enable_sgx_provisioning(s), has_sgx_provisioning); | ||||
| } | ||||
| 
 | ||||
| static bool host_supports_vmx(void) | ||||
| { | ||||
|     uint32_t ecx, unused; | ||||
|  | ||||
| @ -51,4 +51,6 @@ bool kvm_hyperv_expand_features(X86CPU *cpu, Error **errp); | ||||
| 
 | ||||
| uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address); | ||||
| 
 | ||||
| bool kvm_enable_sgx_provisioning(KVMState *s); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -1415,6 +1415,25 @@ static const VMStateDescription vmstate_msr_tsx_ctrl = { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static bool intel_sgx_msrs_needed(void *opaque) | ||||
| { | ||||
|     X86CPU *cpu = opaque; | ||||
|     CPUX86State *env = &cpu->env; | ||||
| 
 | ||||
|     return !!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_SGX_LC); | ||||
| } | ||||
| 
 | ||||
| static const VMStateDescription vmstate_msr_intel_sgx = { | ||||
|     .name = "cpu/intel_sgx", | ||||
|     .version_id = 1, | ||||
|     .minimum_version_id = 1, | ||||
|     .needed = intel_sgx_msrs_needed, | ||||
|     .fields = (VMStateField[]) { | ||||
|         VMSTATE_UINT64_ARRAY(env.msr_ia32_sgxlepubkeyhash, X86CPU, 4), | ||||
|         VMSTATE_END_OF_LIST() | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const VMStateDescription vmstate_x86_cpu = { | ||||
|     .name = "cpu", | ||||
|     .version_id = 12, | ||||
| @ -1551,6 +1570,7 @@ const VMStateDescription vmstate_x86_cpu = { | ||||
|         &vmstate_nested_state, | ||||
| #endif | ||||
|         &vmstate_msr_tsx_ctrl, | ||||
|         &vmstate_msr_intel_sgx, | ||||
|         NULL | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @ -35,6 +35,7 @@ | ||||
| #include "qapi/qapi-commands-misc-target.h" | ||||
| #include "qapi/qapi-commands-misc.h" | ||||
| #include "hw/i386/pc.h" | ||||
| #include "hw/i386/sgx.h" | ||||
| 
 | ||||
| /* Perform linear address sign extension */ | ||||
| static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) | ||||
| @ -763,3 +764,34 @@ qmp_query_sev_attestation_report(const char *mnonce, Error **errp) | ||||
| { | ||||
|     return sev_get_attestation_report(mnonce, errp); | ||||
| } | ||||
| 
 | ||||
| SGXInfo *qmp_query_sgx(Error **errp) | ||||
| { | ||||
|     return sgx_get_info(errp); | ||||
| } | ||||
| 
 | ||||
| void hmp_info_sgx(Monitor *mon, const QDict *qdict) | ||||
| { | ||||
|     Error *err = NULL; | ||||
|     g_autoptr(SGXInfo) info = qmp_query_sgx(&err); | ||||
| 
 | ||||
|     if (err) { | ||||
|         error_report_err(err); | ||||
|         return; | ||||
|     } | ||||
|     monitor_printf(mon, "SGX support: %s\n", | ||||
|                    info->sgx ? "enabled" : "disabled"); | ||||
|     monitor_printf(mon, "SGX1 support: %s\n", | ||||
|                    info->sgx1 ? "enabled" : "disabled"); | ||||
|     monitor_printf(mon, "SGX2 support: %s\n", | ||||
|                    info->sgx2 ? "enabled" : "disabled"); | ||||
|     monitor_printf(mon, "FLC support: %s\n", | ||||
|                    info->flc ? "enabled" : "disabled"); | ||||
|     monitor_printf(mon, "size: %" PRIu64 "\n", | ||||
|                    info->section_size); | ||||
| } | ||||
| 
 | ||||
| SGXInfo *qmp_query_sgx_capabilities(Error **errp) | ||||
| { | ||||
|     return sgx_get_capabilities(errp); | ||||
| } | ||||
|  | ||||
| @ -1123,6 +1123,7 @@ nvmm_log_sync(MemoryListener *listener, MemoryRegionSection *section) | ||||
| } | ||||
| 
 | ||||
| static MemoryListener nvmm_memory_listener = { | ||||
|     .name = "nvmm", | ||||
|     .begin = nvmm_transaction_begin, | ||||
|     .commit = nvmm_transaction_commit, | ||||
|     .region_add = nvmm_region_add, | ||||
|  | ||||
| @ -565,7 +565,7 @@ static int | ||||
| sev_read_file_base64(const char *filename, guchar **data, gsize *len) | ||||
| { | ||||
|     gsize sz; | ||||
|     gchar *base64; | ||||
|     g_autofree gchar *base64 = NULL; | ||||
|     GError *error = NULL; | ||||
| 
 | ||||
|     if (!g_file_get_contents(filename, &base64, &sz, &error)) { | ||||
|  | ||||
| @ -1598,6 +1598,7 @@ static void whpx_log_sync(MemoryListener *listener, | ||||
| } | ||||
| 
 | ||||
| static MemoryListener whpx_memory_listener = { | ||||
|     .name = "whpx", | ||||
|     .begin = whpx_transaction_begin, | ||||
|     .commit = whpx_transaction_commit, | ||||
|     .region_add = whpx_region_add, | ||||
|  | ||||
| @ -68,12 +68,12 @@ qtests_i386 = \ | ||||
|   (config_all_devices.has_key('CONFIG_RTL8139_PCI') ? ['rtl8139-test'] : []) +              \ | ||||
|   (config_all_devices.has_key('CONFIG_E1000E_PCI_EXPRESS') ? ['fuzz-e1000e-test'] : []) +   \ | ||||
|   (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) +                 \ | ||||
|   (unpack_edk2_blobs ? ['bios-tables-test'] : []) +                                         \ | ||||
|   qtests_pci +                                                                              \ | ||||
|   ['fdc-test', | ||||
|    'ide-test', | ||||
|    'hd-geo-test', | ||||
|    'boot-order-test', | ||||
|    'bios-tables-test', | ||||
|    'rtc-test', | ||||
|    'i440fx-test', | ||||
|    'fw_cfg-test', | ||||
| @ -180,7 +180,7 @@ qtests_arm = \ | ||||
| 
 | ||||
| # TODO: once aarch64 TCG is fixed on ARM 32 bit host, make bios-tables-test unconditional | ||||
| qtests_aarch64 = \ | ||||
|   (cpu != 'arm' ? ['bios-tables-test'] : []) +                                                  \ | ||||
|   (cpu != 'arm' and unpack_edk2_blobs ? ['bios-tables-test'] : []) +                            \ | ||||
|   (config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-test'] : []) +        \ | ||||
|   (config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? ['tpm-tis-device-swtpm-test'] : []) +  \ | ||||
|   ['arm-cpu-features', | ||||
| @ -269,7 +269,7 @@ foreach dir : target_dirs | ||||
|   qtest_emulator = emulators['qemu-system-' + target_base] | ||||
|   target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic | ||||
| 
 | ||||
|   test_deps = [] | ||||
|   test_deps = roms | ||||
|   qtest_env = environment() | ||||
|   if have_tools | ||||
|     qtest_env.set('QTEST_QEMU_IMG', './qemu-img') | ||||
|  | ||||
| @ -100,6 +100,8 @@ static bool query_is_ignored(const char *cmd) | ||||
|         /* Success depends on Host or Hypervisor SEV support */ | ||||
|         "query-sev", | ||||
|         "query-sev-capabilities", | ||||
|         "query-sgx", | ||||
|         "query-sgx-capabilities", | ||||
|         NULL | ||||
|     }; | ||||
|     int i; | ||||
|  | ||||
| @ -278,7 +278,7 @@ static int inet_listen_saddr(InetSocketAddress *saddr, | ||||
| 
 | ||||
|     /* create socket + bind/listen */ | ||||
|     for (e = res; e != NULL; e = e->ai_next) { | ||||
| #ifdef IPPROTO_MPTCP | ||||
| #ifdef HAVE_IPPROTO_MPTCP | ||||
|         if (saddr->has_mptcp && saddr->mptcp) { | ||||
|             e->ai_protocol = IPPROTO_MPTCP; | ||||
|         } | ||||
| @ -462,7 +462,7 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) | ||||
|         error_free(local_err); | ||||
|         local_err = NULL; | ||||
| 
 | ||||
| #ifdef IPPROTO_MPTCP | ||||
| #ifdef HAVE_IPPROTO_MPTCP | ||||
|         if (saddr->has_mptcp && saddr->mptcp) { | ||||
|             e->ai_protocol = IPPROTO_MPTCP; | ||||
|         } | ||||
| @ -699,7 +699,7 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) | ||||
|         } | ||||
|         addr->has_keep_alive = true; | ||||
|     } | ||||
| #ifdef IPPROTO_MPTCP | ||||
| #ifdef HAVE_IPPROTO_MPTCP | ||||
|     begin = strstr(optstr, ",mptcp"); | ||||
|     if (begin) { | ||||
|         if (inet_parse_flag("mptcp", begin + strlen(",mptcp"), | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell