summaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/i386/x86.c25
-rw-r--r--hw/misc/applesmc.c192
2 files changed, 215 insertions, 2 deletions
diff --git a/hw/i386/x86.c b/hw/i386/x86.c
index 41ef9a84a9..0c7c054e3a 100644
--- a/hw/i386/x86.c
+++ b/hw/i386/x86.c
@@ -47,6 +47,7 @@
#include "hw/i386/fw_cfg.h"
#include "hw/intc/i8259.h"
#include "hw/rtc/mc146818rtc.h"
+#include "target/i386/sev_i386.h"
#include "hw/acpi/cpu_hotplug.h"
#include "hw/irq.h"
@@ -780,6 +781,7 @@ void x86_load_linux(X86MachineState *x86ms,
const char *initrd_filename = machine->initrd_filename;
const char *dtb_filename = machine->dtb;
const char *kernel_cmdline = machine->kernel_cmdline;
+ SevKernelLoaderContext sev_load_ctx = {};
/* Align to 16 bytes as a paranoia measure */
cmdline_size = (strlen(kernel_cmdline) + 16) & ~15;
@@ -926,6 +928,8 @@ void x86_load_linux(X86MachineState *x86ms,
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr);
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1);
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
+ sev_load_ctx.cmdline_data = (char *)kernel_cmdline;
+ sev_load_ctx.cmdline_size = strlen(kernel_cmdline) + 1;
if (protocol >= 0x202) {
stl_p(header + 0x228, cmdline_addr);
@@ -1007,6 +1011,8 @@ void x86_load_linux(X86MachineState *x86ms,
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr);
fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size);
+ sev_load_ctx.initrd_data = initrd_data;
+ sev_load_ctx.initrd_size = initrd_size;
stl_p(header + 0x218, initrd_addr);
stl_p(header + 0x21c, initrd_size);
@@ -1065,15 +1071,32 @@ void x86_load_linux(X86MachineState *x86ms,
load_image_size(dtb_filename, setup_data->data, dtb_size);
}
- memcpy(setup, header, MIN(sizeof(header), setup_size));
+ /*
+ * If we're starting an encrypted VM, it will be OVMF based, which uses the
+ * efi stub for booting and doesn't require any values to be placed in the
+ * kernel header. We therefore don't update the header so the hash of the
+ * kernel on the other side of the fw_cfg interface matches the hash of the
+ * file the user passed in.
+ */
+ if (!sev_enabled()) {
+ memcpy(setup, header, MIN(sizeof(header), setup_size));
+ }
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size);
+ sev_load_ctx.kernel_data = (char *)kernel;
+ sev_load_ctx.kernel_size = kernel_size;
fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr);
fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
+ sev_load_ctx.setup_data = (char *)setup;
+ sev_load_ctx.setup_size = setup_size;
+
+ if (sev_enabled()) {
+ sev_add_kernel_loader_hashes(&sev_load_ctx, &error_fatal);
+ }
option_rom[nb_option_roms].bootindex = 0;
option_rom[nb_option_roms].name = "linuxboot.bin";
diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c
index 1b9acaf1d3..cec247b5ee 100644
--- a/hw/misc/applesmc.c
+++ b/hw/misc/applesmc.c
@@ -38,6 +38,171 @@
#include "qemu/timer.h"
#include "qom/object.h"
+#if defined(__APPLE__) && defined(__MACH__)
+#include <IOKit/IOKitLib.h>
+
+enum {
+ kSMCSuccess = 0x00,
+ kSMCKeyNotFound = 0x84
+};
+
+enum {
+ kSMCUserClientOpen = 0x00,
+ kSMCUserClientClose = 0x01,
+ kSMCHandleYPCEvent = 0x02,
+ kSMCReadKey = 0x05,
+ kSMCGetKeyInfo = 0x09
+};
+
+typedef struct SMCVersion {
+ uint8_t major;
+ uint8_t minor;
+ uint8_t build;
+ uint8_t reserved;
+ uint16_t release;
+} SMCVersion;
+
+typedef struct SMCPLimitData {
+ uint16_t version;
+ uint16_t length;
+ uint32_t cpuPLimit;
+ uint32_t gpuPLimit;
+ uint32_t memPLimit;
+} SMCPLimitData;
+
+typedef struct SMCKeyInfoData {
+ IOByteCount dataSize;
+ uint32_t dataType;
+ uint8_t dataAttributes;
+} SMCKeyInfoData;
+
+typedef struct {
+ uint32_t key;
+ SMCVersion vers;
+ SMCPLimitData pLimitData;
+ SMCKeyInfoData keyInfo;
+ uint8_t result;
+ uint8_t status;
+ uint8_t data8;
+ uint32_t data32;
+ uint8_t bytes[32];
+} SMCParamStruct;
+
+static IOReturn smc_call_struct_method(uint32_t selector,
+ SMCParamStruct *inputStruct,
+ SMCParamStruct *outputStruct)
+{
+ IOReturn ret;
+
+ size_t inputStructCnt = sizeof(SMCParamStruct);
+ size_t outputStructCnt = sizeof(SMCParamStruct);
+
+ io_service_t smcService = IO_OBJECT_NULL;
+ io_connect_t smcConnect = IO_OBJECT_NULL;
+
+ smcService = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching("AppleSMC"));
+ if (smcService == IO_OBJECT_NULL) {
+ ret = kIOReturnNotFound;
+ goto exit;
+ }
+
+ ret = IOServiceOpen(smcService, mach_task_self(), 1, &smcConnect);
+ if (ret != kIOReturnSuccess) {
+ smcConnect = IO_OBJECT_NULL;
+ goto exit;
+ }
+ if (smcConnect == IO_OBJECT_NULL) {
+ ret = kIOReturnError;
+ goto exit;
+ }
+
+ ret = IOConnectCallMethod(smcConnect, kSMCUserClientOpen,
+ NULL, 0, NULL, 0,
+ NULL, NULL, NULL, NULL);
+ if (ret != kIOReturnSuccess) {
+ goto exit;
+ }
+
+ ret = IOConnectCallStructMethod(smcConnect, selector,
+ inputStruct, inputStructCnt,
+ outputStruct, &outputStructCnt);
+
+exit:
+ if (smcConnect != IO_OBJECT_NULL) {
+ IOConnectCallMethod(smcConnect, kSMCUserClientClose,
+ NULL, 0, NULL, 0, NULL,
+ NULL, NULL, NULL);
+ IOServiceClose(smcConnect);
+ }
+
+ return ret;
+}
+
+static IOReturn smc_read_key(uint32_t key,
+ uint8_t *bytes,
+ IOByteCount *dataSize)
+{
+ IOReturn ret;
+
+ SMCParamStruct inputStruct;
+ SMCParamStruct outputStruct;
+
+ if (key == 0 || bytes == NULL) {
+ ret = kIOReturnCannotWire;
+ goto exit;
+ }
+
+ /* determine key's data size */
+ memset(&inputStruct, 0, sizeof(SMCParamStruct));
+ inputStruct.data8 = kSMCGetKeyInfo;
+ inputStruct.key = key;
+
+ memset(&outputStruct, 0, sizeof(SMCParamStruct));
+ ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct);
+ if (ret != kIOReturnSuccess) {
+ goto exit;
+ }
+ if (outputStruct.result == kSMCKeyNotFound) {
+ ret = kIOReturnNotFound;
+ goto exit;
+ }
+ if (outputStruct.result != kSMCSuccess) {
+ ret = kIOReturnInternalError;
+ goto exit;
+ }
+
+ /* get key value */
+ memset(&inputStruct, 0, sizeof(SMCParamStruct));
+ inputStruct.data8 = kSMCReadKey;
+ inputStruct.key = key;
+ inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize;
+
+ memset(&outputStruct, 0, sizeof(SMCParamStruct));
+ ret = smc_call_struct_method(kSMCHandleYPCEvent, &inputStruct, &outputStruct);
+ if (ret != kIOReturnSuccess) {
+ goto exit;
+ }
+ if (outputStruct.result == kSMCKeyNotFound) {
+ ret = kIOReturnNotFound;
+ goto exit;
+ }
+ if (outputStruct.result != kSMCSuccess) {
+ ret = kIOReturnInternalError;
+ goto exit;
+ }
+
+ memset(bytes, 0, *dataSize);
+ if (*dataSize > inputStruct.keyInfo.dataSize) {
+ *dataSize = inputStruct.keyInfo.dataSize;
+ }
+ memcpy(bytes, outputStruct.bytes, *dataSize);
+
+exit:
+ return ret;
+}
+#endif
+
/* #define DEBUG_SMC */
#define APPLESMC_DEFAULT_IOBASE 0x300
@@ -315,6 +480,7 @@ static const MemoryRegionOps applesmc_err_io_ops = {
static void applesmc_isa_realize(DeviceState *dev, Error **errp)
{
AppleSMCState *s = APPLE_SMC(dev);
+ bool valid_key = false;
memory_region_init_io(&s->io_data, OBJECT(s), &applesmc_data_io_ops, s,
"applesmc-data", 1);
@@ -331,7 +497,31 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp)
isa_register_ioport(&s->parent_obj, &s->io_err,
s->iobase + APPLESMC_ERR_PORT);
- if (!s->osk || (strlen(s->osk) != 64)) {
+ if (s->osk) {
+ valid_key = strlen(s->osk) == 64;
+ } else {
+#if defined(__APPLE__) && defined(__MACH__)
+ IOReturn ret;
+ IOByteCount size = 32;
+
+ ret = smc_read_key('OSK0', (uint8_t *) default_osk, &size);
+ if (ret != kIOReturnSuccess) {
+ goto failure;
+ }
+
+ ret = smc_read_key('OSK1', (uint8_t *) default_osk + size, &size);
+ if (ret != kIOReturnSuccess) {
+ goto failure;
+ }
+
+ warn_report("Using AppleSMC with host key");
+ valid_key = true;
+ s->osk = default_osk;
+failure:;
+#endif
+ }
+
+ if (!valid_key) {
warn_report("Using AppleSMC with invalid key");
s->osk = default_osk;
}