Add AppleT2Smc module: Apple T2 SMC over MMIO - #71
Conversation
On 2018-2020 Intel Macs the T2 chip intercepts the legacy SMC I/O-port protocol, so the SMC is only reachable through an MMIO window at 0xFE0B0000. This module maps that window and performs the SMC read/get-key/write handshake in kernel mode, exposing only SMC operations (no raw physical memory access). Writes are restricted by an allowlist to fan-control keys (F<n>Tg, F<n>Md, FS!). Register offsets, access widths and protocol follow the Linux T2 applesmc driver.
|
Hi @namazso — friendly bump on this one, no rush 🙂 It's been a few weeks so I wanted to check whether there's anything you'd like changed before it could be considered for signing. Quick recap: the module only maps the T2 SMC MMIO window (0xFE0B0000) and exposes read / get-key / write, with writes restricted by an allowlist to fan-control keys so it can't poke arbitrary SMC state. CI builds green, and another T2 Mac owner has reviewed and tested it. Happy to make any adjustments you'd want. Thanks for maintaining PawnIO! |
The module mapped a hardcoded physical address and immediately wrote the key/command registers to probe FNum, so on a machine where that window belongs to another device it wrote there before finding out. Follow what Apple's driver and the Linux port do instead: - refuse to load on non-Intel CPUs - read the status register first and bail on 0xff, before any write - replace the FNum probe with the protocol's own LDKN >= 2 gate - map 0x4006 (APPLESMC_IOMEM_MIN_SIZE) rather than a full 64K Also read the GET_KEY_TYPE type code with a single dword read, matching the driver's ioread32 instead of four byte reads, and document that the base address is a constant here because PawnIO has no ACPI access.
|
I've pushed a revision that addresses what I now think was the actual problem with this PR, and I should have caught it earlier. The module mapped a hardcoded physical address and then immediately wrote the key/command registers to probe What it does now, following Apple's driver and the Linux port:
I also switched the One limitation I want to be upfront about: the Linux driver takes the MMIO base from the ACPI On the "not proper first party drivers" guideline: there is no first-party driver here. Apple's Boot Camp package installs no SMC driver on Windows — on my Mac the only Two things I would like your read on:
Happy to change anything else you'd like. |
Each of the load-time gates now prints its own reason, so a tester who captures debug output can tell an unsupported machine apart from a wrong base address or an SMC that answered but reported an old LDKN. Also add the trailing newline the other modules use.
Yes, but not per module identity, ie two different programs can load separate copies of your module. You should probably recommend using some global mutant to userspace if such serialization is required, and hope for the best.
No, since a faulty signed module cannot be undone. Additionally, we now have DMI checks possible via registry, see ValveLeds.p. I'd hope that it makes it possible to verify Apple hardware during load. I put this back into a draft until it's tested at least. |
namazso pointed out that registry-backed DMI checks are now available (LedsValve.p). The MMIO base here is a compile-time constant, so this is the check that keeps it from being touched on a machine where that address belongs to something else: read SystemManufacturer from \Registry\Machine\HARDWARE\DESCRIPTION\System\BIOS and refuse to load unless it says Apple. It runs before anything is mapped, and it is a read, so nothing is written on hardware this module has no business on. The status-register and LDKN gates stay as the second and third checks: Apple firmware alone does not mean the window is a T2 SMC.
|
Tested on real T2 hardware, two machines, PawnIO 2.0.1 unrestricted:
The tested binary is One bug surfaced during testing and it was in my app, not the module: T2 fan keys are Note for anyone else testing: the "unrestricted" option in PawnIO_setup 2.1.0/2.2.0 installs a driver that still enforces signatures (PawnIO.Setup#11) — 2.0.1 works. Marking ready for review. |
|
hey @namazso , just wanted to tell smth in as one of the testers. ive been using this build on my MacBookPro15,1 (Windows 10) for daily use with RPMac, and the module has been solid. all SMC sensor readings, fan curves, and manual setpoints work smoothly with zero issues or instability. huge thanks to @golirt1 for getting the DMI check integrated. really hoping to see this signed and merged whenever you have time to review! RPMac_wF3pwJywYZ.mp4 |
Adds a module for the SMC on 2018-2020 Intel Macs with the Apple T2 chip.
On those machines the T2 intercepts the legacy SMC I/O-port protocol
(0x300/0x304), so the usual port handshake returns garbage under Windows. The T2
instead exposes the SMC through an MMIO window at physical
0xFE0B0000.The module maps that window and performs the SMC request/response handshake in
kernel mode, exposing only SMC operations - never raw physical memory access:
ioctl_smc_read- READ (0x10), GET_KEY_BY_INDEX (0x12), GET_KEY_TYPE (0x13)ioctl_smc_write- WRITE (0x11)Writes are restricted by an allowlist to fan-control keys only (
F<n>Tg,F<n>Md,FS!), so the module can't be used to poke arbitrary SMC state -only fan speed and mode, which is its purpose.
Load-time gates
Nothing is written to the window until it has identified itself, in this order:
STATUS_NOT_SUPPORTED, nothing is mapped at all0xff-> unmap and decline, stillwithout having written anything
LDKN(the SMC key-interface version) and require>= 2- this is thefirst access that writes, and only runs once step 2 has passed
Each gate logs its reason through
debug_print, so an unsupported machine canbe told apart from a wrong base address or an SMC reporting an old
LDKN.Only
0x4006is mapped (APPLESMC_IOMEM_MIN_SIZE), which is the last byte theprotocol needs, so no unrelated MMIO beyond the status register is ever mapped.
Reference
Register offsets, access widths (control registers are 32-bit writes;
status/length/error are byte reads; the data buffer is byte-addressed), the
completion-status bit (0x20), the exponential backoff, the
0xffstatus checkand the
LDKN >= 2gate all mirror the Linux T2applesmcdriver(MCMrARM/mbp2018-etc,
applesmc_t2_kmod.c).GET_KEY_TYPE's special MMIOresult layout (type@0, datalen@5, flags@6) is repacked into the conventional
[len, type, flags]form; the type code is read as a single dword to match thedriver's
ioread32.Known limitation
The Linux driver takes the MMIO base from the ACPI
_CRSof deviceAPP0001.PawnIO has no ACPI access, so the base is a compile-time constant here, taken
from the machines it has been observed on. If a T2 model places the window
elsewhere, the gates above fail and the module declines to load rather than
touching the wrong device - but it would also mean no support on that model
until the address is known. I'd rather be explicit about this than hide it.
Notes
the register sequence and I don't see a locking primitive in the includes, so
I'm not sure whether I need to handle that myself.