From: Bobby Johnson Subject: Apple firmware change battery capacity To: tech@openbsd.org Date: Wed, 4 Nov 2026 21:33:36 -0700 After updating MacOS on my M1 macbook air, not to MacOS27, it was a minor update for Macos 15, my battery sensor became erratic. apm reports wild numbers, often above 100%. Found the behavior and cause documented here - https://github.com/torvalds/linux/commit/f7b253a6e217f71d754d70c525e9b4c1dcbd4414. Apple changed the byte order for the remaining battery capacity sensor(B0RM). The diff below checks for a teltale sign of the new firmware and reverses the order in that case. This fixed the apm output on my laptop. Claude helped. Index: sys/arch/arm64/dev/aplsmc.c =================================================================== RCS file: /cvs/src/sys/arch/arm64/dev/aplsmc.c,v diff -u -p -u -p -r1.34 aplsmc.c --- sys/arch/arm64/dev/aplsmc.c 13 Oct 2026 12:30:12 -0000 1.34 +++ sys/arch/arm64/dev/aplsmc.c 5 Nov 2026 00:30:04 -0000 @@ -114,6 +114,7 @@ struct aplsmc_sensor { #define APLSMC_BE (1 << 0) #define APLSMC_HIDDEN (1 << 1) +#define APLSMC_BE_OLDFW (1 << 2) /* BE on older firmware only */ #define APLSMC_MAX_SENSORS 19 @@ -143,6 +144,7 @@ struct aplsmc_softc { struct ksensordev sc_sensordev; int sc_nbatteries; uint32_t sc_suspend_pstr; + int sc_newfw; /* 1-byte BCF0, LE B0RM */ }; #define CH0I_DISCHARGE (1 << 0) @@ -156,7 +158,7 @@ struct aplsmc_softc *aplsmc_sc; struct aplsmc_sensor aplsmc_sensors[] = { { "ACDI", "ui16", SENSOR_INDICATOR, 1, "power supply" }, { "B0RM", "ui16", SENSOR_AMPHOUR, 1000, "remaining battery capacity", - APLSMC_BE }, + APLSMC_BE_OLDFW }, { "B0FC", "ui16", SENSOR_AMPHOUR, 1000, "last full battery capacity" }, { "B0DC", "ui16", SENSOR_AMPHOUR, 1000, "battery design capacity" }, { "B0AV", "ui16", SENSOR_VOLTS_DC, 1000, "battery" }, @@ -198,6 +200,8 @@ int aplsmc_send_cmd(struct aplsmc_softc int aplsmc_wait_cmd(struct aplsmc_softc *sc); int aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t); int aplsmc_write_key(struct aplsmc_softc *, uint32_t, void *, size_t); +int aplsmc_get_key_info(struct aplsmc_softc *, uint32_t, + struct smc_key_info *); int64_t aplsmc_convert_flt(uint32_t, int); void aplsmc_refresh_sensors(void *); int aplsmc_apminfo(struct apm_power_info *); @@ -231,6 +235,7 @@ aplsmc_attach(struct device *parent, str uint8_t ntap; int error, node; #ifndef SMALL_KERNEL + struct smc_key_info info; int i; #endif @@ -322,18 +327,20 @@ aplsmc_attach(struct device *parent, str #ifndef SMALL_KERNEL - for (i = 0; i < nitems(aplsmc_sensors); i++) { - struct smc_key_info info; + /* + * Newer SMC firmware changed the BCF0 key from 4 bytes to 1 + * byte and at the same time switched B0RM from big-endian to + * little-endian. Use the size of BCF0 to tell them apart. + */ + if (aplsmc_get_key_info(sc, SMC_KEY("BCF0"), &info) == 0 && + info.size == 1) + sc->sc_newfw = 1; - aplsmc_send_cmd(sc, SMC_GET_KEY_INFO, - SMC_KEY(aplsmc_sensors[i].key), 0); - error = aplsmc_wait_cmd(sc); - if (error || SMC_ERROR(sc->sc_data) != SMC_OK) + for (i = 0; i < nitems(aplsmc_sensors); i++) { + if (aplsmc_get_key_info(sc, SMC_KEY(aplsmc_sensors[i].key), + &info) != 0) continue; - bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0, - (uint8_t *)&info, sizeof(info)); - /* Skip if the key type doesn't match. */ if (memcmp(aplsmc_sensors[i].key_type, info.type, sizeof(info.type)) != 0) @@ -581,6 +588,24 @@ aplsmc_write_key(struct aplsmc_softc *sc #ifndef SMALL_KERNEL +int +aplsmc_get_key_info(struct aplsmc_softc *sc, uint32_t key, + struct smc_key_info *info) +{ + int error; + + aplsmc_send_cmd(sc, SMC_GET_KEY_INFO, key, 0); + error = aplsmc_wait_cmd(sc); + if (error) + return error; + if (SMC_ERROR(sc->sc_data) != SMC_OK) + return EINVAL; + + bus_space_read_region_1(sc->sc_iot, sc->sc_sram_ioh, 0, + (uint8_t *)info, sizeof(*info)); + return 0; +} + int64_t aplsmc_convert_flt(uint32_t flt, int scale) { @@ -624,7 +649,9 @@ aplsmc_refresh_sensors(void *arg) uint16_t ui16; error = aplsmc_read_key(sc, key, &ui16, sizeof(ui16)); - if (sensor->flags & APLSMC_BE) + if ((sensor->flags & APLSMC_BE) || + ((sensor->flags & APLSMC_BE_OLDFW) && + !sc->sc_newfw)) ui16 = betoh16(ui16); value = (int64_t)ui16 * sensor->scale; } else if (strcmp(sensor->key_type, "flt ") == 0) {