From nobody Wed Aug 19 23:03:19 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787143468682831.2945769650375; Wed, 19 Aug 2026 05:44:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wwfdn-00080o-CI; Wed, 19 Aug 2026 08:43:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzY-0005Py-3d for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:40 -0400 Received: from pidgin.makrotopia.org ([2a07:2ec0:3002::65]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzW-0000Kv-Gd for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:39 -0400 Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wwZzM-000000002Qo-0Gve; Wed, 19 Aug 2026 06:41:28 +0000 Date: Wed, 19 Aug 2026 07:41:25 +0100 From: Daniel Golle To: Daniel Golle , Paolo Bonzini , Alexander Graf , Pierrick Bouvier , Philippe =?iso-8859-1?Q?Mathieu-Daud=E9?= , =?iso-8859-1?Q?C=E9dric?= Le Goater , Peter Maydell , Kane-Chen-AS , jack wang <163wangjack@gmail.com>, qemu-devel@nongnu.org Subject: [PATCH 1/4] hw/misc/applesmc: Add trace events Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a07:2ec0:3002::65; envelope-from=daniel@makrotopia.org; helo=pidgin.makrotopia.org X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 19 Aug 2026 08:43:31 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-Zoho-Virus-Status: 1 X-Zoho-AV-Stamp: zmail-av-0.2.10.1.5.2/287.128.6 X-ZM-MESSAGEID: 1787143473271158500 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The device only offered the compile-time DEBUG_SMC fprintf() macro for introspection. Add trace events for the command and data port transactions: the incoming command byte and whether it was accepted or rejected, each four-character key as it is assembled, key lookups that miss, and every data byte handed back. The SMC conversation a guest performs can then be observed at runtime, for example with -trace 'applesmc*', which is helpful for understanding which keys and commands a macOS guest actually issues. Signed-off-by: Daniel Golle Reviewed-by/Tested-by for his series are in my replies to it. --- hw/misc/applesmc.c | 13 +++++++++++++ hw/misc/trace-events | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index d004e8b443..6f9d0a590d 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -39,6 +39,7 @@ #include "qemu/timer.h" #include "qom/object.h" #include "hw/acpi/acpi_aml_interface.h" +#include "trace.h" =20 /* #define DEBUG_SMC */ =20 @@ -123,6 +124,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr = addr, uint64_t val, AppleSMCState *s =3D opaque; uint8_t status =3D s->status & 0x0f; =20 + trace_applesmc_cmd_write((uint8_t)val, s->status); smc_debug("CMD received: 0x%02x\n", (uint8_t)val); switch (val) { case APPLESMC_READ_CMD: @@ -130,6 +132,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr = addr, uint64_t val, if (status =3D=3D APPLESMC_ST_CMD_DONE || status =3D=3D APPLESMC_S= T_NEW_CMD) { s->cmd =3D val; s->status =3D APPLESMC_ST_NEW_CMD | APPLESMC_ST_ACK; + trace_applesmc_cmd_accepted((uint8_t)val); } else { smc_debug("ERROR: previous command interrupted!\n"); s->status =3D APPLESMC_ST_NEW_CMD; @@ -140,6 +143,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr = addr, uint64_t val, smc_debug("UNEXPECTED CMD 0x%02x\n", (uint8_t)val); s->status =3D APPLESMC_ST_NEW_CMD; s->status_1e =3D APPLESMC_ST_1E_BAD_CMD; + trace_applesmc_cmd_rejected((uint8_t)val, s->status_1e); } s->read_pos =3D 0; s->data_pos =3D 0; @@ -164,6 +168,7 @@ static void applesmc_io_data_write(void *opaque, hwaddr= addr, uint64_t val, const struct AppleSMCData *d; =20 smc_debug("DATA received: 0x%02x\n", (uint8_t)val); + trace_applesmc_data_write(s->cmd, s->read_pos, (uint8_t)val); switch (s->cmd) { case APPLESMC_READ_CMD: if ((s->status & 0x0f) =3D=3D APPLESMC_ST_CMD_DONE) { @@ -172,6 +177,10 @@ static void applesmc_io_data_write(void *opaque, hwadd= r addr, uint64_t val, if (s->read_pos < 4) { s->key[s->read_pos] =3D val; s->status =3D APPLESMC_ST_ACK; + if (s->read_pos =3D=3D 3) { + trace_applesmc_key_selected(s->key[0], s->key[1], + s->key[2], s->key[3]); + } } else if (s->read_pos =3D=3D 4) { d =3D applesmc_find_key(s); if (d !=3D NULL) { @@ -183,6 +192,8 @@ static void applesmc_io_data_write(void *opaque, hwaddr= addr, uint64_t val, } 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]); + trace_applesmc_key_not_found(s->key[0], s->key[1], + s->key[2], s->key[3]); s->status =3D APPLESMC_ST_CMD_DONE; s->status_1e =3D APPLESMC_ST_1E_NOEXIST; } @@ -213,6 +224,8 @@ static uint64_t applesmc_io_data_read(void *opaque, hwa= ddr addr, unsigned size) } if (s->data_pos < s->data_len) { s->last_ret =3D s->data[s->data_pos]; + trace_applesmc_data_read(s->key[0], s->key[1], s->key[2], + s->key[3], s->data_pos, s->last_ret); smc_debug("READ '%c%c%c%c'[%d] =3D %02x\n", s->key[0], s->key[1], s->key[2], s->key[3], s->data_pos, s->last_ret); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c9a868b3ef..b924b4398e 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -37,6 +37,15 @@ allwinner_sid_write(uint64_t offset, uint64_t data, unsi= gned size) "offset 0x%" allwinner_sramc_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 "= data 0x%" PRIx64 allwinner_sramc_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 = " data 0x%" PRIx64 =20 +# applesmc.c +applesmc_cmd_write(uint8_t cmd, uint8_t status) "cmd 0x%02x (status 0x%02x= )" +applesmc_cmd_accepted(uint8_t cmd) "cmd 0x%02x accepted" +applesmc_cmd_rejected(uint8_t cmd, uint8_t status_1e) "cmd 0x%02x rejected= (status_1e 0x%02x)" +applesmc_data_write(uint8_t cmd, uint8_t pos, uint8_t val) "cmd 0x%02x pos= %u data 0x%02x" +applesmc_key_selected(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "key= %c%c%c%c" +applesmc_key_not_found(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "ke= y %c%c%c%c not found" +applesmc_data_read(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t= pos, uint8_t val) "read key %c%c%c%c [%u] 0x%02x" + # avr_power.c avr_power_read(uint8_t value) "power_reduc read value:%u" avr_power_write(uint8_t value) "power_reduc write value:%u" --=20 2.55.0 From nobody Wed Aug 19 23:03:19 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787143472016308.5781756160965; Wed, 19 Aug 2026 05:44:32 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wwfdp-000849-MZ; Wed, 19 Aug 2026 08:43:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzY-0005Q6-QT for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:40 -0400 Received: from pidgin.makrotopia.org ([2a07:2ec0:3002::65]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzX-0000Li-8V for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:40 -0400 Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wwZzS-000000002R6-3ZgM; Wed, 19 Aug 2026 06:41:34 +0000 Date: Wed, 19 Aug 2026 07:41:32 +0100 From: Daniel Golle To: Daniel Golle , Paolo Bonzini , Alexander Graf , Pierrick Bouvier , Philippe =?iso-8859-1?Q?Mathieu-Daud=E9?= , =?iso-8859-1?Q?C=E9dric?= Le Goater , Peter Maydell , Kane-Chen-AS , jack wang <163wangjack@gmail.com>, qemu-devel@nongnu.org Subject: [PATCH 2/4] hw/misc/applesmc: Implement the write command Message-ID: References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a07:2ec0:3002::65; envelope-from=daniel@makrotopia.org; helo=pidgin.makrotopia.org X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 19 Aug 2026 08:43:31 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-Zoho-Virus-Status: 1 X-Zoho-AV-Stamp: zmail-av-0.2.10.1.5.2/287.128.6 X-ZM-MESSAGEID: 1787143473785158500 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The device implemented only the SMC read command (0x10). The write command (0x11) fell through to the default case and returned kSMCBadCommand, so every SMC key write a guest performs failed. A macOS guest writes keys through this command in normal operation: at boot it writes "NTOK", "MSDW", "QENA" and "HE0N", and retrying those failed writes delays boot; at shutdown it writes "OSWD". All of them showed up as a stream of kSMCBadCommand errors. Accept the write command, collect the key, the declared length and the payload, and return success. The payload is not interpreted yet, so the writes are accepted and discarded, which is enough to remove the boot delay and the shutdown errors; the following patch gives the watchdog keys meaning. Signed-off-by: Daniel Golle Reviewed-by/Tested-by for his series are in my replies to it. --- hw/misc/applesmc.c | 39 +++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 3 +++ 2 files changed, 42 insertions(+) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 6f9d0a590d..bc44b0f1d8 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -128,6 +128,7 @@ 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: + case APPLESMC_WRITE_CMD: /* did last command run through OK? */ if (status =3D=3D APPLESMC_ST_CMD_DONE || status =3D=3D APPLESMC_S= T_NEW_CMD) { s->cmd =3D val; @@ -200,6 +201,44 @@ static void applesmc_io_data_write(void *opaque, hwadd= r addr, uint64_t val, } s->read_pos++; break; + case APPLESMC_WRITE_CMD: + if (s->read_pos < 4) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + if (s->read_pos =3D=3D 3) { + trace_applesmc_key_selected(s->key[0], s->key[1], + s->key[2], s->key[3]); + } + s->read_pos++; + } else if (s->read_pos =3D=3D 4) { + s->data_len =3D val; + s->data_pos =3D 0; + s->read_pos++; + s->status =3D APPLESMC_ST_ACK; + trace_applesmc_write_len(s->key[0], s->key[1], s->key[2], + s->key[3], s->data_len); + if (s->data_len =3D=3D 0) { + s->status =3D APPLESMC_ST_CMD_DONE; + s->status_1e =3D APPLESMC_ST_CMD_DONE; + } + } else { + if (s->data_pos < s->data_len) { + s->data[s->data_pos] =3D val; + trace_applesmc_write_data(s->key[0], s->key[1], s->key[2], + s->key[3], s->data_pos, (uint8_t= )val); + s->data_pos++; + } + if (s->data_pos >=3D s->data_len) { + trace_applesmc_write_complete(s->key[0], s->key[1], + s->key[2], s->key[3], + s->data_len); + s->status =3D APPLESMC_ST_CMD_DONE; + s->status_1e =3D APPLESMC_ST_CMD_DONE; + } else { + s->status =3D APPLESMC_ST_ACK; + } + } + break; default: s->status =3D APPLESMC_ST_CMD_DONE; s->status_1e =3D APPLESMC_ST_1E_STILL_BAD_CMD; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b924b4398e..fc01cc9b7d 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -45,6 +45,9 @@ applesmc_data_write(uint8_t cmd, uint8_t pos, uint8_t val= ) "cmd 0x%02x pos %u da applesmc_key_selected(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "key= %c%c%c%c" applesmc_key_not_found(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "ke= y %c%c%c%c not found" applesmc_data_read(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t= pos, uint8_t val) "read key %c%c%c%c [%u] 0x%02x" +applesmc_write_len(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t= len) "write key %c%c%c%c len %u" +applesmc_write_data(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_= t pos, uint8_t val) "write key %c%c%c%c [%u] 0x%02x" +applesmc_write_complete(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, ui= nt8_t len) "write key %c%c%c%c complete (%u bytes)" =20 # avr_power.c avr_power_read(uint8_t value) "power_reduc read value:%u" --=20 2.55.0 From nobody Wed Aug 19 23:03:19 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787143479711396.0535870793426; Wed, 19 Aug 2026 05:44:39 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wwfdp-00080q-FE; Wed, 19 Aug 2026 08:43:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzh-0005TN-Ko for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:49 -0400 Received: from pidgin.makrotopia.org ([2a07:2ec0:3002::65]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzf-0000Oq-No for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:49 -0400 Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wwZzb-000000002Rb-0NKK; Wed, 19 Aug 2026 06:41:43 +0000 Date: Wed, 19 Aug 2026 07:41:40 +0100 From: Daniel Golle To: Daniel Golle , Paolo Bonzini , Alexander Graf , Pierrick Bouvier , Philippe =?iso-8859-1?Q?Mathieu-Daud=E9?= , =?iso-8859-1?Q?C=E9dric?= Le Goater , Peter Maydell , Kane-Chen-AS , jack wang <163wangjack@gmail.com>, qemu-devel@nongnu.org Subject: [PATCH 3/4] hw/misc/applesmc: Emulate the SMC shutdown watchdog Message-ID: <1a25c5a68854462891d8830847b28687fa5fe432.1787114994.git.daniel@makrotopia.org> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a07:2ec0:3002::65; envelope-from=daniel@makrotopia.org; helo=pidgin.makrotopia.org X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 19 Aug 2026 08:43:31 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-Zoho-Virus-Status: 1 X-Zoho-AV-Stamp: zmail-av-0.2.10.1.5.2/287.128.6 X-ZM-MESSAGEID: 1787143482202158500 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" "NATi", "NATJ" and "OSWD" are the SMC shutdown watchdog keys. On real Intel Macs the OS arms this watchdog so that, if the machine wedges, the SMC forces it off or reset. "NATi" and "OSWD" hold a countdown in seconds and "NATJ" a job code (1 shut down, 2 restart); a non-zero countdown arms or refreshes the watchdog and zero disarms it. The previous patch already lets writes to these keys succeed, but treats them like any other write. Model them as the watchdog they are instead of silently accepting them: back them with a QEMUTimer that a write arms, refreshes or disarms, and whose expiry requests a guest shutdown or reset through the main loop with no guest cooperation, matching the hardware backstop. Add "NATi" and "OSWD" so the guest can read them back. A traced macOS guest writes "OSWD" with a zero countdown to disarm the watchdog during shutdown, which this handles; the arm-and-expiry path is the documented behaviour for a guest that arms it. Signed-off-by: Daniel Golle Reviewed-by/Tested-by for his series are in my replies to it. --- hw/misc/applesmc.c | 74 ++++++++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 3 ++ 2 files changed, 77 insertions(+) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index bc44b0f1d8..8d5bf74373 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -39,6 +39,7 @@ #include "qemu/timer.h" #include "qom/object.h" #include "hw/acpi/acpi_aml_interface.h" +#include "system/runstate.h" #include "trace.h" =20 /* #define DEBUG_SMC */ @@ -80,6 +81,17 @@ enum { APPLESMC_ST_1E_BAD_INDEX =3D 0xb8, }; =20 +/* + * Job codes written to the "NATJ" key, and implied by "OSWD": the action = the + * SMC watchdog takes when its countdown (seeded from "NATi"/"OSWD") elaps= es + * because the guest failed to power down in time. + */ +enum { + APPLESMC_WDT_DISABLE =3D 0, + APPLESMC_WDT_SHUTDOWN =3D 1, + APPLESMC_WDT_RESTART =3D 2, +}; + #ifdef DEBUG_SMC #define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) #else @@ -116,6 +128,10 @@ struct AppleSMCState { uint8_t data[255]; char *osk; QLIST_HEAD(, AppleSMCData) data_def; + + QEMUTimer *wdt_timer; /* shutdown watchdog, armed via NATi/NATJ/OSWD= */ + uint16_t wdt_timeout; /* countdown in seconds */ + uint8_t wdt_job; /* action on expiry (APPLESMC_WDT_*) */ }; =20 static void applesmc_io_cmd_write(void *opaque, hwaddr addr, uint64_t val, @@ -162,6 +178,48 @@ static const struct AppleSMCData *applesmc_find_key(Ap= pleSMCState *s) return NULL; } =20 +static void applesmc_wdt_expired(void *opaque) +{ + AppleSMCState *s =3D opaque; + + trace_applesmc_wdt_expired(s->wdt_job); + warn_report("applesmc: watchdog expired, forcing the guest down"); + if (s->wdt_job =3D=3D APPLESMC_WDT_SHUTDOWN) { + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + } else { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } +} + +static void applesmc_wdt_set(AppleSMCState *s, uint8_t job, uint16_t secon= ds) +{ + s->wdt_job =3D job; + if (job =3D=3D APPLESMC_WDT_DISABLE || seconds =3D=3D 0) { + timer_del(s->wdt_timer); + trace_applesmc_wdt_disarm(); + return; + } + timer_mod(s->wdt_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (int64_t)seconds * NANOSECONDS_PER_SECOND); + trace_applesmc_wdt_arm(seconds, job); +} + +/* Act on a guest write once its full payload has been received. */ +static void applesmc_write_key(AppleSMCState *s) +{ + if (!memcmp(s->key, "NATi", 4) && s->data_len >=3D 2) { + /* Big-endian seconds; stored only, the "NATJ" write arms the time= r. */ + s->wdt_timeout =3D (s->data[0] << 8) | s->data[1]; + } else if (!memcmp(s->key, "NATJ", 4) && s->data_len >=3D 1) { + applesmc_wdt_set(s, s->data[0], s->wdt_timeout); + } else if (!memcmp(s->key, "OSWD", 4) && s->data_len >=3D 2) { + uint16_t seconds =3D (s->data[0] << 8) | s->data[1]; + uint8_t job =3D seconds ? APPLESMC_WDT_RESTART : APPLESMC_WDT_DISA= BLE; + + applesmc_wdt_set(s, job, seconds); + } +} + static void applesmc_io_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -218,6 +276,7 @@ static void applesmc_io_data_write(void *opaque, hwaddr= addr, uint64_t val, trace_applesmc_write_len(s->key[0], s->key[1], s->key[2], s->key[3], s->data_len); if (s->data_len =3D=3D 0) { + applesmc_write_key(s); s->status =3D APPLESMC_ST_CMD_DONE; s->status_1e =3D APPLESMC_ST_CMD_DONE; } @@ -229,6 +288,7 @@ static void applesmc_io_data_write(void *opaque, hwaddr= addr, uint64_t val, s->data_pos++; } if (s->data_pos >=3D s->data_len) { + applesmc_write_key(s); trace_applesmc_write_complete(s->key[0], s->key[1], s->key[2], s->key[3], s->data_len); @@ -325,6 +385,12 @@ static void qdev_applesmc_isa_reset(DeviceState *dev) s->status =3D 0x00; s->status_1e =3D 0x00; s->last_ret =3D 0x00; + + if (s->wdt_timer) { + timer_del(s->wdt_timer); + } + s->wdt_job =3D APPLESMC_WDT_DISABLE; + s->wdt_timeout =3D 0; } =20 static const MemoryRegionOps applesmc_data_io_ops =3D { @@ -388,6 +454,10 @@ static void applesmc_isa_realize(DeviceState *dev, Err= or **errp) applesmc_add_key(s, "NATJ", 1, "\x00"); applesmc_add_key(s, "MSSP", 1, "\x00"); applesmc_add_key(s, "MSSD", 1, "\x03"); + applesmc_add_key(s, "NATi", 2, "\x00\x00"); + applesmc_add_key(s, "OSWD", 2, "\x00\x00"); + + s->wdt_timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, applesmc_wdt_expired= , s); } =20 static void applesmc_unrealize(DeviceState *dev) @@ -395,6 +465,10 @@ static void applesmc_unrealize(DeviceState *dev) AppleSMCState *s =3D APPLE_SMC(dev); struct AppleSMCData *d, *next; =20 + if (s->wdt_timer) { + timer_free(s->wdt_timer); + } + /* Remove existing entries */ QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { QLIST_REMOVE(d, node); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index fc01cc9b7d..d94a5fba8a 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -48,6 +48,9 @@ applesmc_data_read(uint8_t c0, uint8_t c1, uint8_t c2, ui= nt8_t c3, uint8_t pos, applesmc_write_len(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t= len) "write key %c%c%c%c len %u" applesmc_write_data(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_= t pos, uint8_t val) "write key %c%c%c%c [%u] 0x%02x" applesmc_write_complete(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, ui= nt8_t len) "write key %c%c%c%c complete (%u bytes)" +applesmc_wdt_arm(uint16_t seconds, uint8_t job) "watchdog armed %u s, job = %u" +applesmc_wdt_disarm(void) "watchdog disarmed" +applesmc_wdt_expired(uint8_t job) "watchdog expired, job %u" =20 # avr_power.c avr_power_read(uint8_t value) "power_reduc read value:%u" --=20 2.55.0 From nobody Wed Aug 19 23:03:19 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1787143493555120.4426386724814; Wed, 19 Aug 2026 05:44:53 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wwfdr-0008CG-BZ; Wed, 19 Aug 2026 08:43:39 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzn-0005Uh-H9 for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:55 -0400 Received: from pidgin.makrotopia.org ([2a07:2ec0:3002::65]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wwZzl-0000QC-Pr for qemu-devel@nongnu.org; Wed, 19 Aug 2026 02:41:55 -0400 Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wwZzh-000000002Ru-19Ui; Wed, 19 Aug 2026 06:41:49 +0000 Date: Wed, 19 Aug 2026 07:41:46 +0100 From: Daniel Golle To: Daniel Golle , Paolo Bonzini , Alexander Graf , Pierrick Bouvier , Philippe =?iso-8859-1?Q?Mathieu-Daud=E9?= , =?iso-8859-1?Q?C=E9dric?= Le Goater , Peter Maydell , Kane-Chen-AS , jack wang <163wangjack@gmail.com>, qemu-devel@nongnu.org Subject: [PATCH 4/4] hw/misc/applesmc: Implement the get-key-type command Message-ID: <1a56c5b024ebf2c0c80dda7ac9b106e1b3b87ec4.1787114994.git.daniel@makrotopia.org> References: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2a07:2ec0:3002::65; envelope-from=daniel@makrotopia.org; helo=pidgin.makrotopia.org X-Spam_score_int: -18 X-Spam_score: -1.9 X-Spam_bar: - X-Spam_report: (-1.9 / 5.0 requ) BAYES_00=-1.9, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Wed, 19 Aug 2026 08:43:31 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-Zoho-Virus-Status: 1 X-Zoho-AV-Stamp: zmail-av-0.2.10.1.5.2/287.128.6 X-ZM-MESSAGEID: 1787143493840158500 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" macOS probes the type of many keys through the SMC get-key-type command (0x13), dozens of times during boot. The device did not implement it, so each probe fell through to the default case and returned kSMCBadCommand. Implement it. Give each key a four-character SMC type ("ui8 ", "ui16", "ch8*", "{rev", ...) and answer the command with the standard key information structure: the one-byte data size, the four-byte type and a one-byte attributes field (reported as zero, as the device does not model key attributes). Unknown keys return kSMCKeyNotFound, consistent with a read of a missing key. macOS tolerates the probe failing, so this is not a functional fix, but it replies to the queries the guest makes instead of rejecting them. Signed-off-by: Daniel Golle Reviewed-by/Tested-by for his series are in my replies to it. --- hw/misc/applesmc.c | 52 ++++++++++++++++++++++++++++++++++++-------- hw/misc/trace-events | 1 + 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 8d5bf74373..47e7e3f4be 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -104,6 +104,7 @@ static char default_osk[64] =3D "This is a dummy key. E= nter the real key " struct AppleSMCData { uint8_t len; const char *key; + const char *type; const char *data; QLIST_ENTRY(AppleSMCData) node; }; @@ -145,6 +146,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr = addr, uint64_t val, switch (val) { case APPLESMC_READ_CMD: case APPLESMC_WRITE_CMD: + case APPLESMC_GET_KEY_TYPE_CMD: /* did last command run through OK? */ if (status =3D=3D APPLESMC_ST_CMD_DONE || status =3D=3D APPLESMC_S= T_NEW_CMD) { s->cmd =3D val; @@ -299,6 +301,36 @@ static void applesmc_io_data_write(void *opaque, hwadd= r addr, uint64_t val, } } break; + case APPLESMC_GET_KEY_TYPE_CMD: + /* Unlike a read, the guest sends only the 4 key bytes, no length.= */ + if (s->read_pos < 4) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + if (++s->read_pos =3D=3D 4) { + trace_applesmc_key_selected(s->key[0], s->key[1], + s->key[2], s->key[3]); + d =3D applesmc_find_key(s); + if (d !=3D NULL) { + /* key info: 1-byte size, 4-byte type, 1-byte attribut= es */ + s->data[0] =3D d->len; + memcpy(&s->data[1], d->type, 4); + s->data[5] =3D 0; + s->data_len =3D 6; + s->data_pos =3D 0; + s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; + s->status_1e =3D APPLESMC_ST_CMD_DONE; + trace_applesmc_key_type(s->key[0], s->key[1], s->key[2= ], + s->key[3], d->type[0], d->type= [1], + d->type[2], d->type[3], d->len= ); + } else { + trace_applesmc_key_not_found(s->key[0], s->key[1], + s->key[2], s->key[3]); + s->status =3D APPLESMC_ST_CMD_DONE; + s->status_1e =3D APPLESMC_ST_1E_NOEXIST; + } + } + } + break; default: s->status =3D APPLESMC_ST_CMD_DONE; s->status_1e =3D APPLESMC_ST_1E_STILL_BAD_CMD; @@ -318,6 +350,7 @@ static uint64_t applesmc_io_data_read(void *opaque, hwa= ddr addr, unsigned size) =20 switch (s->cmd) { case APPLESMC_READ_CMD: + case APPLESMC_GET_KEY_TYPE_CMD: if (!(s->status & APPLESMC_ST_DATA_READY)) { break; } @@ -366,12 +399,13 @@ static uint64_t applesmc_io_err_read(void *opaque, hw= addr addr, unsigned size) } =20 static void applesmc_add_key(AppleSMCState *s, const char *key, - int len, const char *data) + const char *type, int len, const char *data) { struct AppleSMCData *def; =20 def =3D g_new0(struct AppleSMCData, 1); def->key =3D key; + def->type =3D type; def->len =3D len; def->data =3D data; =20 @@ -448,14 +482,14 @@ static void applesmc_isa_realize(DeviceState *dev, Er= ror **errp) } =20 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, "\x00"); - applesmc_add_key(s, "MSSP", 1, "\x00"); - applesmc_add_key(s, "MSSD", 1, "\x03"); - applesmc_add_key(s, "NATi", 2, "\x00\x00"); - applesmc_add_key(s, "OSWD", 2, "\x00\x00"); + applesmc_add_key(s, "REV ", "{rev", 6, "\x01\x13\x0f\x00\x00\x03"); + applesmc_add_key(s, "OSK0", "ch8*", 32, s->osk); + applesmc_add_key(s, "OSK1", "ch8*", 32, s->osk + 32); + applesmc_add_key(s, "NATJ", "ui8 ", 1, "\x00"); + applesmc_add_key(s, "MSSP", "ui8 ", 1, "\x00"); + applesmc_add_key(s, "MSSD", "si8 ", 1, "\x03"); + applesmc_add_key(s, "NATi", "ui16", 2, "\x00\x00"); + applesmc_add_key(s, "OSWD", "ui16", 2, "\x00\x00"); =20 s->wdt_timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, applesmc_wdt_expired= , s); } diff --git a/hw/misc/trace-events b/hw/misc/trace-events index d94a5fba8a..bd79fa45e1 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -44,6 +44,7 @@ applesmc_cmd_rejected(uint8_t cmd, uint8_t status_1e) "cm= d 0x%02x rejected (stat applesmc_data_write(uint8_t cmd, uint8_t pos, uint8_t val) "cmd 0x%02x pos= %u data 0x%02x" applesmc_key_selected(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "key= %c%c%c%c" applesmc_key_not_found(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "ke= y %c%c%c%c not found" +applesmc_key_type(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t = t0, uint8_t t1, uint8_t t2, uint8_t t3, uint8_t len) "type key %c%c%c%c =3D= %c%c%c%c[%u]" applesmc_data_read(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t= pos, uint8_t val) "read key %c%c%c%c [%u] 0x%02x" applesmc_write_len(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t= len) "write key %c%c%c%c len %u" applesmc_write_data(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_= t pos, uint8_t val) "write key %c%c%c%c [%u] 0x%02x" --=20 2.55.0