[PATCH] isa-applesmc: provide OSK forwarding on Apple hosts

Vladislav Yaroshchuk posted 1 patch 2 years, 5 months ago
Test checkpatch passed
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20211021172845.69316-1-yaroshchuk2000@gmail.com
There is a newer version of this series
hw/misc/applesmc.c | 129 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 129 insertions(+)
[PATCH] isa-applesmc: provide OSK forwarding on Apple hosts
Posted by Vladislav Yaroshchuk 2 years, 5 months ago
On Apple hosts we can read AppleSMC OSK directly from SMC
and forward this value to QEMU

Signed-off-by: Vladislav Yaroshchuk <yaroshchuk2000@gmail.com>
---
 hw/misc/applesmc.c | 129 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)

diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c
index 1b9acaf1d3..d4a6d1eaab 100644
--- a/hw/misc/applesmc.c
+++ b/hw/misc/applesmc.c
@@ -38,6 +38,10 @@
 #include "qemu/timer.h"
 #include "qom/object.h"
 
+#if defined(__APPLE__)
+#include <IOKit/IOKitLib.h>
+#endif
+
 /* #define DEBUG_SMC */
 
 #define APPLESMC_DEFAULT_IOBASE        0x300
@@ -108,6 +112,7 @@ struct AppleSMCState {
     uint8_t data_len;
     uint8_t data_pos;
     uint8_t data[255];
+    char *oskdirect;
     char *osk;
     QLIST_HEAD(, AppleSMCData) data_def;
 };
@@ -312,6 +317,124 @@ static const MemoryRegionOps applesmc_err_io_ops = {
     },
 };
 
+#if defined(__APPLE__)
+/* Based on http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ */
+enum {
+    SMC_CLIENT_OPEN      = 0,
+    SMC_CLIENT_CLOSE     = 1,
+    SMC_HANDLE_EVENT     = 2,
+    SMC_READ_KEY         = 5
+};
+
+struct AppleSMCParam {
+    uint32_t    key;
+    uint8_t     pad0[22];
+    IOByteCount data_size;
+    uint8_t     pad1[10];
+    uint8_t     command;
+    uint32_t    pad2;
+    uint8_t     bytes[32];
+};
+
+static void applesmc_direct_read_osk(DeviceState *dev, Error **errp)
+{
+    AppleSMCState           *s = APPLE_SMC(dev);
+    io_service_t            realsmc = IO_OBJECT_NULL;
+    io_connect_t            realsmc_connect = IO_OBJECT_NULL;
+    size_t                  out_size = sizeof(struct AppleSMCParam);
+    IOReturn                status = kIOReturnError;
+    struct AppleSMCParam    in = {0};
+    struct AppleSMCParam    out = {0};
+    char                    *osk_buffer;
+
+    /* OSK key size + '\0' */
+    osk_buffer = g_malloc0(65);
+    osk_buffer[64] = '\0';
+
+    realsmc = IOServiceGetMatchingService(kIOMasterPortDefault,
+                                          IOServiceMatching("AppleSMC"));
+    if (realsmc == IO_OBJECT_NULL) {
+        warn_report("host AppleSMC device is unreachable");
+        goto osk_buffer_free;
+    }
+
+    status = IOServiceOpen(realsmc, mach_task_self(), 1, &realsmc_connect);
+    if (status != kIOReturnSuccess || realsmc_connect == IO_OBJECT_NULL) {
+        warn_report("host AppleSMC device is unreachable");
+        goto osk_buffer_free;
+    }
+
+    status = IOConnectCallMethod(
+        realsmc_connect,
+        SMC_CLIENT_OPEN,
+        NULL, 0, NULL, 0, NULL, NULL, NULL, NULL
+    );
+    if (status != kIOReturnSuccess) {
+        warn_report("host AppleSMC device is unreachable");
+        goto ioservice_close;
+    }
+
+    in.key = ('OSK0');
+    in.data_size = sizeof(out.bytes);
+    in.command = SMC_READ_KEY;
+    status = IOConnectCallStructMethod(
+        realsmc_connect,
+        SMC_HANDLE_EVENT,
+        &in,
+        sizeof(struct AppleSMCParam),
+        &out,
+        &out_size
+    );
+
+    if (status != kIOReturnSuccess) {
+        warn_report("unable to read OSK0 from host AppleSMC device");
+        goto ioconnect_close;
+    }
+    strncpy(osk_buffer, (const char *) out.bytes, 32);
+
+    in.key = ('OSK1');
+    status = IOConnectCallStructMethod(
+        realsmc_connect,
+        SMC_HANDLE_EVENT,
+        &in,
+        sizeof(struct AppleSMCParam),
+        &out,
+        &out_size
+    );
+    if (status != kIOReturnSuccess) {
+        warn_report("unable to read OSK1 from host AppleSMC device");
+        goto ioconnect_close;
+    }
+    strncpy(osk_buffer + 32, (const char *) out.bytes, 32);
+
+    s->osk = osk_buffer;
+
+    IOConnectCallMethod(
+        realsmc_connect,
+        SMC_CLIENT_CLOSE,
+        NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
+    IOServiceClose(realsmc_connect);
+    return;
+
+ioconnect_close:
+    IOConnectCallMethod(
+        realsmc_connect,
+        SMC_CLIENT_CLOSE,
+        NULL, 0, NULL, 0, NULL, NULL, NULL, NULL);
+ioservice_close:
+    IOServiceClose(realsmc_connect);
+
+osk_buffer_free:
+    g_free(osk_buffer);
+}
+#else
+static void applesmc_direct_read_osk(DeviceState *dev, Error **errp)
+{
+    warn_report("isa-applesmc.oskdirect ignored: "
+                "unsupported on non-Apple hosts");
+}
+#endif
+
 static void applesmc_isa_realize(DeviceState *dev, Error **errp)
 {
     AppleSMCState *s = APPLE_SMC(dev);
@@ -331,6 +454,11 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp)
     isa_register_ioport(&s->parent_obj, &s->io_err,
                         s->iobase + APPLESMC_ERR_PORT);
 
+    /* Use key retrieved directly from real SMC has higher priority */
+    if (s->oskdirect && !strcmp("on", s->oskdirect)) {
+        applesmc_direct_read_osk(dev, errp);
+    }
+
     if (!s->osk || (strlen(s->osk) != 64)) {
         warn_report("Using AppleSMC with invalid key");
         s->osk = default_osk;
@@ -344,6 +472,7 @@ static Property applesmc_isa_properties[] = {
     DEFINE_PROP_UINT32(APPLESMC_PROP_IO_BASE, AppleSMCState, iobase,
                        APPLESMC_DEFAULT_IOBASE),
     DEFINE_PROP_STRING("osk", AppleSMCState, osk),
+    DEFINE_PROP_STRING("oskdirect", AppleSMCState, oskdirect),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-- 
2.23.0