Date: Wed, 15 Apr 2026 04:28:05 +0000 From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Abdelkader Boudih <chaos@seuros.com> Subject: git: 3e27114a7f96 - main - asmc: add raw SMC key read/write interface Message-ID: <69df13d5.3084c.15512e6d@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=3e27114a7f961aac49d75a663a55332375d0bef3 commit 3e27114a7f961aac49d75a663a55332375d0bef3 Author: Abdelkader Boudih <chaos@seuros.com> AuthorDate: 2026-04-15 04:20:52 +0000 Commit: Adrian Chadd <adrian@FreeBSD.org> CommitDate: 2026-04-15 04:21:46 +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 --- 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 9e3550661797..9b42d021e1aa 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.home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69df13d5.3084c.15512e6d>
