qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] hw/misc: applesmc: use host osk as default on macs


From: Phil Dennis-Jordan
Subject: Re: [PATCH] hw/misc: applesmc: use host osk as default on macs
Date: Wed, 13 Oct 2021 22:03:55 +0200

On Mon, 11 Oct 2021 at 15:19, Gabriel L. Somlo <gsomlo@gmail.com> wrote:
Given *this* conversation, it might be worth someone's effort to try
that approach again. IMO it's really the most efficient: have an
already existing applesmc driver in the hypervisor's kernel expose the
desired key values (it's whole job is to expose key values to
userspace in the first place). Then have userspace read that and use
it for whatever purposes (including populating guest-facing emulated
smc devices). Nobody has to use anyone's copyrighted code or strings
or whatever. If only the hwmon folks could be convinced this time
around :)

This is very similar to the situation on macOS. The way to request the information from the kernel driver from userspace would of course be different. (IOKitLib on macOS, sysfs or perhaps ioctls on some /dev node on Linux.)

I can give a quick rundown on the situation on macOS:
- The SMC device is matched by the AppleSMC.kext kernel-mode driver.
- This driver provides a bunch of other functionality such the SMC's hardware watchdog etc., but also the basics of reading these SMC keys.
- The driver offers a userspace-facing API via the "I/O Kit" HAL mechanism that underpins almost all interfacing with device drivers on macOS.
- The IOKit node in question is "AppleSMC"; this service can be "opened" (IOServiceOpen()) by user processes, with parameter type=0.
- With an open service connection, there are a number of different "method calls" (via the IOConnectCall*Method() family of functions) that can be invoked on the driver.

>From here onwards you pretty much have to look at the source code that Pedro linked in the original message, there's no actual documentation.

- One of these method calls (selector=2, Apple calls this kSMCHandleYPCEvent) accepts a command struct (Apple calls this SMCParamStruct) and returns a modified version of this struct. This allows you to perform operations on each of the many defined "Keys" in the SMC. (The keys are identified via 4 bytes which can be interpreted as 4 ASCII characters, like FourCC codes - GCC and clang support this by multi-character character literals, e.g. 'OSK0'. The key is selected in the first 4 bytes of the struct.)
- Within this struct there's a kind of command selector (data8 field, 1 byte, offset 42), one possible value is kSMCReadKey = 5 for reading the key value. In that case you also need to provide space at the end of the output struct (offset 48) for the read data and indicate in a size field (offset 28, uint32) how many bytes you're reading. (32 bytes seems to be the maximum, which also coincides with the sizes of each of the OSK halves. This means the struct is 80 bytes in total.) Everything else can be 0-initialised. There is a 1-byte "result" field at offset 20 which will be 0 if the call was successful. (And indeed the IOConnectCall*Method() library function itself must return kIOReturnSuccess = 0 as well.)

To read the 2 OSK keys on macOS therefore, you need to perform 5 steps:
1. Find the AppleSMC IOKit service:
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC"));
2. Open a connection to the service (assuming it was found):
IOServiceOpen(service, mach_task_self(), 0, &connection);
3. Initialise the SMC command struct with key 'OSK0', command 5 (read key), and set the data size to 32 for reading 32 bytes of data. Pass this as the "input" struct argument to IOConnectCallStructMethod(connection, …) and a pointer to another instance of such a struct as the output, and use method selector 2.
4. Same for 'OSK1'
5. Clean up by closing the service connection (IOServiceClose) and releasing the service handle. (IOObjectReturn)

Assuming appropriate error checking at every stage, the values for the 2 keys will be in the data fields of the respective output structs.

From this I *think* it should be possible to put together a working implementation on macOS hosts. Pedro's original code did a lot more, but anything outside of the above is essentially fluff.


Support for Linux would be great too; does the applesmc driver create a node in /dev? If so, perhaps we can persuade the maintainers to accept a patch with an ioctl for submitting commands directly to the SMC? Then it's not specifically the OSK keys, but *any* key. The device supports more keys than it publicly advertises, after all. (Such a feature would be useful for improving qemu's virtual AppleSMC incidentally - there's a ~10 second boot delay for macOS guests and I strongly suspect it's at least in part down to the SMC not behaving as the OS expects. For example, the OS wants the watchdog feature, but Qemu's virtual SMC does not provide it. If we could easily submit arbitrary SMC commands to the physical device from a guest running inside Qemu during development, we could work out some of that hidden behaviour.)

Happy to answer any questions on the macOS side - for what it's worth, I'm not affiliated with Apple in any way, but I do a lot of systems-level development on their platforms and know the IOKit inside out.

Hope that helps!
Phil

reply via email to

[Prev in Thread] Current Thread [Next in Thread]