From nobody Sat Sep 05 23:32:16 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hcqPF3zY2z6rJhj for ; Sat, 05 Sep 2026 23:32:17 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hcqPF0ZMlz47Z0 for ; Sat, 05 Sep 2026 23:32:17 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1788651137; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=OyuOiitT3Dv82nshfSeskNf1NC6PBAA7v6nvHrXmiRc=; b=RXwT7QE6MsXBLBN3seKOrc9JnyMSbPqMt9rPUtJuwKbP2gE9kUO1IFZ8DH5SdXb+xh6SKA K1NL+sacRz6tQZyhIki04E6CBMo3M3iWLLwHlklsCOi7pIPavsEtR+DNVjFBNoxxIScC/6 2Ad45l9arETjkYOkIyerjHowXvEGj9rSfsKSYuvEK8WHRFenmUrB2iNdtrgpQqBksixUqi 6x9VGJH18ZI+SYtdDfuFbVjbMm9aZgGlFuEz84euAt58lSKkKwAxxP4kM4MDl1MACR2r2s 73rGRZm6hS604baMeW7N4gMGotrlviVOqg018QTgbn+zBsnlkCZH2cXoC6P2wQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1788651137; a=rsa-sha256; cv=none; b=BGD3FQUlKGD2yZ64CDE4y5lvmr5hmKUCgPypqPzMAhsFyWDgKdD1UH6060fIJ6PT73YbIG RaCSt86/KO5uHmr1ofa3KKQXJFadwSQNWbUnbtXdYzYYvjoadCa7c0tjrb+V2c3qSbZqQZ dhkTw5fGvwlTbmCuGo/dzaDnBoerDbajOArUHHGL1Uu1/s4pnprcLWPB1fiZ+dc59Cfa2n FK6C7Sgdkm6fs0lYkgKLrQiHKM0VyW2eH8/esGzEk4HItZzxobnJg9AiLLIk3sC5sySBRo zZ56FGpdoW54soK/gUIfhWKW+Ks7EvnZT2g0wpnFmR/WqaHqpQWUx9ea2Vwtdg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1788651137; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=OyuOiitT3Dv82nshfSeskNf1NC6PBAA7v6nvHrXmiRc=; b=hb1yfyEbS+iGMQ8WpAOekzRAe3y8RXdo7X3nCLvsb23FHz5F6surHVnZmHSRrobt71USL3 5kiHv8tH893O49pTWuSBKFyHGvoBFQ88kmV0Wd7LjfSGKaRyWntMr0KHf3DHEnVIaQbWS/ Zoqsx/P/ETShM7R0I/dmgMDCI/W72RrZbMgy34irAh1Q7osv9xUxeljK0cHziupytV2UNJ YAKIYIX3IXK5ADBJ2B+bd7GE7/T4WWnc7L0TIR7Em3g/y8h3vCNfuFrXrY7/g1qWEpC6fi 9aA0ylBYqH3dK4klGM7JpjZUMuKCg6eroDQ6qSBC2FCjg1R3kxOBe9SdFHD3UQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hcqPD6jZdz15n7 for ; Sat, 05 Sep 2026 23:32:16 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 24191 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sat, 05 Sep 2026 23:32:16 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Abdelkader Boudih From: Enji Cooper Subject: git: 967501b3c7cc - stable/14 - asmc: add raw SMC key read/write interface List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: ngie X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 967501b3c7cc170b6b373db22d137650b3ef519a Auto-Submitted: auto-generated Date: Sat, 05 Sep 2026 23:32:16 +0000 Message-Id: <6a9ca680.24191.3333263d@gitrepo.freebsd.org> The branch stable/14 has been updated by ngie: URL: https://cgit.FreeBSD.org/src/commit/?id=967501b3c7cc170b6b373db22d137650b3ef519a commit 967501b3c7cc170b6b373db22d137650b3ef519a Author: Abdelkader Boudih AuthorDate: 2026-04-15 04:20:52 +0000 Commit: Enji Cooper CommitDate: 2026-09-05 23:31:45 +0000 asmc: add raw SMC key read/write interface This patch adds a debugging interface to read and write arbitrary Apple SMC keys by name through sysctl, enabling hardware exploration and control of undocumented features. The interface provides four sysctls under dev.asmc.0.raw.*: - key - Set the 4-character SMC key name (e.g., "AUPO") - value - Read/write key value as a hex string - len - Auto-detected key value length (can be overridden) - type - Read-only 4-character type string (e.g., "ui8", "flt") Implementation includes a new asmc_key_getinfo() function using SMC command 0x13 to query key metadata. The interface automatically detects key lengths and types, uses hex string encoding for arbitrary binary values, and is safe for concurrent access via CTLFLAG_NEEDGIANT. This interface was essential for discovering that the AUPO key enables Wake-on-LAN from S5 state, and for mapping all 297 SMC keys on Mac Mini 5,1. Reviewed by: ngie, adrian, markj Differential Revision: https://reviews.freebsd.org/D54441 (cherry picked from commit 3e27114a7f961aac49d75a663a55332375d0bef3) --- share/man/man4/asmc.4 | 29 +++++ sys/dev/asmc/asmc.c | 287 +++++++++++++++++++++++++++++++++++++++++-------- sys/dev/asmc/asmcvar.h | 19 ++++ 3 files changed, 288 insertions(+), 47 deletions(-) diff --git a/share/man/man4/asmc.4 b/share/man/man4/asmc.4 index f3eddcaf965e..37976922acae 100644 --- a/share/man/man4/asmc.4 +++ b/share/man/man4/asmc.4 @@ -112,6 +112,35 @@ minimum fan speed, the minimum speed and the maximum speed respectively. .Pp All values are in RPM. +.Sh RAW SMC KEY ACCESS +When the kernel is compiled with the +.Dv ASMC_DEBUG +option, a set of sysctl nodes is provided under +.Va dev.asmc.%d.raw +for reading and writing arbitrary SMC keys by name. +.Pp +.Bl -tag -width "dev.asmc.%d.raw.value" -compact +.It Va dev.asmc.%d.raw.key +Set the 4-character SMC key name to access (e.g.,\& +.Dq AUPO ) . +Setting this automatically queries the key's length and type. +.It Va dev.asmc.%d.raw.value +Read or write the key's value as a hex string. +.It Va dev.asmc.%d.raw.len +The auto-detected value length in bytes (read-only). +.It Va dev.asmc.%d.raw.type +The 4-character SMC type string (e.g.,\& +.Dq ui8 , +.Dq flt ) +(read-only). +.El +.Pp +Example usage: +.Bd -literal -offset indent +sysctl dev.asmc.0.raw.key=AUPO +sysctl dev.asmc.0.raw.value +sysctl dev.asmc.0.raw.value=01 +.Ed .Sh SUDDEN MOTION SENSOR The Sudden Motion Sensor (SMS for short) is a device that detects laptop movement and notifies the operating system via an interrupt. diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c index 4a6734e22786..044fd7e85057 100644 --- a/sys/dev/asmc/asmc.c +++ b/sys/dev/asmc/asmc.c @@ -123,6 +123,15 @@ static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS); static int asmc_mbp_sysctl_light_left_10byte(SYSCTL_HANDLER_ARGS); static int asmc_wol_sysctl(SYSCTL_HANDLER_ARGS); +#ifdef ASMC_DEBUG +/* Raw key access */ +static int asmc_key_getinfo(device_t, const char *, uint8_t *, char *); +static int asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS); +static int asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS); +#endif + struct asmc_model { const char *smc_model; /* smbios.system.product env var. */ const char *smc_desc; /* driver description */ @@ -828,6 +837,43 @@ asmc_attach(device_t dev) } } +#ifdef ASMC_DEBUG + /* + * Raw SMC key access for debugging. + */ + sc->sc_raw_tree = SYSCTL_ADD_NODE(sysctlctx, + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "raw", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Raw SMC key access"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "key", + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_key_sysctl, "A", + "SMC key name (4 chars)"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "value", + CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_value_sysctl, "A", + "SMC key value (hex string)"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "len", + CTLTYPE_U8 | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_len_sysctl, "CU", + "SMC key value length"); + + SYSCTL_ADD_PROC(sysctlctx, + SYSCTL_CHILDREN(sc->sc_raw_tree), + OID_AUTO, "type", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + dev, 0, asmc_raw_type_sysctl, "A", + "SMC key type (4 chars)"); +#endif + if (model->smc_sms_x == NULL) goto nosms; @@ -1202,10 +1248,10 @@ static int asmc_key_dump(device_t dev, int number) { struct asmc_softc *sc = device_get_softc(dev); - char key[5] = { 0 }; - char type[7] = { 0 }; + char key[ASMC_KEYLEN + 1] = { 0 }; + char type[ASMC_KEYINFO_RESPLEN + 1] = { 0 }; uint8_t index[4]; - uint8_t v[32]; + uint8_t v[ASMC_MAXVAL]; uint8_t maxlen; int i, error = 1, try = 0; @@ -1214,40 +1260,40 @@ asmc_key_dump(device_t dev, int number) index[0] = (number >> 24) & 0xff; index[1] = (number >> 16) & 0xff; index[2] = (number >> 8) & 0xff; - index[3] = (number) & 0xff; + index[3] = number & 0xff; begin: - if (asmc_command(dev, 0x12)) + if (asmc_command(dev, ASMC_CMDGETBYINDEX)) goto out; - for (i = 0; i < 4; i++) { + for (i = 0; i < ASMC_KEYLEN; i++) { ASMC_DATAPORT_WRITE(sc, index[i]); - if (asmc_wait(dev, 0x04)) + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) goto out; } - ASMC_DATAPORT_WRITE(sc, 4); + ASMC_DATAPORT_WRITE(sc, ASMC_KEYLEN); - for (i = 0; i < 4; i++) { - if (asmc_wait(dev, 0x05)) + for (i = 0; i < ASMC_KEYLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) goto out; key[i] = ASMC_DATAPORT_READ(sc); } - /* get type */ - if (asmc_command(dev, 0x13)) + /* Get key info (length + type). */ + if (asmc_command(dev, ASMC_CMDGETINFO)) goto out; - for (i = 0; i < 4; i++) { + for (i = 0; i < ASMC_KEYLEN; i++) { ASMC_DATAPORT_WRITE(sc, key[i]); - if (asmc_wait(dev, 0x04)) + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) goto out; } - ASMC_DATAPORT_WRITE(sc, 6); + ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN); - for (i = 0; i < 6; i++) { - if (asmc_wait(dev, 0x05)) + for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) goto out; type[i] = ASMC_DATAPORT_READ(sc); } @@ -1255,41 +1301,188 @@ begin: error = 0; out: if (error) { - if (++try < 10) + if (++try < ASMC_MAXRETRIES) goto begin; - device_printf(dev, "%s for key %s failed %d times, giving up\n", - __func__, key, try); - mtx_unlock_spin(&sc->sc_mtx); - } else { - char buf[1024]; - char buf2[8]; - mtx_unlock_spin(&sc->sc_mtx); - maxlen = type[0]; - type[0] = ' '; - type[5] = 0; - if (maxlen > sizeof(v)) { - device_printf(dev, - "WARNING: cropping maxlen from %d to %zu\n", maxlen, - sizeof(v)); - maxlen = sizeof(v); - } - for (i = 0; i < sizeof(v); i++) { - v[i] = 0; - } - asmc_key_read(dev, key, v, maxlen); - snprintf(buf, sizeof(buf), - "key %d is: %s, type %s (len %d), data", - number, key, type, maxlen); - for (i = 0; i < maxlen; i++) { - snprintf(buf2, sizeof(buf2), " %02x", v[i]); - strlcat(buf, buf2, sizeof(buf)); - } - strlcat(buf, " \n", sizeof(buf)); - device_printf(dev, "%s", buf); + device_printf(dev, + "%s for key %d failed %d times, giving up\n", + __func__, number, try); + } + mtx_unlock_spin(&sc->sc_mtx); + + if (error) + return (error); + + maxlen = type[0]; + type[0] = ' '; + type[5] = '\0'; + if (maxlen > sizeof(v)) + maxlen = sizeof(v); + + memset(v, 0, sizeof(v)); + error = asmc_key_read(dev, key, v, maxlen); + if (error) + return (error); + + device_printf(dev, "key %d: %s, type%s (len %d), data", + number, key, type, maxlen); + for (i = 0; i < maxlen; i++) + printf(" %02x", v[i]); + printf("\n"); + + return (0); +} + +/* + * Get key info (length and type) from SMC using command 0x13. + * Returns 0 on success, -1 on failure. + * If len is non-NULL, stores the key's value length. + * If type is non-NULL, stores the 4-char type string (must be at least 5 bytes). + */ +static int +asmc_key_getinfo(device_t dev, const char *key, uint8_t *len, char *type) +{ + struct asmc_softc *sc = device_get_softc(dev); + uint8_t info[ASMC_KEYINFO_RESPLEN]; + int i, error = -1, try = 0; + + mtx_lock_spin(&sc->sc_mtx); + +begin: + if (asmc_command(dev, ASMC_CMDGETINFO)) + goto out; + + for (i = 0; i < ASMC_KEYLEN; i++) { + ASMC_DATAPORT_WRITE(sc, key[i]); + if (asmc_wait(dev, ASMC_STATUS_AWAIT_DATA)) + goto out; } + ASMC_DATAPORT_WRITE(sc, ASMC_KEYINFO_RESPLEN); + + for (i = 0; i < ASMC_KEYINFO_RESPLEN; i++) { + if (asmc_wait(dev, ASMC_STATUS_DATA_READY)) + goto out; + info[i] = ASMC_DATAPORT_READ(sc); + } + + error = 0; +out: + if (error && ++try < ASMC_MAXRETRIES) + goto begin; + mtx_unlock_spin(&sc->sc_mtx); + + if (error == 0) { + if (len != NULL) + *len = info[0]; + if (type != NULL) { + for (i = 0; i < ASMC_TYPELEN; i++) + type[i] = info[i + 1]; + type[ASMC_TYPELEN] = '\0'; + } + } return (error); } + +/* + * Raw SMC key access sysctls - enables reading/writing any SMC key by name + * Usage: + * sysctl dev.asmc.0.raw.key=AUPO # Set key, auto-detects length + * sysctl dev.asmc.0.raw.value # Read current value (hex bytes) + * sysctl dev.asmc.0.raw.value=01 # Write new value + */ +static int +asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + char newkey[ASMC_KEYLEN + 1]; + uint8_t keylen; + int error; + + strlcpy(newkey, sc->sc_rawkey, sizeof(newkey)); + error = sysctl_handle_string(oidp, newkey, sizeof(newkey), req); + if (error || req->newptr == NULL) + return (error); + + if (strlen(newkey) != ASMC_KEYLEN) + return (EINVAL); + + /* Get key info to auto-detect length and type */ + if (asmc_key_getinfo(dev, newkey, &keylen, sc->sc_rawtype) != 0) + return (ENOENT); + + if (keylen > ASMC_MAXVAL) + keylen = ASMC_MAXVAL; + + strlcpy(sc->sc_rawkey, newkey, sizeof(sc->sc_rawkey)); + sc->sc_rawlen = keylen; + memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval)); + + /* Read the key value */ + asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen); + + return (0); +} + +static int +asmc_raw_value_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + char hexbuf[ASMC_MAXVAL * 2 + 1]; + int error, i; + + /* Refresh from SMC if a key has been selected. */ + if (sc->sc_rawkey[0] != '\0') { + asmc_key_read(dev, sc->sc_rawkey, sc->sc_rawval, + sc->sc_rawlen > 0 ? sc->sc_rawlen : ASMC_MAXVAL); + } + + /* Format as hex string */ + for (i = 0; i < sc->sc_rawlen && i < ASMC_MAXVAL; i++) + snprintf(hexbuf + i * 2, 3, "%02x", sc->sc_rawval[i]); + hexbuf[i * 2] = '\0'; + + error = sysctl_handle_string(oidp, hexbuf, sizeof(hexbuf), req); + if (error || req->newptr == NULL) + return (error); + + /* Reject writes until a key is selected via raw.key. */ + if (sc->sc_rawkey[0] == '\0') + return (EINVAL); + + memset(sc->sc_rawval, 0, sizeof(sc->sc_rawval)); + for (i = 0; i < sc->sc_rawlen && hexbuf[i*2] && hexbuf[i*2+1]; i++) { + unsigned int val; + char tmp[3] = { hexbuf[i*2], hexbuf[i*2+1], 0 }; + if (sscanf(tmp, "%02x", &val) == 1) + sc->sc_rawval[i] = (uint8_t)val; + } + + if (asmc_key_write(dev, sc->sc_rawkey, sc->sc_rawval, sc->sc_rawlen) != 0) + return (EIO); + + return (0); +} + +static int +asmc_raw_len_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + + return (sysctl_handle_8(oidp, &sc->sc_rawlen, 0, req)); +} + +static int +asmc_raw_type_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = (device_t) arg1; + struct asmc_softc *sc = device_get_softc(dev); + + return (sysctl_handle_string(oidp, sc->sc_rawtype, + sizeof(sc->sc_rawtype), req)); +} #endif static int diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h index cfc176559ed9..0e8d4e9d4a36 100644 --- a/sys/dev/asmc/asmcvar.h +++ b/sys/dev/asmc/asmcvar.h @@ -28,6 +28,9 @@ */ #define ASMC_MAXFANS 6 +#define ASMC_MAXVAL 32 /* Maximum SMC value size */ +#define ASMC_KEYLEN 4 /* SMC key name length */ +#define ASMC_TYPELEN 4 /* SMC type string length */ struct asmc_softc { device_t sc_dev; @@ -53,6 +56,14 @@ struct asmc_softc { uint8_t sc_sms_intr_works; struct cdev *sc_kbd_bkl; uint32_t sc_kbd_bkl_level; +#ifdef ASMC_DEBUG + /* Raw key access */ + struct sysctl_oid *sc_raw_tree; + char sc_rawkey[ASMC_KEYLEN + 1]; + uint8_t sc_rawval[ASMC_MAXVAL]; + uint8_t sc_rawlen; + char sc_rawtype[ASMC_TYPELEN + 1]; +#endif }; /* @@ -71,6 +82,14 @@ struct asmc_softc { bus_write_1(sc->sc_ioport, 0x04, val) #define ASMC_CMDREAD 0x10 #define ASMC_CMDWRITE 0x11 +#define ASMC_CMDGETBYINDEX 0x12 +#define ASMC_CMDGETINFO 0x13 + +#define ASMC_STATUS_AWAIT_DATA 0x04 +#define ASMC_STATUS_DATA_READY 0x05 + +#define ASMC_KEYINFO_RESPLEN 6 /* getinfo: 1 len + 4 type + 1 attr */ +#define ASMC_MAXRETRIES 10 /* * Interrupt port.