xref: /qemu/hw/i386/sgx.c (revision 654d6b04)
1 /*
2  * SGX common code
3  *
4  * Copyright (C) 2021 Intel Corporation
5  *
6  * Authors:
7  *   Yang Zhong<yang.zhong@intel.com>
8  *   Sean Christopherson <sean.j.christopherson@intel.com>
9  *
10  * This work is licensed under the terms of the GNU GPL, version 2 or later.
11  * See the COPYING file in the top-level directory.
12  */
13 #include "qemu/osdep.h"
14 #include "hw/i386/pc.h"
15 #include "hw/i386/sgx-epc.h"
16 #include "hw/mem/memory-device.h"
17 #include "monitor/qdev.h"
18 #include "qapi/error.h"
19 #include "exec/address-spaces.h"
20 #include "hw/i386/sgx.h"
21 #include "sysemu/hw_accel.h"
22 
23 #define SGX_MAX_EPC_SECTIONS            8
24 #define SGX_CPUID_EPC_INVALID           0x0
25 
26 /* A valid EPC section. */
27 #define SGX_CPUID_EPC_SECTION           0x1
28 #define SGX_CPUID_EPC_MASK              0xF
29 
30 static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
31 {
32     return (low & MAKE_64BIT_MASK(12, 20)) +
33            ((high & MAKE_64BIT_MASK(0, 20)) << 32);
34 }
35 
36 static uint64_t sgx_calc_host_epc_section_size(void)
37 {
38     uint32_t i, type;
39     uint32_t eax, ebx, ecx, edx;
40     uint64_t size = 0;
41 
42     for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
43         host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
44 
45         type = eax & SGX_CPUID_EPC_MASK;
46         if (type == SGX_CPUID_EPC_INVALID) {
47             break;
48         }
49 
50         if (type != SGX_CPUID_EPC_SECTION) {
51             break;
52         }
53 
54         size += sgx_calc_section_metric(ecx, edx);
55     }
56 
57     return size;
58 }
59 
60 SGXInfo *sgx_get_capabilities(Error **errp)
61 {
62     SGXInfo *info = NULL;
63     uint32_t eax, ebx, ecx, edx;
64 
65     int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
66     if (fd < 0) {
67         error_setg(errp, "SGX is not enabled in KVM");
68         return NULL;
69     }
70 
71     info = g_new0(SGXInfo, 1);
72     host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
73 
74     info->sgx = ebx & (1U << 2) ? true : false;
75     info->flc = ecx & (1U << 30) ? true : false;
76 
77     host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
78     info->sgx1 = eax & (1U << 0) ? true : false;
79     info->sgx2 = eax & (1U << 1) ? true : false;
80 
81     info->section_size = sgx_calc_host_epc_section_size();
82 
83     close(fd);
84 
85     return info;
86 }
87 
88 SGXInfo *sgx_get_info(Error **errp)
89 {
90     SGXInfo *info = NULL;
91     X86MachineState *x86ms;
92     PCMachineState *pcms =
93         (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
94                                               TYPE_PC_MACHINE);
95     if (!pcms) {
96         error_setg(errp, "SGX is only supported on PC machines");
97         return NULL;
98     }
99 
100     x86ms = X86_MACHINE(pcms);
101     if (!x86ms->sgx_epc_list) {
102         error_setg(errp, "No EPC regions defined, SGX not available");
103         return NULL;
104     }
105 
106     SGXEPCState *sgx_epc = &pcms->sgx_epc;
107     info = g_new0(SGXInfo, 1);
108 
109     info->sgx = true;
110     info->sgx1 = true;
111     info->sgx2 = true;
112     info->flc = true;
113     info->section_size = sgx_epc->size;
114 
115     return info;
116 }
117 
118 int sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
119 {
120     PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
121     SGXEPCDevice *epc;
122 
123     if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
124         return 1;
125     }
126 
127     epc = pcms->sgx_epc.sections[section_nr];
128 
129     *addr = epc->addr;
130     *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
131 
132     return 0;
133 }
134 
135 void pc_machine_init_sgx_epc(PCMachineState *pcms)
136 {
137     SGXEPCState *sgx_epc = &pcms->sgx_epc;
138     X86MachineState *x86ms = X86_MACHINE(pcms);
139     SgxEPCList *list = NULL;
140     Object *obj;
141 
142     memset(sgx_epc, 0, sizeof(SGXEPCState));
143     if (!x86ms->sgx_epc_list) {
144         return;
145     }
146 
147     sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size;
148 
149     memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
150     memory_region_add_subregion(get_system_memory(), sgx_epc->base,
151                                 &sgx_epc->mr);
152 
153     for (list = x86ms->sgx_epc_list; list; list = list->next) {
154         obj = object_new("sgx-epc");
155 
156         /* set the memdev link with memory backend */
157         object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
158                               &error_fatal);
159         object_property_set_bool(obj, "realized", true, &error_fatal);
160         object_unref(obj);
161     }
162 
163     if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
164         error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap",
165                      sgx_epc->size);
166         exit(EXIT_FAILURE);
167     }
168 
169     memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
170 }
171