[PATCH 0/2] hw/misc/applesmc: fix GET_KEY_BY_INDEX iteration and populate Apple SMC key set

Matthew Jackson posted 2 patches 1 month ago
Failed in applying to current master (apply log)
There is a newer version of this series
hw/misc/applesmc.c | 344 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 337 insertions(+), 7 deletions(-)
[PATCH 0/2] hw/misc/applesmc: fix GET_KEY_BY_INDEX iteration and populate Apple SMC key set
Posted by Matthew Jackson 1 month ago
The QEMU applesmc device implements just enough of the Apple SMC PMIO
protocol to satisfy the OSK boot check on older macOS versions. On
modern macOS guests (x86 10.14+, all of the 15.x series) the real
AppleSMC kext enumerates the SMC key space at boot via
APPLESMC_GET_KEY_BY_INDEX_CMD (0x12). The current device only
acknowledges APPLESMC_READ_CMD (0x10) at the command port; every
other command falls through to the default arm of the switch and
sets ST_1E_BAD_CMD.

The macOS driver interprets the resulting 0x82 reply as "spurious
data" and enters a retry loop that floods the kernel log with
kSMCSpuriousData (0x81) / kSMCKeyNotFound errors at roughly 1800
events per second, pegging kernel_task at ~70% CPU and WindowServer
at ~509% CPU. This reproduces reliably on any recent macOS 15 guest
booted with -device isa-applesmc,osk=<valid-OSK>.

This two-patch series fixes the protocol-level bug and rounds out
the SMC key table to a complete iMac20,1 profile.

  Patch 1: protocol-level fix
    - Accept WRITE_CMD, GET_KEY_BY_INDEX_CMD, GET_KEY_TYPE_CMD at the
      command port (in addition to READ_CMD).
    - Implement the indexed-iteration walker (returns real key names
      from s->data_def, or APPLESMC_ST_1E_BAD_INDEX 0xb8 once the
      index is past the end so the guest stops iterating).
    - Implement GET_KEY_TYPE returning a 6-byte type/size/attr
      response matching VirtualSMC's kern_pmio.cpp behaviour.
    - Accept and log WRITE_CMD silently.
    - Replace the unknown-key NOEXIST (0x84) reply with a zeroed
      payload of the requested length, logged at LOG_UNIMP.
    - Route the BAD_CMD path through qemu_log_mask(LOG_GUEST_ERROR).
    - Fix MSSD initialiser typo ("\0x3" -> "\x03"). The original
      literal was three bytes ('\0', 'x', '3') truncated to one
      ('\0') by the size argument, so MSSD has been silently
      returning 0 since the device was introduced; the corrected
      value matches what a real iMac20,1 SMC reports.

  Patch 2: populate the key table
    - Add 94 keys covering the categories macOS queries on a Sequoia
      15.7.5 guest: 28 temperature sensors (sp78), 4 fan keys (fpe2),
      12 power-rail keys, 6 DIMM keys, 11 SMC-internal bookkeeping,
      13 motion-sensor / wireless, 3 write targets (HE0N/MSDW/NTOK),
      2 power-management gates (HE2N/WDTC), 8 platform-identity /
      probe keys, plus the Apple-canonical #KEY total-count.
    - Sensor values match a real iMac20,1 idle probe published at
      https://linux-hardware.org/?probe=999fc708a4&log=sensors:
      CPU 40-51 C, GPU 36-42 C, fan at 1200 RPM (= F0Mn idle), etc.

Measured impact (macOS 15.7.5 guest, iMac20,1 profile):

   Metric           | Before   | After
   -----------------|---------:|------:
   SMC errors / 5s  |   9,225  |     2
   kernel_task CPU  |    70 %  |  ~2 %
   WindowServer CPU |   509 %  |  ~6 %

A note for review on the zero-valued keys in patch 2: the 26 keys
covering DIMM / SMC bookkeeping / motion-sensor / wireless rails are
registered with present-with-zero values rather than omitted. macOS
distinguishes "absent" (NOEXIST reply, retry-poll) from "broken"
(present, value 0, accepted-and-ignored). Registering these keys
present-with-zero stops the retry-poll behaviour without asserting
any specific value. If the maintainer prefers a tighter scope for
this series I am happy to drop any subset and follow up; the
present-with-zero approach was driven by which keys macOS observed
querying during boot.

Backwards compatibility: legacy macOS guests (10.11-10.13) which do
not iterate the key space via GET_KEY_BY_INDEX boot unchanged. The
original six keys (REV/OSK0/OSK1/NATJ/MSSP/MSSD) are still present
and respond with the same values, modulo the MSSD typo fix in patch
1 which corrects MSSD to the value a real iMac20,1 SMC reports.

Tested against current master (post v11.0.0). Builds clean on
gcc-13 / clang-17 with --enable-werror. Sequoia 15.7.5 guest boots
to login screen with the SMC retry storm absent from the kernel
log; smoke test recipe in TESTING.md alongside the series.

Matthew Jackson (2):
  hw/misc/applesmc: fix GET_KEY_BY_INDEX to return real keys, accept
    WRITE/TYPE commands
  hw/misc/applesmc: populate Apple SMC key table

 hw/misc/applesmc.c | 344 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 337 insertions(+), 7 deletions(-)

-- 
2.50.1 (Apple Git-155)
[PATCH v2 0/2] hw/misc/applesmc: fix GET_KEY_BY_INDEX iteration and populate Apple SMC key set
Posted by Matthew Jackson 1 month ago
v1: https://lore.kernel.org/qemu-devel/20260507040153.14565-1-matthew@pq.io/

Changes in v2 (all in patch 2/2, applesmc_isa_realize() #KEY block):

  * Add braces around the QLIST_FOREACH() count loop body
    (qemu coding style: loops always need braces, even single-line).
    Reported-by: Peter Maydell <peter.maydell@linaro.org>

  * Replace the manual 4-byte big-endian byte-shift packing of
    `count` into `numkey_buf` with a single stl_be_p() call.
    Reported-by: Peter Maydell <peter.maydell@linaro.org>

Patch 1/2 is unchanged from v1.

Peter also noted that the static-buffer "must remain valid forever"
contract that applesmc_add_key() requires is awkward — fair, but
fixing it (e.g. switching the key table to a glib hashtable that
copies values) is independent from this series and not reported as
performance-noticeable in profiles. Happy to follow up with a
hashtable conversion as a separate series if the maintainers
think it's worth doing.

Original v1 cover letter follows below.

---

The QEMU applesmc device implements just enough of the Apple SMC PMIO
protocol to satisfy the OSK boot check on older macOS versions. On
modern macOS guests (x86 10.14+, all of the 15.x series) the real
AppleSMC kext enumerates the SMC key space at boot via
APPLESMC_GET_KEY_BY_INDEX_CMD (0x12). The current device only
acknowledges APPLESMC_READ_CMD (0x10) at the command port; every
other command falls through to the default arm of the switch and
sets ST_1E_BAD_CMD.

The macOS driver interprets the resulting 0x82 reply as "spurious
data" and enters a retry loop that floods the kernel log with
kSMCSpuriousData (0x81) / kSMCKeyNotFound errors at roughly 1800
events per second, pegging kernel_task at ~70% CPU and WindowServer
at ~509% CPU. This reproduces reliably on any recent macOS 15 guest
booted with -device isa-applesmc,osk=<valid-OSK>.

This two-patch series fixes the protocol-level bug and rounds out
the SMC key table to a complete iMac20,1 profile.

  Patch 1: protocol-level fix
    - Accept WRITE_CMD, GET_KEY_BY_INDEX_CMD, GET_KEY_TYPE_CMD at the
      command port (in addition to READ_CMD).
    - Implement the indexed-iteration walker (returns real key names
      from s->data_def, or APPLESMC_ST_1E_BAD_INDEX 0xb8 once the
      index is past the end so the guest stops iterating).
    - Implement GET_KEY_TYPE returning a 6-byte type/size/attr
      response matching VirtualSMC's kern_pmio.cpp behaviour.
    - Accept and log WRITE_CMD silently.
    - Replace the unknown-key NOEXIST (0x84) reply with a zeroed
      payload of the requested length, logged at LOG_UNIMP.
    - Route the BAD_CMD path through qemu_log_mask(LOG_GUEST_ERROR).
    - Fix MSSD initialiser typo ("\0x3" -> "\x03"). The original
      literal was three bytes ('\0', 'x', '3') truncated to one
      ('\0') by the size argument, so MSSD has been silently
      returning 0 since the device was introduced; the corrected
      value matches what a real iMac20,1 SMC reports.

  Patch 2: populate the key table
    - Add 94 keys covering the categories macOS queries on a Sequoia
      15.7.5 guest: 28 temperature sensors (sp78), 4 fan keys (fpe2),
      12 power-rail keys, 6 DIMM keys, 11 SMC-internal bookkeeping,
      13 motion-sensor / wireless, 3 write targets (HE0N/MSDW/NTOK),
      2 power-management gates (HE2N/WDTC), 8 platform-identity /
      probe keys, plus the Apple-canonical #KEY total-count.
    - Sensor values match a real iMac20,1 idle probe published at
      https://linux-hardware.org/?probe=999fc708a4&log=sensors:
      CPU 40-51 C, GPU 36-42 C, fan at 1200 RPM (= F0Mn idle), etc.

Measured impact (macOS 15.7.5 guest, iMac20,1 profile):

   Metric           | Before   | After
   -----------------|---------:|------:
   SMC errors / 5s  |   9,225  |     2
   kernel_task CPU  |    70 %  |  ~2 %
   WindowServer CPU |   509 %  |  ~6 %

A note for review on the zero-valued keys in patch 2: the 26 keys
covering DIMM / SMC bookkeeping / motion-sensor / wireless rails are
registered with present-with-zero values rather than omitted. macOS
distinguishes "absent" (NOEXIST reply, retry-poll) from "broken"
(present, value 0, accepted-and-ignored). Registering these keys
present-with-zero stops the retry-poll behaviour without asserting
any specific value. If the maintainer prefers a tighter scope for
this series I am happy to drop any subset and follow up; the
present-with-zero approach was driven by which keys macOS observed
querying during boot.

Backwards compatibility: legacy macOS guests (10.11-10.13) which do
not iterate the key space via GET_KEY_BY_INDEX boot unchanged. The
original six keys (REV/OSK0/OSK1/NATJ/MSSP/MSSD) are still present
and respond with the same values, modulo the MSSD typo fix in patch
1 which corrects MSSD to the value a real iMac20,1 SMC reports.

Tested against current master (post v11.0.0). Builds clean on
gcc-13 / clang-17 with --enable-werror. Sequoia 15.7.5 guest boots
to login screen with the SMC retry storm absent from the kernel
log; smoke test recipe in TESTING.md alongside the series.

Matthew Jackson (2):
  hw/misc/applesmc: fix GET_KEY_BY_INDEX to return real keys, accept
    WRITE/TYPE commands
  hw/misc/applesmc: populate Apple SMC key table

 hw/misc/applesmc.c | 346 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 338 insertions(+), 8 deletions(-)

-- 
2.50.1 (Apple Git-155)


[PATCH v3 0/3] hw/misc/applesmc: fix GET_KEY_BY_INDEX iteration and populate Apple SMC key set
Posted by Matthew Jackson 1 week, 3 days ago
v1: https://lore.kernel.org/qemu-devel/20260507040153.14565-1-matthew@pq.io/
v2: https://lore.kernel.org/qemu-devel/20260507152030.48753-1-matthew@pq.io/

This is a rebase/respin of v2, which had no outstanding review
comments after Peter's two style nits were addressed. Resending on
current master with checkpatch cleared, plus a new patch 3/3 that
gives the file a MAINTAINERS entry.

Changes in v3:

  * New patch 3/3: add a MAINTAINERS entry for hw/misc/applesmc.c.
    The file currently has no entry, so get_maintainer.pl resolves
    only the open list and these patches had no designated reviewer.
    This is easy to drop if a maintainer would rather route it
    elsewhere; patches 1-2 do not depend on it.

  * Rebased on current master. hw/misc/applesmc.c is byte-identical
    upstream to the v2 base (blob fd96f5f), so the rebase is a clean
    no-op with no functional change to patches 1-2 from v2.

  * checkpatch cleanups in patches 1-2, no functional change:
      - patch 1/2: reformat the GET_KEY_TYPE 4th-byte block comment to
        the leading-/* and trailing-*/-on-their-own-line house style.
      - patch 2/2: shorten the F0Ac fan-key comment to stay under the
        80-column limit.
    Both were pre-existing checkpatch warnings; neither was raised in
    the v1/v2 review. The series is now checkpatch-clean.

Changes in v2 (carried into v3, all in patch 2/2, #KEY block):

  * Add braces around the QLIST_FOREACH() count loop body
    (qemu coding style: loops always need braces, even single-line).
    Reported-by: Peter Maydell <peter.maydell@linaro.org>

  * Replace the manual 4-byte big-endian byte-shift packing of
    `count` into `numkey_buf` with a single stl_be_p() call.
    Reported-by: Peter Maydell <peter.maydell@linaro.org>

Patch 1/2 is otherwise unchanged since v1.

On Peter's v2 remark that applesmc_add_key()'s "pointer must remain
valid forever" contract is awkward: agreed it's not ideal, but the
fix (e.g. converting the key table to a glib hashtable that copies
values) is orthogonal to this protocol bug fix and isn't reported as
noticeable in profiles. Happy to send that conversion as a separate
follow-up series if the maintainers think it's worth doing.

Original v1 cover letter follows below.

---

The QEMU applesmc device implements just enough of the Apple SMC PMIO
protocol to satisfy the OSK boot check on older macOS versions. On
modern macOS guests (x86 10.14+, all of the 15.x series) the real
AppleSMC kext enumerates the SMC key space at boot via
APPLESMC_GET_KEY_BY_INDEX_CMD (0x12). The current device only
acknowledges APPLESMC_READ_CMD (0x10) at the command port; every
other command falls through to the default arm of the switch and
sets ST_1E_BAD_CMD.

The macOS driver interprets the resulting 0x82 reply as "spurious
data" and enters a retry loop that floods the kernel log with
kSMCSpuriousData (0x81) / kSMCKeyNotFound errors at roughly 1800
events per second, pegging kernel_task at ~70% CPU and WindowServer
at ~509% CPU. This reproduces reliably on any recent macOS 15 guest
booted with -device isa-applesmc,osk=<valid-OSK>.

This series fixes the protocol-level bug and rounds out the SMC key
table to a complete iMac20,1 profile.

  Patch 1: protocol-level fix
    - Accept WRITE_CMD, GET_KEY_BY_INDEX_CMD, GET_KEY_TYPE_CMD at the
      command port (in addition to READ_CMD).
    - Implement the indexed-iteration walker (returns real key names
      from s->data_def, or APPLESMC_ST_1E_BAD_INDEX 0xb8 once the
      index is past the end so the guest stops iterating).
    - Implement GET_KEY_TYPE returning a 6-byte type/size/attr
      response matching VirtualSMC's kern_pmio.cpp behaviour.
    - Accept and log WRITE_CMD silently.
    - Replace the unknown-key NOEXIST (0x84) reply with a zeroed
      payload of the requested length, logged at LOG_UNIMP.
    - Route the BAD_CMD path through qemu_log_mask(LOG_GUEST_ERROR).
    - Fix MSSD initialiser typo ("\0x3" -> "\x03"). The original
      literal was three bytes ('\0', 'x', '3') truncated to one
      ('\0') by the size argument, so MSSD has been silently
      returning 0 since the device was introduced; the corrected
      value matches what a real iMac20,1 SMC reports.

  Patch 2: populate the key table
    - Add 94 keys covering the categories macOS queries on a Sequoia
      15.7.5 guest: 28 temperature sensors (sp78), 4 fan keys (fpe2),
      12 power-rail keys, 6 DIMM keys, 11 SMC-internal bookkeeping,
      13 motion-sensor / wireless, 3 write targets (HE0N/MSDW/NTOK),
      2 power-management gates (HE2N/WDTC), 8 platform-identity /
      probe keys, plus the Apple-canonical #KEY total-count.
    - Sensor values match a real iMac20,1 idle probe published at
      https://linux-hardware.org/?probe=999fc708a4&log=sensors:
      CPU 40-51 C, GPU 36-42 C, fan at 1200 RPM (= F0Mn idle), etc.

  Patch 3: MAINTAINERS entry for the device (see "Changes in v3").

Measured impact (macOS 15.7.5 guest, iMac20,1 profile):

   Metric           | Before   | After
   -----------------|---------:|------:
   SMC errors / 5s  |   9,225  |     2
   kernel_task CPU  |    70 %  |  ~2 %
   WindowServer CPU |   509 %  |  ~6 %

A note for review on the zero-valued keys in patch 2: the 26 keys
covering DIMM / SMC bookkeeping / motion-sensor / wireless rails are
registered with present-with-zero values rather than omitted. macOS
distinguishes "absent" (NOEXIST reply, retry-poll) from "broken"
(present, value 0, accepted-and-ignored). Registering these keys
present-with-zero stops the retry-poll behaviour without asserting
any specific value. If the maintainer prefers a tighter scope for
this series I am happy to drop any subset and follow up; the
present-with-zero approach was driven by which keys macOS observed
querying during boot.

Backwards compatibility: legacy macOS guests (10.11-10.13) which do
not iterate the key space via GET_KEY_BY_INDEX boot unchanged. The
original six keys (REV/OSK0/OSK1/NATJ/MSSP/MSSD) are still present
and respond with the same values, modulo the MSSD typo fix in patch
1 which corrects MSSD to the value a real iMac20,1 SMC reports.

Tested against current master. Builds clean on gcc-13 / clang-17
with --enable-werror. Sequoia 15.7.5 guest boots to login screen
with the SMC retry storm absent from the kernel log.

Matthew Jackson (3):
  hw/misc/applesmc: fix GET_KEY_BY_INDEX to return real keys, accept
    WRITE/TYPE commands
  hw/misc/applesmc: populate Apple SMC key table
  MAINTAINERS: adopt hw/misc/applesmc.c

 MAINTAINERS        |   5 +
 hw/misc/applesmc.c | 348 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 345 insertions(+), 8 deletions(-)

--
2.50.1 (Apple Git-155)
[PATCH v3 1/3] hw/misc/applesmc: fix GET_KEY_BY_INDEX to return real keys, accept WRITE/TYPE commands
Posted by Matthew Jackson 1 week, 3 days ago
The applesmc device implements just enough of the Apple SMC PMIO
protocol to satisfy the OSK boot check on older macOS versions.
On modern macOS guests (x86 10.14+, all 15.x), the AppleSMC kext
enumerates the SMC key space at boot via APPLESMC_GET_KEY_BY_INDEX_CMD
(0x12). The current device only acknowledges APPLESMC_READ_CMD
(0x10) at the command port; every other command falls through to
the default arm of the switch and sets ST_1E_BAD_CMD.

The macOS driver interprets the resulting 0x82 reply as
"spurious data" and enters a retry loop that floods the kernel
log with kSMCSpuriousData (0x81) / kSMCKeyNotFound errors at
roughly 1800 events per second, pegging kernel_task at ~70%
CPU and WindowServer at ~509% CPU. This reproduces reliably
on any recent macOS 15 guest booted with -device
isa-applesmc,osk=<valid-OSK>.

This patch:

  * Accepts APPLESMC_WRITE_CMD, APPLESMC_GET_KEY_BY_INDEX_CMD,
    and APPLESMC_GET_KEY_TYPE_CMD at the command port (in
    addition to the existing READ_CMD path).

  * Implements GET_KEY_BY_INDEX by walking s->data_def and
    returning the 4-byte ASCII key name at the requested index;
    returns APPLESMC_ST_1E_BAD_INDEX (0xb8) once the index is
    past the end of the list so the guest stops iterating.

  * Implements GET_KEY_TYPE by looking up the key in s->data_def
    and returning a 6-byte response (type[4] + size[1] + attr[1])
    matching VirtualSMC's kern_pmio.cpp behaviour.

  * Implements WRITE by accepting the key name, length, and
    payload and logging at LOG_UNIMP. macOS writes SMC keys
    during normal power management; silent acceptance avoids
    BAD_CMD on every write.

  * Replaces the unknown-key NOEXIST (0x84) reply on READ with
    a zeroed payload of the requested length, logged at
    LOG_UNIMP. Early-boot probes hit hundreds of undocumented
    keys per second; NOEXIST triggers retry storms while a
    zeroed payload satisfies the probe semantics without
    asserting a particular value.

  * Routes the BAD_CMD path through qemu_log_mask
    (LOG_GUEST_ERROR) instead of the smc_debug printf.

  * Fixes a typo in the MSSD key initialiser ("\0x3" -> "\x03").
    The original literal was three bytes ('\\0', 'x', '3')
    truncated to one ('\\0') by the size argument, so MSSD has
    been silently returning 0 since introduction; the corrected
    value matches what a real iMac20,1 SMC reports.

Reported-by: macOS guests booted with -device isa-applesmc since 10.14.
Signed-off-by: Matthew Jackson <matthew@pq.io>
---
 hw/misc/applesmc.c | 179 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 172 insertions(+), 7 deletions(-)

diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c
index fd96f5f245..263b2251b0 100644
--- a/hw/misc/applesmc.c
+++ b/hw/misc/applesmc.c
@@ -35,6 +35,7 @@
 #include "hw/core/qdev-properties.h"
 #include "ui/console.h"
 #include "qemu/error-report.h"
+#include "qemu/log.h"
 #include "qemu/module.h"
 #include "qemu/timer.h"
 #include "qom/object.h"
@@ -126,7 +127,14 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val,
     smc_debug("CMD received: 0x%02x\n", (uint8_t)val);
     switch (val) {
     case APPLESMC_READ_CMD:
-        /* did last command run through OK? */
+    case APPLESMC_WRITE_CMD:
+    case APPLESMC_GET_KEY_BY_INDEX_CMD:
+    case APPLESMC_GET_KEY_TYPE_CMD:
+        /*
+         * Accept all standard SMC commands. Pre-existing code only handled
+         * READ_CMD; macOS boots hang if WRITE/TYPE/GET_KEY_BY_INDEX commands
+         * return BAD_CMD during early AppleSMC driver init.
+         */
         if (status == APPLESMC_ST_CMD_DONE || status == APPLESMC_ST_NEW_CMD) {
             s->cmd = val;
             s->status = APPLESMC_ST_NEW_CMD | APPLESMC_ST_ACK;
@@ -137,7 +145,8 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val,
         }
         break;
     default:
-        smc_debug("UNEXPECTED CMD 0x%02x\n", (uint8_t)val);
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "applesmc: unexpected CMD 0x%02x\n", (uint8_t)val);
         s->status = APPLESMC_ST_NEW_CMD;
         s->status_1e = APPLESMC_ST_1E_BAD_CMD;
     }
@@ -179,17 +188,172 @@ static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val,
                 s->data_len = d->len;
                 s->data_pos = 0;
                 s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY;
-                s->status_1e = APPLESMC_ST_CMD_DONE;  /* clear on valid key */
+                s->status_1e = APPLESMC_ST_CMD_DONE;
             } else {
-                smc_debug("READ_CMD: key '%c%c%c%c' not found!\n",
-                          s->key[0], s->key[1], s->key[2], s->key[3]);
+                /*
+                 * Return zeros for unknown keys instead of NOEXIST. Early
+                 * macOS boot probes many undocumented keys; responding
+                 * NOEXIST triggers retry storms. A zeroed payload satisfies
+                 * the probe without asserting a particular value.
+                 */
+                qemu_log_mask(LOG_UNIMP,
+                              "applesmc: READ unknown key '%c%c%c%c' len=%d\n",
+                              s->key[0], s->key[1], s->key[2], s->key[3],
+                              (uint8_t)val);
+                memset(s->data, 0, APPLESMC_MAX_DATA_LENGTH);
+                s->data_len = (uint8_t)val;
+                s->data_pos = 0;
+                s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY;
+                s->status_1e = APPLESMC_ST_CMD_DONE;
+            }
+        }
+        s->read_pos++;
+        break;
+    case APPLESMC_WRITE_CMD:
+        /*
+         * Accept writes silently. macOS writes SMC keys during power
+         * management, fan control, etc. Log at LOG_UNIMP for visibility
+         * without treating the write as an error.
+         */
+        if ((s->status & 0x0f) == APPLESMC_ST_CMD_DONE) {
+            break;
+        }
+        if (s->read_pos < 4) {
+            s->key[s->read_pos] = val;
+            s->status = APPLESMC_ST_ACK;
+        } else if (s->read_pos == 4) {
+            s->data_len = (uint8_t)val;
+            s->data_pos = 0;
+            s->status = APPLESMC_ST_ACK;
+        } else {
+            if (s->data_pos < s->data_len) {
+                s->data[s->data_pos] = (uint8_t)val;
+                s->data_pos++;
+                if (s->data_pos == s->data_len) {
+                    qemu_log_mask(LOG_UNIMP,
+                                  "applesmc: WRITE key '%c%c%c%c' len=%d\n",
+                                  s->key[0], s->key[1], s->key[2], s->key[3],
+                                  s->data_len);
+                    s->status = APPLESMC_ST_CMD_DONE;
+                    s->status_1e = APPLESMC_ST_CMD_DONE;
+                } else {
+                    s->status = APPLESMC_ST_ACK;
+                }
+            }
+        }
+        s->read_pos++;
+        break;
+    case APPLESMC_GET_KEY_TYPE_CMD:
+        /*
+         * Return key type info. Protocol (matches VirtualSMC):
+         * - Receive 4 bytes of key name.
+         * - After the 4th byte, immediately set DATA_READY with response.
+         * - Response is 6 bytes: type[4] + size[1] + attr[1].
+         * Unlike READ_CMD there is no length byte between key name and
+         * response.
+         */
+        if ((s->status & 0x0f) == APPLESMC_ST_CMD_DONE) {
+            break;
+        }
+        if (s->read_pos < 3) {
+            s->key[s->read_pos] = val;
+            s->status = APPLESMC_ST_ACK;
+        } else if (s->read_pos == 3) {
+            /*
+             * 4th and final key byte. Unlike READ_CMD which has a 5th byte
+             * for data length, GET_KEY_TYPE responds immediately after the
+             * 4-byte key name (matching VirtualSMC kern_pmio.cpp behavior).
+             */
+            s->key[3] = val;
+            d = applesmc_find_key(s);
+            if (d != NULL) {
+                switch (d->len) {
+                case 1:
+                    s->data[0] = 'u'; s->data[1] = 'i';
+                    s->data[2] = '8'; s->data[3] = ' ';
+                    break;
+                case 2:
+                    s->data[0] = 'u'; s->data[1] = 'i';
+                    s->data[2] = '1'; s->data[3] = '6';
+                    break;
+                case 4:
+                    s->data[0] = 'u'; s->data[1] = 'i';
+                    s->data[2] = '3'; s->data[3] = '2';
+                    break;
+                default:
+                    s->data[0] = 'c'; s->data[1] = 'h';
+                    s->data[2] = '8'; s->data[3] = '*';
+                    break;
+                }
+                s->data[4] = d->len;
+                s->data[5] = 0xD0;
+            } else {
+                qemu_log_mask(LOG_UNIMP,
+                              "applesmc: GET_KEY_TYPE unknown '%c%c%c%c'\n",
+                              s->key[0], s->key[1], s->key[2], s->key[3]);
+                s->data[0] = 'u'; s->data[1] = 'i';
+                s->data[2] = '8'; s->data[3] = ' ';
+                s->data[4] = 1;
+                s->data[5] = 0xD0;
+            }
+            s->data_len = 6;
+            s->data_pos = 0;
+            s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY;
+            s->status_1e = APPLESMC_ST_CMD_DONE;
+        }
+        s->read_pos++;
+        break;
+    case APPLESMC_GET_KEY_BY_INDEX_CMD:
+        /*
+         * Return key name by index. macOS sends a 4-byte big-endian index
+         * and expects the 4-byte ASCII key name at that position. The
+         * previous implementation returned 4 zero bytes, which macOS
+         * treated as kSMCSpuriousData (0x81) and retried indefinitely,
+         * flooding the kernel log at ~1800 errors/sec. Walk the keys list
+         * to return the actual key name, or APPLESMC_ST_1E_BAD_INDEX
+         * (0xb8) once the index is past the end of the list so the guest
+         * stops iterating.
+         */
+        if ((s->status & 0x0f) == APPLESMC_ST_CMD_DONE) {
+            break;
+        }
+        if (s->read_pos < 3) {
+            s->key[s->read_pos] = val;
+            s->status = APPLESMC_ST_ACK;
+        } else if (s->read_pos == 3) {
+            s->key[3] = val;
+            uint32_t idx = ((uint8_t)s->key[0] << 24)
+                         | ((uint8_t)s->key[1] << 16)
+                         | ((uint8_t)s->key[2] << 8)
+                         |  (uint8_t)s->key[3];
+            struct AppleSMCData *def;
+            uint32_t i = 0;
+            bool found = false;
+            QLIST_FOREACH(def, &s->data_def, node) {
+                if (i == idx) {
+                    memcpy(s->data, def->key, 4);
+                    s->data_len = 4;
+                    s->data_pos = 0;
+                    found = true;
+                    break;
+                }
+                i++;
+            }
+            if (!found) {
+                s->data_len = 0;
+                s->status_1e = APPLESMC_ST_1E_BAD_INDEX;
                 s->status = APPLESMC_ST_CMD_DONE;
-                s->status_1e = APPLESMC_ST_1E_NOEXIST;
+                s->read_pos++;
+                break;
             }
+            s->status = APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY;
+            s->status_1e = APPLESMC_ST_CMD_DONE;
         }
         s->read_pos++;
         break;
     default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "applesmc: unhandled data for cmd 0x%02x\n", s->cmd);
         s->status = APPLESMC_ST_CMD_DONE;
         s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD;
     }
@@ -330,12 +494,13 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp)
     }
 
     QLIST_INIT(&s->data_def);
+
     applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03");
     applesmc_add_key(s, "OSK0", 32, s->osk);
     applesmc_add_key(s, "OSK1", 32, s->osk + 32);
     applesmc_add_key(s, "NATJ", 1, "\0");
     applesmc_add_key(s, "MSSP", 1, "\0");
-    applesmc_add_key(s, "MSSD", 1, "\0x3");
+    applesmc_add_key(s, "MSSD", 1, "\x03");
 }
 
 static void applesmc_unrealize(DeviceState *dev)
-- 
2.50.1 (Apple Git-155)
[PATCH v3 2/3] hw/misc/applesmc: populate Apple SMC key table
Posted by Matthew Jackson 1 week, 3 days ago
With APPLESMC_GET_KEY_BY_INDEX_CMD now functional (previous
patch), the modern macOS AppleSMC kext walks the device's key
table at boot to discover what's available. The current key
set (REV/OSK0/OSK1/NATJ/MSSP/MSSD) is sparse enough that
several macOS subsystems retry-poll keys they expect to find,
contributing to the same kSMCSpuriousData traffic the previous
patch addresses.

This patch fills the key table out to a complete iMac20,1
profile (94 additional keys plus the canonical #KEY count).
Sensor values match a real iMac20,1 idle probe published at:

  https://linux-hardware.org/?probe=999fc708a4&log=sensors

Categories:

  * 28 temperature sensors in sp78 format with idle readings
    from the probe: CPU 40 C (TC0P) / 45 C (TC0F) / 51 C
    (TCXc), GPU 36 C (TG0P) / 42 C (TG0F/TG1F/TGDD), HDD
    41 C (TH0P/TH1C/TH1F), LCD 28-29 C (TL0V/TL1V), memory
    34-36 C (TM0V/TM0P), ambient 24 C (TA0V), PSU 33 C
    (Tp00/Tp2F), generic sensors 33 C (Ts*S, TS0V), VRM
    50 C (TVMD/TVmS/TVSL/TVSR; not in the probe, conservative
    estimate). Battery sensors (TB0T/TB1T/TB2T) are
    present-with-zero because iMacs have no battery; macOS's
    "absent vs broken" distinction relies on the key being
    registered even with a zero reading.

  * 4 fan keys (FNum/F0Ac/F0Mn/F0Mx) describing iMac20,1's
    single chassis fan: 1 fan, current 1200 RPM (= F0Mn,
    matching the probe-reported idle), min 1200, max 3600.
    fpe2 encoding (raw = RPM * 4, big-endian).

  * 12 power-rail keys (PC0R/PCPC/PCPG/PCPT/PfCP/PfCT/PfGT/
    PfHT/PfM0/PfST/PSTR/PHDC) for telemetry; present-with-
    zero satisfies macOS without claiming a specific value.

  * 6 DIMM keys (DM0P/DM0S/DM1P/DM1S/MD1R/MD1W); same
    rationale.

  * 11 SMC-internal bookkeeping keys (CLKH/DICT/RPlt/SBFL/
    VRTC/WKTP plus 5 lower-case Apple-private bookkeeping
    keys cePn/cmDU/maNN/mxT1/zEPD); same rationale.

  * 13 motion-sensor / wireless keys (MSAc/MSAf/MSAg/MSAi/
    MSGA/MSHP/MSPA/MTLV/QCLV/QENA/WIr0/WIw0/WIz0). Present-
    with-zero is the correct desktop-class answer.

  * 3 write targets that must also be readable: HE0N (iGPU
    power; AGPM driver writes 1 on start), MSDW, NTOK.

  * 2 power-management gates required by AGPM and the
    watchdog probe: HE2N (dGPU power enable), WDTC.

  * 8 platform-identity / probe keys (MPRO/MPRD/LGPB/BSLN/
    EPCI/BEMB/$Adr/RGEN/DPLM/MSSW/OSWD): VirtualSMC-style
    identity plus the early boot-time SMC probe keys.

  * 2 GPU temperature sensors (TGDD/TG0P) and #KEY (the
    Apple-canonical total-keys count, computed by walking
    the data_def list at realize time).

  * 1 VirtualSMC-compatible $Num key (some drivers prefer it
    over #KEY).

After this patch, with the protocol fix from the previous
patch in place, a Sequoia 15.7.5 guest boots without the SMC
retry storm:

   Metric           | Before   | After
   -----------------|---------:|------:
   SMC errors / 5s  |   9,225  |     2
   kernel_task CPU  |    70 %  |  ~2 %
   WindowServer CPU |   509 %  |  ~6 %

Legacy macOS guests (10.11-10.13) that do not iterate the
key space see no behavioural change beyond the corrected
MSSD value (now 0x03 as on real iMac20,1 hardware) and the
fact that READ on a previously-unknown key returns zeros
instead of NOEXIST.

Signed-off-by: Matthew Jackson <matthew@pq.io>
---
 hw/misc/applesmc.c | 169 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 168 insertions(+), 1 deletion(-)

diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c
index 263b2251b0..38062a07b5 100644
--- a/hw/misc/applesmc.c
+++ b/hw/misc/applesmc.c
@@ -32,7 +32,7 @@
 
 #include "qemu/osdep.h"
 #include "hw/isa/isa.h"
-#include "hw/core/qdev-properties.h"
+#include "hw/qdev-properties.h"
 #include "ui/console.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
@@ -495,12 +495,179 @@ static void applesmc_isa_realize(DeviceState *dev, Error **errp)
 
     QLIST_INIT(&s->data_def);
 
+    /* System identification */
     applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03");
     applesmc_add_key(s, "OSK0", 32, s->osk);
     applesmc_add_key(s, "OSK1", 32, s->osk + 32);
     applesmc_add_key(s, "NATJ", 1, "\0");
     applesmc_add_key(s, "MSSP", 1, "\0");
     applesmc_add_key(s, "MSSD", 1, "\x03");
+
+    /*
+     * GPU power-management keys. Without HE2N the AGPM driver reads
+     * NOEXIST, errors out (0x82) and hangs the system on dynamic
+     * wallpaper changes. The driver itself sets the value to 1 on
+     * start, so the boot default of 0 is correct.
+     */
+    applesmc_add_key(s, "HE2N", 1, "\x00");  /* dGPU power enable */
+
+    /*
+     * Watchdog timer control. Queried by macOS during early boot; an
+     * unanswered query produces a cluster of SMCWDT errors per boot.
+     */
+    applesmc_add_key(s, "WDTC", 1, "\x00");
+
+    /*
+     * GPU temperature sensors. SP78 format: 16-bit big-endian with the
+     * top 8 bits encoding integer degrees C and the bottom 8 bits the
+     * fraction. Returning zeroed sensors caused macOS to flag these as
+     * broken and retry-poll. Values approximate a warm idle iMac20,1.
+     */
+    applesmc_add_key(s, "TGDD", 2, "\x2A\x00");  /* GPU diode: 42 C */
+    applesmc_add_key(s, "TG0P", 2, "\x24\x00");  /* GPU proximity: 36 C */
+
+    /*
+     * Fan control (iMac20,1 has one chassis fan). fpe2 format: raw
+     * value is RPM << 2, big-endian. Idle ~1500 RPM, range 1200-3600.
+     */
+    applesmc_add_key(s, "FNum", 1, "\x01");
+    applesmc_add_key(s, "F0Ac", 2, "\x12\xC0");  /* 1200 RPM (idle = F0Mn) */
+    applesmc_add_key(s, "F0Mn", 2, "\x12\xC0");  /* min:     1200 RPM */
+    applesmc_add_key(s, "F0Mx", 2, "\x38\x40");  /* max:     3600 RPM */
+
+    /* Platform-identity keys also provided by VirtualSMC. */
+    applesmc_add_key(s, "MPRO", 1, "\x01");  /* model property */
+    applesmc_add_key(s, "MPRD", 1, "\x00");  /* model product */
+    applesmc_add_key(s, "LGPB", 1, "\x00");  /* lid/GPU power */
+
+    /* CPU/power keys consumed by X86PlatformPlugin. */
+    applesmc_add_key(s, "BSLN", 1, "\x00");  /* baseline */
+    applesmc_add_key(s, "EPCI", 4, "\x00\x00\x00\x00"); /* EPC info */
+    applesmc_add_key(s, "BEMB", 1, "\x01");  /* board embedded */
+
+    /* Keys read by the boot-time SMC probe. */
+    applesmc_add_key(s, "OSWD", 2, "\x00\x00");  /* OS watchdog timer */
+    applesmc_add_key(s, "MSSW", 1, "\x00");  /* macOS software state */
+    applesmc_add_key(s, "RGEN", 1, "\x02");  /* SMC generation */
+    applesmc_add_key(s, "DPLM", 4, "\x00\x00\x00\x00"); /* display power */
+    applesmc_add_key(s, "$Adr", 4, "\x00\x00\x03\x00"); /* SMC base addr */
+
+    /*
+     * Temperature sensors covering the rails the macOS AppleSMC client
+     * polls on an iMac20,1-class host. Values are realistic idle
+     * readings in SP78 (big-endian) format; zeros trigger retry storms
+     * on broken-sensor paths.
+     */
+    applesmc_add_key(s, "TC0F", 2, "\x2D\x00");  /* CPU PECI filt: 45 C */
+    applesmc_add_key(s, "TC0P", 2, "\x28\x00");  /* CPU proximity: 40 C */
+    applesmc_add_key(s, "TCXc", 2, "\x33\x00");  /* CPU core max: 51 C */
+    applesmc_add_key(s, "TG0F", 2, "\x2A\x00");  /* GPU 0 filt: 42 C */
+    applesmc_add_key(s, "TG1F", 2, "\x2A\x00");  /* GPU 1 filt: 42 C */
+    applesmc_add_key(s, "TH0P", 2, "\x29\x00");  /* HDD proximity: 41 C */
+    applesmc_add_key(s, "TH1A", 2, "\x1A\x00");  /* HDD 1 ambient: 26 C */
+    applesmc_add_key(s, "TH1C", 2, "\x29\x00");  /* HDD 1 core: 41 C */
+    applesmc_add_key(s, "TH1F", 2, "\x29\x00");  /* HDD 1 filt: 41 C */
+    applesmc_add_key(s, "TL0V", 2, "\x1D\x00");  /* LCD 0: 29 C */
+    applesmc_add_key(s, "TL1V", 2, "\x1C\x00");  /* LCD 1: 28 C */
+    applesmc_add_key(s, "TM0P", 2, "\x24\x00");  /* memory prox: 36 C */
+    applesmc_add_key(s, "TM0V", 2, "\x22\x00");  /* memory VRM: 34 C */
+    applesmc_add_key(s, "Tp00", 2, "\x21\x00");  /* PSU: 33 C */
+    applesmc_add_key(s, "Tp2F", 2, "\x21\x00");  /* PSU 2: 33 C */
+    applesmc_add_key(s, "Ts0S", 2, "\x21\x00");  /* sensor 0: 33 C */
+    applesmc_add_key(s, "TS0V", 2, "\x21\x00");  /* sensor 0 V: 33 C */
+    applesmc_add_key(s, "Ts1S", 2, "\x21\x00");  /* sensor 1: 33 C */
+    applesmc_add_key(s, "Ts2S", 2, "\x21\x00");  /* sensor 2: 33 C */
+    applesmc_add_key(s, "TB0T", 2, "\x00\x00");  /* no battery */
+    applesmc_add_key(s, "TB1T", 2, "\x00\x00");  /* no battery */
+    applesmc_add_key(s, "TB2T", 2, "\x00\x00");  /* no battery */
+    applesmc_add_key(s, "TA0V", 2, "\x18\x00");  /* ambient 0: 24 C */
+    applesmc_add_key(s, "TVMD", 2, "\x32\x00");  /* VRM diode: 50 C */
+    applesmc_add_key(s, "TVmS", 2, "\x32\x00");  /* VRM sense: 50 C */
+    applesmc_add_key(s, "TVSL", 2, "\x32\x00");  /* VRM sense L: 50 C */
+    applesmc_add_key(s, "TVSR", 2, "\x32\x00");  /* VRM sense R: 50 C */
+
+    /* Power and platform rails (12 keys). */
+    applesmc_add_key(s, "PC0R", 2, "\x00\x00");  /* CPU 0 rail */
+    applesmc_add_key(s, "PCPC", 2, "\x00\x00");  /* CPU package core */
+    applesmc_add_key(s, "PCPG", 2, "\x00\x00");  /* CPU package GPU */
+    applesmc_add_key(s, "PCPT", 2, "\x00\x00");  /* CPU package total */
+    applesmc_add_key(s, "PfCP", 2, "\x00\x00");  /* platform CPU power */
+    applesmc_add_key(s, "PfCT", 2, "\x00\x00");  /* platform CPU temp */
+    applesmc_add_key(s, "PfGT", 2, "\x00\x00");  /* platform GPU temp */
+    applesmc_add_key(s, "PfHT", 2, "\x00\x00");  /* platform HDD temp */
+    applesmc_add_key(s, "PfM0", 2, "\x00\x00");  /* platform memory 0 */
+    applesmc_add_key(s, "PfST", 2, "\x00\x00");  /* platform system temp */
+    applesmc_add_key(s, "PSTR", 2, "\x00\x00");  /* power supply temp rail */
+    applesmc_add_key(s, "PHDC", 2, "\x00\x00");  /* platform HDC */
+
+    /* Memory / DIMM counters (6 keys). */
+    applesmc_add_key(s, "DM0P", 2, "\x00\x00");  /* DIMM 0 proximity */
+    applesmc_add_key(s, "DM0S", 2, "\x00\x00");  /* DIMM 0 sensor */
+    applesmc_add_key(s, "DM1P", 2, "\x00\x00");  /* DIMM 1 proximity */
+    applesmc_add_key(s, "DM1S", 2, "\x00\x00");  /* DIMM 1 sensor */
+    applesmc_add_key(s, "MD1R", 2, "\x00\x00");  /* memory DIMM 1 read */
+    applesmc_add_key(s, "MD1W", 2, "\x00\x00");  /* memory DIMM 1 write */
+
+    /* SMC internal bookkeeping (11 keys). */
+    applesmc_add_key(s, "CLKH", 1, "\x00");  /* clock halt */
+    applesmc_add_key(s, "DICT", 1, "\x00");  /* dictionary */
+    applesmc_add_key(s, "RPlt", 2, "\x00\x00");  /* platform revision */
+    applesmc_add_key(s, "SBFL", 1, "\x00");  /* secure boot flags */
+    applesmc_add_key(s, "VRTC", 2, "\x00\x00");  /* virtual RTC */
+    applesmc_add_key(s, "WKTP", 2, "\x00\x00");  /* wake type */
+    applesmc_add_key(s, "zEPD", 1, "\x00");  /* endpoint descriptor */
+    applesmc_add_key(s, "cePn", 1, "\x00");  /* CE panel */
+    applesmc_add_key(s, "cmDU", 1, "\x00");  /* cm display unit */
+    applesmc_add_key(s, "maNN", 1, "\x00");  /* manufacturer NN */
+    applesmc_add_key(s, "mxT1", 2, "\x00\x00");  /* max temp 1 */
+
+    /* Miscellaneous sensor/motion keys (13 keys). */
+    applesmc_add_key(s, "MSAc", 1, "\x00");  /* motion sensor active */
+    applesmc_add_key(s, "MSAf", 1, "\x00");  /* motion sensor filter */
+    applesmc_add_key(s, "MSAg", 1, "\x00");  /* motion sensor gain */
+    applesmc_add_key(s, "MSAi", 1, "\x00");  /* motion sensor interval */
+    applesmc_add_key(s, "MSGA", 1, "\x00");  /* MSG A */
+    applesmc_add_key(s, "MSHP", 1, "\x00");  /* MS HP */
+    applesmc_add_key(s, "MSPA", 1, "\x00");  /* MS PA */
+    applesmc_add_key(s, "MTLV", 1, "\x00");  /* MT level */
+    applesmc_add_key(s, "QCLV", 1, "\x00");  /* Q clevel */
+    applesmc_add_key(s, "QENA", 1, "\x00");  /* Q enable */
+    applesmc_add_key(s, "WIr0", 2, "\x00\x00");  /* WiFi rate 0 */
+    applesmc_add_key(s, "WIw0", 2, "\x00\x00");  /* WiFi write 0 */
+    applesmc_add_key(s, "WIz0", 2, "\x00\x00");  /* WiFi zone 0 */
+
+    /* Write targets that must also be readable. */
+    applesmc_add_key(s, "HE0N", 1, "\x00");  /* iGPU power; driver sets 1 */
+    applesmc_add_key(s, "MSDW", 1, "\x00");  /* MSD write */
+    applesmc_add_key(s, "NTOK", 1, "\x00");  /* notification token */
+
+    /*
+     * VirtualSMC-compatible total-keys key. Kept for compatibility with
+     * drivers that look for $Num; the canonical Apple #KEY below is what
+     * macOS actually iterates against.
+     */
+    applesmc_add_key(s, "$Num", 4, "\x00\x00\x00\x5e");
+
+    /*
+     * #KEY - Apple SMC convention for "total key count". macOS reads it
+     * at boot and iterates 0..count-1 via APPLESMC_GET_KEY_BY_INDEX_CMD.
+     * Without this key, macOS iterates unbounded and every out-of-range
+     * request floods the kernel log with kSMCSpuriousData errors
+     * (measured ~1800/sec on macOS 15). Count is computed by walking the
+     * list. The buffer is function-static so the pointer stays valid
+     * for the device's lifetime.
+     */
+    {
+        int count = 1;  /* include #KEY itself */
+        struct AppleSMCData *def;
+        static char numkey_buf[4];
+
+        QLIST_FOREACH(def, &s->data_def, node) {
+            count++;
+        }
+        stl_be_p(numkey_buf, count);
+        applesmc_add_key(s, "#KEY", 4, numkey_buf);
+    }
 }
 
 static void applesmc_unrealize(DeviceState *dev)
-- 
2.50.1 (Apple Git-155)
[PATCH v3 3/3] MAINTAINERS: adopt hw/misc/applesmc.c
Posted by Matthew Jackson 1 week, 3 days ago
hw/misc/applesmc.c has no MAINTAINERS entry, so patches to it are
not routed to anyone and get_maintainer.pl only resolves the open
list. Add an "Apple SMC" entry (Odd Fixes) so future changes have a
designated recipient.

Signed-off-by: Matthew Jackson <matthew@pq.io>
---
 MAINTAINERS | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 7752917d8c..4155e1d93d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2106,6 +2106,11 @@ F: hw/net/can/xlnx-*
 F: include/hw/net/xlnx-*
 F: tests/qtest/xlnx-can*-test*
 
+Apple SMC
+M: Matthew Jackson <matthew@pq.io>
+S: Odd Fixes
+F: hw/misc/applesmc.c
+
 EDU
 M: Jiri Slaby <jslaby@suse.cz>
 S: Maintained
-- 
2.50.1 (Apple Git-155)