Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 01 Jul 2026 00:18:46 +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 <freebsd@seuros.com>
Subject:   git: 6a1bd5212f29 - main - asmc: add system state and board identity sysctls
Message-ID:  <6a445ce6.22e46.16a85379@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=6a1bd5212f290933d1429a6d4787394ee53e3181

commit 6a1bd5212f290933d1429a6d4787394ee53e3181
Author:     Abdelkader Boudih <freebsd@seuros.com>
AuthorDate: 2026-07-01 00:15:24 +0000
Commit:     Adrian Chadd <adrian@FreeBSD.org>
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 },
+};


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a445ce6.22e46.16a85379>