2021-07-19 14:21:07 +03:00
|
|
|
/*
|
|
|
|
* 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),
|
numa: Enable numa for SGX EPC sections
The basic SGX did not enable numa for SGX EPC sections, which
result in all EPC sections located in numa node 0. This patch
enable SGX numa function in the guest and the EPC section can
work with RAM as one numa node.
The Guest kernel related log:
[ 0.009981] ACPI: SRAT: Node 0 PXM 0 [mem 0x180000000-0x183ffffff]
[ 0.009982] ACPI: SRAT: Node 1 PXM 1 [mem 0x184000000-0x185bfffff]
The SRAT table can normally show SGX EPC sections menory info in different
numa nodes.
The SGX EPC numa related command:
......
-m 4G,maxmem=20G \
-smp sockets=2,cores=2 \
-cpu host,+sgx-provisionkey \
-object memory-backend-ram,size=2G,host-nodes=0,policy=bind,id=node0 \
-object memory-backend-epc,id=mem0,size=64M,prealloc=on,host-nodes=0,policy=bind \
-numa node,nodeid=0,cpus=0-1,memdev=node0 \
-object memory-backend-ram,size=2G,host-nodes=1,policy=bind,id=node1 \
-object memory-backend-epc,id=mem1,size=28M,prealloc=on,host-nodes=1,policy=bind \
-numa node,nodeid=1,cpus=2-3,memdev=node1 \
-M sgx-epc.0.memdev=mem0,sgx-epc.0.node=0,sgx-epc.1.memdev=mem1,sgx-epc.1.node=1 \
......
Signed-off-by: Yang Zhong <yang.zhong@intel.com>
Message-Id: <20211101162009.62161-2-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-11-01 19:20:05 +03:00
|
|
|
DEFINE_PROP_UINT32(SGX_EPC_NUMA_NODE_PROP, SGXEPCDevice, node, 0),
|
2021-07-19 14:21:07 +03:00
|
|
|
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);
|
vl: Add sgx compound properties to expose SGX EPC sections to guest
Because SGX EPC is enumerated through CPUID, EPC "devices" need to be
realized prior to realizing the vCPUs themselves, i.e. long before
generic devices are parsed and realized. From a virtualization
perspective, the CPUID aspect also means that EPC sections cannot be
hotplugged without paravirtualizing the guest kernel (hardware does
not support hotplugging as EPC sections must be locked down during
pre-boot to provide EPC's security properties).
So even though EPC sections could be realized through the generic
-devices command, they need to be created much earlier for them to
actually be usable by the guest. Place all EPC sections in a
contiguous block, somewhat arbitrarily starting after RAM above 4g.
Ensuring EPC is in a contiguous region simplifies calculations, e.g.
device memory base, PCI hole, etc..., allows dynamic calculation of the
total EPC size, e.g. exposing EPC to guests does not require -maxmem,
and last but not least allows all of EPC to be enumerated in a single
ACPI entry, which is expected by some kernels, e.g. Windows 7 and 8.
The new compound properties command for sgx like below:
......
-object memory-backend-epc,id=mem1,size=28M,prealloc=on \
-object memory-backend-epc,id=mem2,size=10M \
-M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Yang Zhong <yang.zhong@intel.com>
Message-Id: <20210719112136.57018-6-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-09-28 11:40:58 +03:00
|
|
|
MemoryDeviceState *md = MEMORY_DEVICE(dev);
|
|
|
|
SGXEPCState *sgx_epc = &pcms->sgx_epc;
|
2021-07-19 14:21:07 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
vl: Add sgx compound properties to expose SGX EPC sections to guest
Because SGX EPC is enumerated through CPUID, EPC "devices" need to be
realized prior to realizing the vCPUs themselves, i.e. long before
generic devices are parsed and realized. From a virtualization
perspective, the CPUID aspect also means that EPC sections cannot be
hotplugged without paravirtualizing the guest kernel (hardware does
not support hotplugging as EPC sections must be locked down during
pre-boot to provide EPC's security properties).
So even though EPC sections could be realized through the generic
-devices command, they need to be created much earlier for them to
actually be usable by the guest. Place all EPC sections in a
contiguous block, somewhat arbitrarily starting after RAM above 4g.
Ensuring EPC is in a contiguous region simplifies calculations, e.g.
device memory base, PCI hole, etc..., allows dynamic calculation of the
total EPC size, e.g. exposing EPC to guests does not require -maxmem,
and last but not least allows all of EPC to be enumerated in a single
ACPI entry, which is expected by some kernels, e.g. Windows 7 and 8.
The new compound properties command for sgx like below:
......
-object memory-backend-epc,id=mem1,size=28M,prealloc=on \
-object memory-backend-epc,id=mem2,size=10M \
-M sgx-epc.0.memdev=mem1,sgx-epc.1.memdev=mem2
Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Signed-off-by: Yang Zhong <yang.zhong@intel.com>
Message-Id: <20210719112136.57018-6-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-09-28 11:40:58 +03:00
|
|
|
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);
|
2021-07-19 14:21:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2021-07-19 14:21:35 +03:00
|
|
|
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);
|
numa: Enable numa for SGX EPC sections
The basic SGX did not enable numa for SGX EPC sections, which
result in all EPC sections located in numa node 0. This patch
enable SGX numa function in the guest and the EPC section can
work with RAM as one numa node.
The Guest kernel related log:
[ 0.009981] ACPI: SRAT: Node 0 PXM 0 [mem 0x180000000-0x183ffffff]
[ 0.009982] ACPI: SRAT: Node 1 PXM 1 [mem 0x184000000-0x185bfffff]
The SRAT table can normally show SGX EPC sections menory info in different
numa nodes.
The SGX EPC numa related command:
......
-m 4G,maxmem=20G \
-smp sockets=2,cores=2 \
-cpu host,+sgx-provisionkey \
-object memory-backend-ram,size=2G,host-nodes=0,policy=bind,id=node0 \
-object memory-backend-epc,id=mem0,size=64M,prealloc=on,host-nodes=0,policy=bind \
-numa node,nodeid=0,cpus=0-1,memdev=node0 \
-object memory-backend-ram,size=2G,host-nodes=1,policy=bind,id=node1 \
-object memory-backend-epc,id=mem1,size=28M,prealloc=on,host-nodes=1,policy=bind \
-numa node,nodeid=1,cpus=2-3,memdev=node1 \
-M sgx-epc.0.memdev=mem0,sgx-epc.0.node=0,sgx-epc.1.memdev=mem1,sgx-epc.1.node=1 \
......
Signed-off-by: Yang Zhong <yang.zhong@intel.com>
Message-Id: <20211101162009.62161-2-yang.zhong@intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2021-11-01 19:20:05 +03:00
|
|
|
se->node = object_property_get_uint(OBJECT(epc), SGX_EPC_NUMA_NODE_PROP,
|
|
|
|
NULL);
|
2021-07-19 14:21:35 +03:00
|
|
|
se->memdev = object_get_canonical_path(OBJECT(epc->hostmem));
|
|
|
|
|
|
|
|
info->u.sgx_epc.data = se;
|
|
|
|
info->type = MEMORY_DEVICE_INFO_KIND_SGX_EPC;
|
2021-07-19 14:21:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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";
|
2021-11-09 20:50:14 +03:00
|
|
|
dc->user_creatable = false;
|
2021-07-19 14:21:07 +03:00
|
|
|
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)
|