qemu/hw/i386/sgx.c
<<
>>
Prefs
   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 "monitor/monitor.h"
  19#include "monitor/hmp-target.h"
  20#include "qapi/error.h"
  21#include "qapi/qapi-commands-misc-target.h"
  22#include "exec/address-spaces.h"
  23#include "sysemu/hw_accel.h"
  24#include "sysemu/reset.h"
  25#include <sys/ioctl.h>
  26
  27#define SGX_MAX_EPC_SECTIONS            8
  28#define SGX_CPUID_EPC_INVALID           0x0
  29
  30/* A valid EPC section. */
  31#define SGX_CPUID_EPC_SECTION           0x1
  32#define SGX_CPUID_EPC_MASK              0xF
  33
  34#define SGX_MAGIC 0xA4
  35#define SGX_IOC_VEPC_REMOVE_ALL       _IO(SGX_MAGIC, 0x04)
  36
  37#define RETRY_NUM                       2
  38
  39static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high)
  40{
  41    return (low & MAKE_64BIT_MASK(12, 20)) +
  42           ((high & MAKE_64BIT_MASK(0, 20)) << 32);
  43}
  44
  45static uint64_t sgx_calc_host_epc_section_size(void)
  46{
  47    uint32_t i, type;
  48    uint32_t eax, ebx, ecx, edx;
  49    uint64_t size = 0;
  50
  51    for (i = 0; i < SGX_MAX_EPC_SECTIONS; i++) {
  52        host_cpuid(0x12, i + 2, &eax, &ebx, &ecx, &edx);
  53
  54        type = eax & SGX_CPUID_EPC_MASK;
  55        if (type == SGX_CPUID_EPC_INVALID) {
  56            break;
  57        }
  58
  59        if (type != SGX_CPUID_EPC_SECTION) {
  60            break;
  61        }
  62
  63        size += sgx_calc_section_metric(ecx, edx);
  64    }
  65
  66    return size;
  67}
  68
  69static void sgx_epc_reset(void *opaque)
  70{
  71    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
  72    HostMemoryBackend *hostmem;
  73    SGXEPCDevice *epc;
  74    int failures;
  75    int fd, i, j, r;
  76    static bool warned = false;
  77
  78    /*
  79     * The second pass is needed to remove SECS pages that could not
  80     * be removed during the first.
  81     */
  82    for (i = 0; i < RETRY_NUM; i++) {
  83        failures = 0;
  84        for (j = 0; j < pcms->sgx_epc.nr_sections; j++) {
  85            epc = pcms->sgx_epc.sections[j];
  86            hostmem = MEMORY_BACKEND(epc->hostmem);
  87            fd = memory_region_get_fd(host_memory_backend_get_memory(hostmem));
  88
  89            r = ioctl(fd, SGX_IOC_VEPC_REMOVE_ALL);
  90            if (r == -ENOTTY && !warned) {
  91                warned = true;
  92                warn_report("kernel does not support SGX_IOC_VEPC_REMOVE_ALL");
  93                warn_report("SGX might operate incorrectly in the guest after reset");
  94                break;
  95            } else if (r > 0) {
  96                /* SECS pages remain */
  97                failures++;
  98                if (i == 1) {
  99                    error_report("cannot reset vEPC section %d", j);
 100                }
 101            }
 102        }
 103        if (!failures) {
 104            break;
 105        }
 106     }
 107}
 108
 109SGXInfo *qmp_query_sgx_capabilities(Error **errp)
 110{
 111    SGXInfo *info = NULL;
 112    uint32_t eax, ebx, ecx, edx;
 113
 114    int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
 115    if (fd < 0) {
 116        error_setg(errp, "SGX is not enabled in KVM");
 117        return NULL;
 118    }
 119
 120    info = g_new0(SGXInfo, 1);
 121    host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx);
 122
 123    info->sgx = ebx & (1U << 2) ? true : false;
 124    info->flc = ecx & (1U << 30) ? true : false;
 125
 126    host_cpuid(0x12, 0, &eax, &ebx, &ecx, &edx);
 127    info->sgx1 = eax & (1U << 0) ? true : false;
 128    info->sgx2 = eax & (1U << 1) ? true : false;
 129
 130    info->section_size = sgx_calc_host_epc_section_size();
 131
 132    close(fd);
 133
 134    return info;
 135}
 136
 137SGXInfo *qmp_query_sgx(Error **errp)
 138{
 139    SGXInfo *info = NULL;
 140    X86MachineState *x86ms;
 141    PCMachineState *pcms =
 142        (PCMachineState *)object_dynamic_cast(qdev_get_machine(),
 143                                              TYPE_PC_MACHINE);
 144    if (!pcms) {
 145        error_setg(errp, "SGX is only supported on PC machines");
 146        return NULL;
 147    }
 148
 149    x86ms = X86_MACHINE(pcms);
 150    if (!x86ms->sgx_epc_list) {
 151        error_setg(errp, "No EPC regions defined, SGX not available");
 152        return NULL;
 153    }
 154
 155    SGXEPCState *sgx_epc = &pcms->sgx_epc;
 156    info = g_new0(SGXInfo, 1);
 157
 158    info->sgx = true;
 159    info->sgx1 = true;
 160    info->sgx2 = true;
 161    info->flc = true;
 162    info->section_size = sgx_epc->size;
 163
 164    return info;
 165}
 166
 167void hmp_info_sgx(Monitor *mon, const QDict *qdict)
 168{
 169    Error *err = NULL;
 170    g_autoptr(SGXInfo) info = qmp_query_sgx(&err);
 171
 172    if (err) {
 173        error_report_err(err);
 174        return;
 175    }
 176    monitor_printf(mon, "SGX support: %s\n",
 177                   info->sgx ? "enabled" : "disabled");
 178    monitor_printf(mon, "SGX1 support: %s\n",
 179                   info->sgx1 ? "enabled" : "disabled");
 180    monitor_printf(mon, "SGX2 support: %s\n",
 181                   info->sgx2 ? "enabled" : "disabled");
 182    monitor_printf(mon, "FLC support: %s\n",
 183                   info->flc ? "enabled" : "disabled");
 184    monitor_printf(mon, "size: %" PRIu64 "\n",
 185                   info->section_size);
 186}
 187
 188bool sgx_epc_get_section(int section_nr, uint64_t *addr, uint64_t *size)
 189{
 190    PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
 191    SGXEPCDevice *epc;
 192
 193    if (pcms->sgx_epc.size == 0 || pcms->sgx_epc.nr_sections <= section_nr) {
 194        return true;
 195    }
 196
 197    epc = pcms->sgx_epc.sections[section_nr];
 198
 199    *addr = epc->addr;
 200    *size = memory_device_get_region_size(MEMORY_DEVICE(epc), &error_fatal);
 201
 202    return false;
 203}
 204
 205void pc_machine_init_sgx_epc(PCMachineState *pcms)
 206{
 207    SGXEPCState *sgx_epc = &pcms->sgx_epc;
 208    X86MachineState *x86ms = X86_MACHINE(pcms);
 209    SgxEPCList *list = NULL;
 210    Object *obj;
 211
 212    memset(sgx_epc, 0, sizeof(SGXEPCState));
 213    if (!x86ms->sgx_epc_list) {
 214        return;
 215    }
 216
 217    sgx_epc->base = 0x100000000ULL + x86ms->above_4g_mem_size;
 218
 219    memory_region_init(&sgx_epc->mr, OBJECT(pcms), "sgx-epc", UINT64_MAX);
 220    memory_region_add_subregion(get_system_memory(), sgx_epc->base,
 221                                &sgx_epc->mr);
 222
 223    for (list = x86ms->sgx_epc_list; list; list = list->next) {
 224        obj = object_new("sgx-epc");
 225
 226        /* set the memdev link with memory backend */
 227        object_property_parse(obj, SGX_EPC_MEMDEV_PROP, list->value->memdev,
 228                              &error_fatal);
 229        object_property_set_bool(obj, "realized", true, &error_fatal);
 230        object_unref(obj);
 231    }
 232
 233    if ((sgx_epc->base + sgx_epc->size) < sgx_epc->base) {
 234        error_report("Size of all 'sgx-epc' =0x%"PRIu64" causes EPC to wrap",
 235                     sgx_epc->size);
 236        exit(EXIT_FAILURE);
 237    }
 238
 239    memory_region_set_size(&sgx_epc->mr, sgx_epc->size);
 240
 241    /* register the reset callback for sgx epc */
 242    qemu_register_reset(sgx_epc_reset, NULL);
 243}
 244