On Mon, 11 Oct 2021 at 15:19, Gabriel L. Somlo <gso...@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