The branch main has been updated by adrian:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=6a1bd5212f290933d1429a6d4787394ee53e3181

commit 6a1bd5212f290933d1429a6d4787394ee53e3181
Author:     Abdelkader Boudih <[email protected]>
AuthorDate: 2026-07-01 00:15:24 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-07-01 00:18:40 +0000

    asmc: add system state and board identity sysctls
    
    Add dev.asmc.0.system subtree with read-only sysctls for SMC diagnostic
    and identity keys: shutdown_cause (MSSD), sleep_cause (MSSP),
    thermal_status (MSAL), time_of_day (CLKT), power_state (MSPS),
    board_id (RPlt), and chip_gen (RGEN).
    
    Each sysctl is registered only if the key exists on the hardware.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D57853
---
 sys/dev/asmc/asmc.c    | 220 +++++++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/asmc/asmcvar.h |  86 +++++++++++++++++++
 2 files changed, 306 insertions(+)

diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c
index 1796867786e5..6513cfed7b4f 100644
--- a/sys/dev/asmc/asmc.c
+++ b/sys/dev/asmc/asmc.c
@@ -129,6 +129,15 @@ static int asmc_aupo_sysctl(SYSCTL_HANDLER_ARGS);
 
 static int     asmc_key_getinfo(device_t, const char *, uint8_t *, char *);
 
+/* System state / board identity sysctls */
+static int     asmc_mssd_sysctl(SYSCTL_HANDLER_ARGS);
+static int     asmc_mssp_sysctl(SYSCTL_HANDLER_ARGS);
+static int     asmc_msal_sysctl(SYSCTL_HANDLER_ARGS);
+static int     asmc_clkt_sysctl(SYSCTL_HANDLER_ARGS);
+static int     asmc_msps_sysctl(SYSCTL_HANDLER_ARGS);
+static int     asmc_rplt_sysctl(SYSCTL_HANDLER_ARGS);
+static int     asmc_rgen_sysctl(SYSCTL_HANDLER_ARGS);
+
 #ifdef ASMC_DEBUG
 /* Raw key access */
 static int     asmc_raw_key_sysctl(SYSCTL_HANDLER_ARGS);
@@ -659,6 +668,72 @@ asmc_attach(device_t dev)
                    "Battery charge limit (0-100)");
        }
 
+       /* System state / board identity subtree. */
+       {
+               struct sysctl_oid *sys_tree;
+               uint8_t msps_len;
+
+               sys_tree = SYSCTL_ADD_NODE(sysctlctx,
+                   SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+                   "system", CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+                   "System state and board identity");
+               if (sys_tree == NULL) {
+                       device_printf(dev,
+                           "failed to create system sysctl node\n");
+                       goto nosms;
+               }
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_MSSD, NULL, NULL) == 0)
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, 
"shutdown_cause",
+                           CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_mssd_sysctl, "A",
+                           "Last shutdown cause (MSSD)");
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_MSSP, NULL, NULL) == 0)
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, "sleep_cause",
+                           CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_mssp_sysctl, "A",
+                           "Last sleep cause (MSSP)");
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_MSAL, NULL, NULL) == 0)
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, 
"thermal_status",
+                           CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_msal_sysctl, "A",
+                           "Thermal subsystem status flags (MSAL)");
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_CLKT, NULL, NULL) == 0)
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, "time_of_day",
+                           CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_clkt_sysctl, "IU",
+                           "Seconds since midnight per SMC clock (CLKT)");
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_MSPS, &msps_len, NULL) == 0 
&&
+                   (msps_len == 1 || msps_len == 2))
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, "power_state",
+                           CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_msps_sysctl, "IU",
+                           "SMC power state index (MSPS)");
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_RPLT, NULL, NULL) == 0)
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, "board_id",
+                           CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_rplt_sysctl, "A",
+                           "Apple internal board codename (RPlt)");
+
+               if (asmc_key_getinfo(dev, ASMC_KEY_RGEN, NULL, NULL) == 0)
+                       SYSCTL_ADD_PROC(sysctlctx,
+                           SYSCTL_CHILDREN(sys_tree), OID_AUTO, "chip_gen",
+                           CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE,
+                           dev, 0, asmc_rgen_sysctl, "IU",
+                           "Apple security chip generation (RGEN; 3=T2)");
+       }
+
        if (!sc->sc_has_sms)
                goto nosms;
 
@@ -2540,3 +2615,148 @@ asmc_backlight_get_info(device_t dev, struct 
backlight_info *info)
 
        return (0);
 }
+
+static const char *
+asmc_cause_str(int8_t cause, bool is_sleep)
+{
+       size_t i;
+
+       for (i = 0; i < nitems(asmc_cause_table); i++) {
+               if (asmc_cause_table[i].code != cause)
+                       continue;
+               if (is_sleep && asmc_cause_table[i].sleep_desc != NULL)
+                       return (asmc_cause_table[i].sleep_desc);
+               return (asmc_cause_table[i].desc);
+       }
+       return (NULL);
+}
+
+static int
+asmc_mssd_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       int8_t cause;
+       const char *desc;
+       char buf[ASMC_CAUSE_BUFLEN];
+
+       /* EIO: SMC I/O bus did not respond to key read. */
+       if (asmc_key_read(dev, ASMC_KEY_MSSD, (uint8_t *)&cause, 1) != 0)
+               return (EIO);
+
+       desc = asmc_cause_str(cause, false);
+       if (desc != NULL)
+               snprintf(buf, sizeof(buf), "%d (%s)", (int)cause, desc);
+       else
+               snprintf(buf, sizeof(buf), "%d", (int)cause);
+
+       return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+asmc_mssp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       int8_t cause;
+       const char *desc;
+       char buf[ASMC_CAUSE_BUFLEN];
+
+       /* EIO: SMC I/O bus did not respond to key read. */
+       if (asmc_key_read(dev, ASMC_KEY_MSSP, (uint8_t *)&cause, 1) != 0)
+               return (EIO);
+
+       desc = asmc_cause_str(cause, true);
+       if (desc != NULL)
+               snprintf(buf, sizeof(buf), "%d (%s)", (int)cause, desc);
+       else
+               snprintf(buf, sizeof(buf), "%d", (int)cause);
+
+       return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+asmc_msal_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       uint8_t msal;
+       char buf[80];
+
+       /* EIO: SMC I/O bus did not respond to key read. */
+       if (asmc_key_read(dev, ASMC_KEY_MSAL, &msal, 1) != 0)
+               return (EIO);
+
+       snprintf(buf, sizeof(buf),
+           "0x%02x (tss=%d therm_valid=%d calib_valid=%d prochot=%d 
plimits=%d)",
+           msal,
+           (msal & ASMC_MSAL_TSS) != 0,
+           (msal & ASMC_MSAL_THERM_VALID) != 0,
+           (msal & ASMC_MSAL_CALIB_VALID) != 0,
+           (msal & ASMC_MSAL_PROCHOT) != 0,
+           (msal & ASMC_MSAL_PLIMITS) != 0);
+
+       return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+asmc_clkt_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       uint8_t buf[4];
+       uint32_t secs;
+
+       if (asmc_key_read(dev, ASMC_KEY_CLKT, buf, 4) != 0)
+               return (EIO);
+
+       secs = be32dec(buf);
+       return (sysctl_handle_32(oidp, &secs, 0, req));
+}
+
+static int
+asmc_msps_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       uint8_t buf[2], len;
+       uint32_t state;
+
+       if (asmc_key_getinfo(dev, ASMC_KEY_MSPS, &len, NULL) != 0)
+               return (EIO);
+       if (len != 1 && len != 2)
+               return (EIO);
+
+       memset(buf, 0, sizeof(buf));
+       if (asmc_key_read(dev, ASMC_KEY_MSPS, buf, len) != 0)
+               return (EIO);
+
+       state = (len == 1) ? buf[0] : be16dec(buf);
+       return (sysctl_handle_32(oidp, &state, 0, req));
+}
+
+static int
+asmc_rplt_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       uint8_t buf[ASMC_RPLT_MAXLEN + 1];
+       char name[ASMC_RPLT_MAXLEN + 1];
+
+       memset(buf, 0, sizeof(buf));
+       if (asmc_key_read(dev, ASMC_KEY_RPLT, buf, ASMC_RPLT_MAXLEN) != 0)
+               return (EIO);
+
+       memcpy(name, buf, ASMC_RPLT_MAXLEN);
+       name[ASMC_RPLT_MAXLEN] = '\0';
+
+       return (sysctl_handle_string(oidp, name, sizeof(name), req));
+}
+
+static int
+asmc_rgen_sysctl(SYSCTL_HANDLER_ARGS)
+{
+       device_t dev = (device_t)arg1;
+       uint8_t gen;
+       uint32_t val;
+
+       if (asmc_key_read(dev, ASMC_KEY_RGEN, &gen, 1) != 0)
+               return (EIO);
+
+       val = gen;
+       return (sysctl_handle_32(oidp, &val, 0, req));
+}
diff --git a/sys/dev/asmc/asmcvar.h b/sys/dev/asmc/asmcvar.h
index 70485c32ea88..940119e59774 100644
--- a/sys/dev/asmc/asmcvar.h
+++ b/sys/dev/asmc/asmcvar.h
@@ -190,3 +190,89 @@ struct asmc_softc {
  * Interrupt keys.
  */
 #define ASMC_KEY_INTOK         "NTOK"  /* WO; 1 byte */
+
+/*
+ * System State Machine / diagnostics keys.
+ *
+ * MSSD - Mac System Shutdown cause (last shutdown reason code)
+ * MSSP - Mac System SleeP cause (last sleep reason code)
+ * MSAL - Mac System ALarm (thermal subsystem status bitmap)
+ * MSPS - Mac System Power State (current power state index)
+ * CLKT - CLocK Time (seconds since midnight, SMC RTC)
+ * MSTS - Mac System sTate/Status (SSM state; 0 = S0 awake)
+ */
+#define ASMC_KEY_CLKT          "CLKT"  /* RO; 4 bytes ui32 BE */
+#define ASMC_KEY_MSSD          "MSSD"  /* RO; 1 byte si8 */
+#define ASMC_KEY_MSSP          "MSSP"  /* RO; 1 byte si8 */
+#define ASMC_KEY_MSAL          "MSAL"  /* RO; 1 byte hex_ */
+#define ASMC_KEY_MSPS          "MSPS"  /* RO; 1 or 2 bytes hex_ */
+#define ASMC_KEY_MSTS          "MSTS"  /* RO; 1 byte ui8 */
+
+#define ASMC_MSAL_TSS          0x01    /* TSS running */
+#define ASMC_MSAL_THERM_VALID  0x02    /* thermal calibration valid */
+#define ASMC_MSAL_CALIB_VALID  0x08    /* I/P calibration valid */
+#define ASMC_MSAL_PROCHOT      0x40    /* Prochot enabled */
+#define ASMC_MSAL_PLIMITS      0x80    /* plimits enabled */
+
+/*
+ * Board identity keys.
+ *
+ * RPlt - board platform identifier (e.g. "Mac-XXXX")
+ * RGEN - security chip generation (3 = T2)
+ */
+#define ASMC_KEY_RPLT          "RPlt"  /* RO; 8 bytes ch8* */
+#define ASMC_KEY_RGEN          "RGEN"  /* RO; 1 byte ui8 */
+#define ASMC_RPLT_MAXLEN       8       /* RPlt key data length */
+
+/*
+ * Shutdown/sleep cause codes (MSSD/MSSP values).
+ *
+ * Positive values indicate normal operation; negative values indicate
+ * fault conditions.
+ * Sources: Apple SMC firmware, VirtualSMC docs, macOS
+ * pmset/powermetrics output.
+ */
+struct asmc_cause {
+       int8_t          code;
+       const char      *desc;          /* shutdown description */
+       const char      *sleep_desc;    /* sleep description, or NULL if same */
+};
+
+#define ASMC_CAUSE_BUFLEN      48      /* "-128 
(temperature-overlimit-timeout)\0" */
+
+static const struct asmc_cause asmc_cause_table[] = {
+       {    5, "good-shutdown",                "good-sleep" },
+       {    3, "power-button",                 NULL },
+       {    2, "low-battery-sleep",            NULL },
+       {    1, "overtemp-sleep",               NULL },
+       {    0, "initial",                      NULL },
+       {   -1, "health-check",                 NULL },
+       {   -2, "power-supply",                 NULL },
+       {   -3, "temperature-multisleep",       NULL },
+       {   -4, "sensor-fan",                   NULL },
+       {  -30, "temperature-overlimit-timeout",NULL },
+       {  -40, "pswr-smrst",                   NULL }, /* power supply 
watchdog reset */
+       {  -50, "unmapped",                     NULL },
+       {  -60, "low-battery",                  NULL },
+       {  -61, "ninja-shutdown",               NULL }, /* sudden unexpected 
power loss */
+       {  -62, "ninja-restart",                NULL },
+       {  -70, "palm-rest-temperature",        NULL },
+       {  -71, "sodimm-temperature",           NULL },
+       {  -72, "heatpipe-temperature",         NULL },
+       {  -74, "battery-temperature",          NULL },
+       {  -75, "adapter-timeout",              NULL },
+       {  -77, "manual-temperature",           NULL },
+       {  -78, "adapter-current",              NULL },
+       {  -79, "battery-current",              NULL },
+       {  -82, "skin-temperature",             NULL },
+       {  -83, "skin-temperature-sensors",     NULL },
+       {  -84, "backup-temperature",           NULL },
+       {  -86, "cpu-proximity-temperature",    NULL },
+       {  -95, "cpu-temperature",              NULL },
+       { -100, "power-supply-temperature",     NULL },
+       { -101, "lcd-temperature",              NULL },
+       { -102, "rsm-power-fail",               NULL },
+       { -103, "battery-cuv",                  NULL }, /* cell under-voltage */
+       { -127, "pmu-forced-shutdown",          NULL },
+       { -128, "unknown",                      NULL },
+};

Reply via email to