From nobody Sat May 30 16:41:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=none dis=none) header.from=pq.io ARC-Seal: i=1; a=rsa-sha256; t=1778174542; cv=none; d=zohomail.com; s=zohoarc; b=j9tQfVKvdHiU6SCy9msh4g6TfvrSfLhMHs0f4W1jFnKVKTyum4xt+4MOYDi+eiukQHcDUfoNuP2A2n/MKseAyUZX8UhZauRSpBeRAhyya/BMo785X94p8N588zYqx5R4XKMZ3lrDoRQ8fAt6K+qA+kV9geUKtf4oLGIn5P+y75M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778174542; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=QoT6ZV0zp2yCk/oaO3p5EzNmYDCXteG6Q8NREGInOLI=; b=C8f5FKb5J+6djsSbiX4MkrqNn34DvjLRukq4dXbLM4jP3QL5EE/VT7Quq8oyJA1HaVWgBmz86VTEP2NXbfqZlYaP7/A5Ta2qwPkgxyyXK2F9r/yzeHVjPAir0GEcVXvIpWPsKYThkXKWNWlZLZ0Lv4QE7qHh20q516RjXx26scQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1778174542898899.1235232198277; Thu, 7 May 2026 10:22:22 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wL2PP-0000su-LP; Thu, 07 May 2026 13:21:12 -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 1wL0Wq-0004AN-5X for qemu-devel@nongnu.org; Thu, 07 May 2026 11:20:44 -0400 Received: from mail-244107.protonmail.ch ([109.224.244.107]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wL0Wm-00046S-NX for qemu-devel@nongnu.org; Thu, 07 May 2026 11:20:43 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pq.io; s=protonmail3; t=1778167236; x=1778426436; bh=QoT6ZV0zp2yCk/oaO3p5EzNmYDCXteG6Q8NREGInOLI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=NteZ6GXvVcD2fAX01t/eyJ3rvh6JcDGTrHsfz+g9Gj+ys0Xfb+MtoE3Asw77xsIDG hnS3m97IHIw3HJxMVlIytbKQmMdaZW0g/LuSVDIumrvsAdsWiqo9spw8yaFxPP2qkI WJ1GAkudqktVC2JIA5HytSBCNdIuH86I0idyBV/qnq5dg3IvX4BEHEFzHZiWckU4YH ckv8tFO5eHNFaQ6oyPuPfJC35qZg2ZSUI7yNSuNzPTkHgmddszQQbKZH9siqgq+VOa /QJv8yqyB4aoia1RAqEPW3EkDu57n/mnnHQxY8byLzFay2oO4vHJBY0sc7zsw2pkYN x1yCsQO3izChg== X-Pm-Submission-Id: 4gBGCj2DxZz2Scpx From: Matthew Jackson To: qemu-devel@nongnu.org Cc: stefanha@redhat.com, peter.maydell@linaro.org Subject: [PATCH v2 1/2] hw/misc/applesmc: fix GET_KEY_BY_INDEX to return real keys, accept WRITE/TYPE commands Date: Thu, 7 May 2026 08:20:29 -0700 Message-ID: <20260507152030.48753-2-matthew@pq.io> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260507040153.14565-1-matthew@pq.io> References: <20260507040153.14565-1-matthew@pq.io> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=109.224.244.107; envelope-from=matthew@pq.io; helo=mail-244107.protonmail.ch X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 07 May 2026 13:21:05 -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-ZohoMail-DKIM: pass (identity @pq.io) X-ZM-MESSAGEID: 1778174544183158500 Content-Type: text/plain; charset="utf-8" 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=3D. 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 --- hw/misc/applesmc.c | 177 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 7 deletions(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index fd96f5f..2b5ef3c 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 comma= nds + * return BAD_CMD during early AppleSMC driver init. + */ 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; @@ -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 =3D APPLESMC_ST_NEW_CMD; s->status_1e =3D APPLESMC_ST_1E_BAD_CMD; } @@ -179,17 +188,170 @@ static void applesmc_io_data_write(void *opaque, hwa= ddr addr, uint64_t val, s->data_len =3D d->len; s->data_pos =3D 0; s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; - s->status_1e =3D APPLESMC_ST_CMD_DONE; /* clear on valid = key */ + s->status_1e =3D 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 satisfi= es + * the probe without asserting a particular value. + */ + qemu_log_mask(LOG_UNIMP, + "applesmc: READ unknown key '%c%c%c%c' len= =3D%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 =3D (uint8_t)val; + s->data_pos =3D 0; + s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; + s->status_1e =3D 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) =3D=3D APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 4) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + } else if (s->read_pos =3D=3D 4) { + s->data_len =3D (uint8_t)val; + s->data_pos =3D 0; + s->status =3D APPLESMC_ST_ACK; + } else { + if (s->data_pos < s->data_len) { + s->data[s->data_pos] =3D (uint8_t)val; + s->data_pos++; + if (s->data_pos =3D=3D s->data_len) { + qemu_log_mask(LOG_UNIMP, + "applesmc: WRITE key '%c%c%c%c' len=3D%d= \n", + 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; + } + } + } + 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) =3D=3D APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 3) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + } else if (s->read_pos =3D=3D 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] =3D val; + d =3D applesmc_find_key(s); + if (d !=3D NULL) { + switch (d->len) { + case 1: + s->data[0] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '8'; s->data[3] =3D ' '; + break; + case 2: + s->data[0] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '1'; s->data[3] =3D '6'; + break; + case 4: + s->data[0] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '3'; s->data[3] =3D '2'; + break; + default: + s->data[0] =3D 'c'; s->data[1] =3D 'h'; + s->data[2] =3D '8'; s->data[3] =3D '*'; + break; + } + s->data[4] =3D d->len; + s->data[5] =3D 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] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '8'; s->data[3] =3D ' '; + s->data[4] =3D 1; + s->data[5] =3D 0xD0; + } + 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; + } + 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) =3D=3D APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 3) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + } else if (s->read_pos =3D=3D 3) { + s->key[3] =3D val; + uint32_t idx =3D ((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 =3D 0; + bool found =3D false; + QLIST_FOREACH(def, &s->data_def, node) { + if (i =3D=3D idx) { + memcpy(s->data, def->key, 4); + s->data_len =3D 4; + s->data_pos =3D 0; + found =3D true; + break; + } + i++; + } + if (!found) { + s->data_len =3D 0; + s->status_1e =3D APPLESMC_ST_1E_BAD_INDEX; s->status =3D APPLESMC_ST_CMD_DONE; - s->status_1e =3D APPLESMC_ST_1E_NOEXIST; + s->read_pos++; + break; } + s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; + s->status_1e =3D 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 =3D APPLESMC_ST_CMD_DONE; s->status_1e =3D APPLESMC_ST_1E_STILL_BAD_CMD; } @@ -330,12 +492,13 @@ 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, "\0"); applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); + applesmc_add_key(s, "MSSD", 1, "\x03"); } =20 static void applesmc_unrealize(DeviceState *dev) --=20 2.50.1 (Apple Git-155) From nobody Sat May 30 16:41:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=none dis=none) header.from=pq.io ARC-Seal: i=1; a=rsa-sha256; t=1778163281; cv=none; d=zohomail.com; s=zohoarc; b=a5XFCrw4H3CKy0B3JK39/H95PfLyVR/hJJNd9vHpKm0LtwhdW7LXj4B6GJnTCveTkxH0ijaFbx59m3ge015MYu5SqjiFHryhtlpnEWVwIh2XRzMsDZkvyl7bcPWv5u6CMPG9gJ2PePmE+3dXn6fKL3l8VDYDsS1P7cb4ZVE0EhE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778163281; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=QoT6ZV0zp2yCk/oaO3p5EzNmYDCXteG6Q8NREGInOLI=; b=XdLB14tBLPm/0hgB767awOgCfCjRkzkgRAEKgSlaBN2m92biykfOL7PsbyNB4DyHF/dsjcajuZqwEnWVt+40IIhjLU3WMg+VtiwfIzE9eCAAQMvkAjQEMFYhXvrNs3MnZhLojH7+6lMECvePADM40JdRLNsJzGGJYjJ+mVWKnxs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1778163281860358.57920664500364; Thu, 7 May 2026 07:14:41 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wKzUD-0007Ak-CB; Thu, 07 May 2026 10:13:57 -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 1wKpwH-0005nn-9M for qemu-devel@nongnu.org; Thu, 07 May 2026 00:02:17 -0400 Received: from mail-07.mail-europe.com ([188.165.51.139]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wKpwE-0004lR-SL for qemu-devel@nongnu.org; Thu, 07 May 2026 00:02:16 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pq.io; s=protonmail3; t=1778126519; x=1778385719; bh=QoT6ZV0zp2yCk/oaO3p5EzNmYDCXteG6Q8NREGInOLI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=EmYhYA//9Wh0jPzyeEjb4d7pK60XHxV8YTSidMF32B/un6iFBhdLUbXXoMruKy/5W RTZ2Xk2BCdQrWWJPNFjAJGtY3c8lY7NbqONUUJ8d6y2F0Jskd+MAxv0XNjM0k8zxHG Q3ShnERv9+ik6W00yBIyVFW5Xb5RWVfVFmy0cKyScYIXAZpZIfxwvFke3tS/maRfUf xex3Kbe2CNwTJjRoy1S8zgLUOyTbKkM6xzeGLiH2t3YIVXLquwuxnbuPLsT7a0YGEo Ca8jX7QyveT/zNwYx6EewU+U/lyClH8NOJCduVf3XCZFs3l3T4GV1DIH/uUHnIAwor OfA7qCKM1lWew== X-Pm-Submission-Id: 4g9z8j2VCjz1DFFg From: Matthew Jackson To: qemu-devel@nongnu.org Cc: stefanha@redhat.com Subject: [PATCH 1/2] hw/misc/applesmc: fix GET_KEY_BY_INDEX to return real keys, accept WRITE/TYPE commands Date: Wed, 6 May 2026 21:01:52 -0700 Message-ID: <20260507040153.14565-2-matthew@pq.io> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260507040153.14565-1-matthew@pq.io> References: <20260507040153.14565-1-matthew@pq.io> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=188.165.51.139; envelope-from=matthew@pq.io; helo=mail-07.mail-europe.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 07 May 2026 10:13:41 -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-ZohoMail-DKIM: pass (identity @pq.io) X-ZM-MESSAGEID: 1778163282416158500 Content-Type: text/plain; charset="utf-8" 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=3D. 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 --- hw/misc/applesmc.c | 177 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 7 deletions(-) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index fd96f5f..2b5ef3c 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 comma= nds + * return BAD_CMD during early AppleSMC driver init. + */ 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; @@ -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 =3D APPLESMC_ST_NEW_CMD; s->status_1e =3D APPLESMC_ST_1E_BAD_CMD; } @@ -179,17 +188,170 @@ static void applesmc_io_data_write(void *opaque, hwa= ddr addr, uint64_t val, s->data_len =3D d->len; s->data_pos =3D 0; s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; - s->status_1e =3D APPLESMC_ST_CMD_DONE; /* clear on valid = key */ + s->status_1e =3D 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 satisfi= es + * the probe without asserting a particular value. + */ + qemu_log_mask(LOG_UNIMP, + "applesmc: READ unknown key '%c%c%c%c' len= =3D%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 =3D (uint8_t)val; + s->data_pos =3D 0; + s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; + s->status_1e =3D 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) =3D=3D APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 4) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + } else if (s->read_pos =3D=3D 4) { + s->data_len =3D (uint8_t)val; + s->data_pos =3D 0; + s->status =3D APPLESMC_ST_ACK; + } else { + if (s->data_pos < s->data_len) { + s->data[s->data_pos] =3D (uint8_t)val; + s->data_pos++; + if (s->data_pos =3D=3D s->data_len) { + qemu_log_mask(LOG_UNIMP, + "applesmc: WRITE key '%c%c%c%c' len=3D%d= \n", + 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; + } + } + } + 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) =3D=3D APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 3) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + } else if (s->read_pos =3D=3D 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] =3D val; + d =3D applesmc_find_key(s); + if (d !=3D NULL) { + switch (d->len) { + case 1: + s->data[0] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '8'; s->data[3] =3D ' '; + break; + case 2: + s->data[0] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '1'; s->data[3] =3D '6'; + break; + case 4: + s->data[0] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '3'; s->data[3] =3D '2'; + break; + default: + s->data[0] =3D 'c'; s->data[1] =3D 'h'; + s->data[2] =3D '8'; s->data[3] =3D '*'; + break; + } + s->data[4] =3D d->len; + s->data[5] =3D 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] =3D 'u'; s->data[1] =3D 'i'; + s->data[2] =3D '8'; s->data[3] =3D ' '; + s->data[4] =3D 1; + s->data[5] =3D 0xD0; + } + 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; + } + 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) =3D=3D APPLESMC_ST_CMD_DONE) { + break; + } + if (s->read_pos < 3) { + s->key[s->read_pos] =3D val; + s->status =3D APPLESMC_ST_ACK; + } else if (s->read_pos =3D=3D 3) { + s->key[3] =3D val; + uint32_t idx =3D ((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 =3D 0; + bool found =3D false; + QLIST_FOREACH(def, &s->data_def, node) { + if (i =3D=3D idx) { + memcpy(s->data, def->key, 4); + s->data_len =3D 4; + s->data_pos =3D 0; + found =3D true; + break; + } + i++; + } + if (!found) { + s->data_len =3D 0; + s->status_1e =3D APPLESMC_ST_1E_BAD_INDEX; s->status =3D APPLESMC_ST_CMD_DONE; - s->status_1e =3D APPLESMC_ST_1E_NOEXIST; + s->read_pos++; + break; } + s->status =3D APPLESMC_ST_ACK | APPLESMC_ST_DATA_READY; + s->status_1e =3D 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 =3D APPLESMC_ST_CMD_DONE; s->status_1e =3D APPLESMC_ST_1E_STILL_BAD_CMD; } @@ -330,12 +492,13 @@ 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, "\0"); applesmc_add_key(s, "MSSP", 1, "\0"); - applesmc_add_key(s, "MSSD", 1, "\0x3"); + applesmc_add_key(s, "MSSD", 1, "\x03"); } =20 static void applesmc_unrealize(DeviceState *dev) --=20 2.50.1 (Apple Git-155) From nobody Sat May 30 16:41:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=none dis=none) header.from=pq.io ARC-Seal: i=1; a=rsa-sha256; t=1778174538; cv=none; d=zohomail.com; s=zohoarc; b=LGByV3HjmPJ3y0MvA0kY7ARvoKNBQ5XYBPzUCqmzT5mutXSlmuO3GLMFrk7Oj7ZKIIL+c5fQqVy0Tm78mwWhL19rnbaJZkF885BdVdfVcfklMc5mBQri4H0Yi+cpQkHZ8NCZb/PIkviNOJNw7Gi7yT37qc4ohC0PKPMhuW3/zXE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778174538; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=DTaXLW46kA4PzjKLdWJotCfuuUW2EZs3VOldH3Gkucg=; b=YG6Rr1dEIdZ4FKlyoUVkkPw9UBUiW81gF6oP26xXzFqVWTpZ0YCmdNGv2gc58O9D0hjpZuNJ+dvyQKTX4fnoWb2GQACKDp8taVAUxMhbQuRnOAdpswtaDnZWGDVOrhsoiG0L9un8G+oxuehfPi0l6yJxHjDGoXiXLIcGPwGTQpM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1778174538624753.1666039796073; Thu, 7 May 2026 10:22:18 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wL2PQ-0000sY-2J; Thu, 07 May 2026 13:21:12 -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 1wL0Wq-0004Bb-S5 for qemu-devel@nongnu.org; Thu, 07 May 2026 11:20:44 -0400 Received: from mail-244108.protonmail.ch ([109.224.244.108]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wL0Wm-00046I-Fz for qemu-devel@nongnu.org; Thu, 07 May 2026 11:20:44 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pq.io; s=protonmail3; t=1778167235; x=1778426435; bh=DTaXLW46kA4PzjKLdWJotCfuuUW2EZs3VOldH3Gkucg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=K0yXNX8ImRkYf9xgA0vDHUB68DpQj+QrCnfuwufokT9F+rHSiSDvtlqZYpboHhzu8 8SFeS50334bN5akCe6BH0MkV8oJ1MIUbH2M/W9yDpt6/PtjIL0lraowI3OdcnCHh1h S1zPMsi227s0ijjM7ie0iSgsgrUatWpsZty6gbPQbyIOFWxrvy6mEWh4HoOO72Wuw1 Koaqln5ycieQs6zn0uzJbGxklTAKpTLcXU7C3EAxK+fR5xaKvbLBK/G3g/YrPXfEgT ubTA917ySOaoCRbH1NR14DpTQ1GxNInRV/ihXNTmdauSpzO7pewvgUgM5GKWBoMbcA JbkMRsd7e0Iog== X-Pm-Submission-Id: 4gBGCj30yZz1DDLg From: Matthew Jackson To: qemu-devel@nongnu.org Cc: stefanha@redhat.com, peter.maydell@linaro.org Subject: [PATCH v2 2/2] hw/misc/applesmc: populate Apple SMC key table Date: Thu, 7 May 2026 08:20:30 -0700 Message-ID: <20260507152030.48753-3-matthew@pq.io> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260507040153.14565-1-matthew@pq.io> References: <20260507040153.14565-1-matthew@pq.io> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=109.224.244.108; envelope-from=matthew@pq.io; helo=mail-244108.protonmail.ch X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_MSPIKE_H2=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 07 May 2026 13:21:07 -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-ZohoMail-DKIM: pass (identity @pq.io) X-ZM-MESSAGEID: 1778174544997154100 Content-Type: text/plain; charset="utf-8" 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=3D999fc708a4&log=3Dsensors 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 (=3D F0Mn, matching the probe-reported idle), min 1200, max 3600. fpe2 encoding (raw =3D 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 --- 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 2b5ef3c..d11fb32 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -32,7 +32,7 @@ =20 #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" @@ -493,12 +493,179 @@ static void applesmc_isa_realize(DeviceState *dev, E= rror **errp) =20 QLIST_INIT(&s->data_def); =20 + /* 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"); /* current: 1200 RPM (idl= e =3D 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 =3D 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); + } } =20 static void applesmc_unrealize(DeviceState *dev) --=20 2.50.1 (Apple Git-155) From nobody Sat May 30 16:41:02 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; 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; dmarc=pass(p=none dis=none) header.from=pq.io ARC-Seal: i=1; a=rsa-sha256; t=1778163254; cv=none; d=zohomail.com; s=zohoarc; b=H5HnO1qfIB9KQkTnt9dVJ/4WFPXu+WFxkwjSsYMPLW8DcYSgGmV9yPsfwU4LWtK/owdtF+iezVLVCrHfpT9otCd0AambEQ6ztIKRCAGv6O4TCfC1+XOAXigdPBgH5J/yu31BCg6XgVb2buxmaRNLnWhccQxi+fmp1/RV3C3vl/M= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1778163254; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=++9yNFQZ47eOroZ1WhHhIjPpxmNbCFJax/8fWsXF6AA=; b=NHpxIPaF0LiWvqQcpGmf+Dju7MQEDmYAVZa/cP+rr3gmxp95f8ZPSSD9wgNHp2dHWlzP8LrXpY2SiiGc0KSkmwSnSC2HRqIAx+2rtsbxJ1tFHTSLWUj47RJeHUsRN+qqjNTn8OUR3F0H2DYDOL9llVA62/tyqVMivZJjfc60jwo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; 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; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1778163253928962.9259296480341; Thu, 7 May 2026 07:14:13 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wKzUB-0007A6-GD; Thu, 07 May 2026 10:13:55 -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 1wKpwE-0005nQ-DC for qemu-devel@nongnu.org; Thu, 07 May 2026 00:02:14 -0400 Received: from mail-05.mail-europe.com ([85.9.206.169]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wKpw9-0004lL-Al for qemu-devel@nongnu.org; Thu, 07 May 2026 00:02:14 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pq.io; s=protonmail3; t=1778126519; x=1778385719; bh=++9yNFQZ47eOroZ1WhHhIjPpxmNbCFJax/8fWsXF6AA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=syA3NcV9OsOyHhFSCydFhB3H49DOXclC/Vv8fmFAPanT8PIYI4Jr0E5J6Nr9ySNPx ZE51LYFWkY2naUAHIXwedLanBFEIy6+aj2Jyd5FlPWHpguZBr4mdXZ2e0/7Mq9WuDu dnJeT06ow5ePXahOEHf3KcL470S1zGjd4IlVf+YcxNkc2MtNaHaGXhkkhhK5j/zMoh BrmfUordWVLZElDJez7AftPnFYyIb2t9h1sCjx4Da7r63PHbyPnopke+xIWNi2bzsv ApFza1GBn7kqtE8kTwFtEvwxXeQHETxf7wpGT6adqJe+Bjz3RPl+MCdwemKQV5XjBx MM9ASRQCUoFFw== X-Pm-Submission-Id: 4g9z8j4FHwz2ScdG From: Matthew Jackson To: qemu-devel@nongnu.org Cc: stefanha@redhat.com Subject: [PATCH 2/2] hw/misc/applesmc: populate Apple SMC key table Date: Wed, 6 May 2026 21:01:53 -0700 Message-ID: <20260507040153.14565-3-matthew@pq.io> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260507040153.14565-1-matthew@pq.io> References: <20260507040153.14565-1-matthew@pq.io> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable 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=85.9.206.169; envelope-from=matthew@pq.io; helo=mail-05.mail-europe.com X-Spam_score_int: -27 X-Spam_score: -2.8 X-Spam_bar: -- X-Spam_report: (-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Thu, 07 May 2026 10:13:39 -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-ZohoMail-DKIM: pass (identity @pq.io) X-ZM-MESSAGEID: 1778163257629154100 Content-Type: text/plain; charset="utf-8" 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=3D999fc708a4&log=3Dsensors 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 (=3D F0Mn, matching the probe-reported idle), min 1200, max 3600. fpe2 encoding (raw =3D 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 --- hw/misc/applesmc.c | 167 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 2b5ef3c..bc2573d 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -493,12 +493,179 @@ static void applesmc_isa_realize(DeviceState *dev, E= rror **errp) =20 QLIST_INIT(&s->data_def); =20 + /* 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"); /* current: 1200 RPM (idl= e =3D 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 =3D 1; /* include #KEY itself */ + struct AppleSMCData *def; + QLIST_FOREACH(def, &s->data_def, node) count++; + static char numkey_buf[4]; + numkey_buf[0] =3D (count >> 24) & 0xff; + numkey_buf[1] =3D (count >> 16) & 0xff; + numkey_buf[2] =3D (count >> 8) & 0xff; + numkey_buf[3] =3D count & 0xff; + applesmc_add_key(s, "#KEY", 4, numkey_buf); + } } =20 static void applesmc_unrealize(DeviceState *dev) --=20 2.50.1 (Apple Git-155)