From 326fed62c2bf807716c6cedc6b85a3b0a4148614 Mon Sep 17 00:00:00 2001 From: Dj <43033058+DjDeveloperr@users.noreply.github.com> Date: Mon, 26 Oct 2026 02:34:18 -0400 Subject: [PATCH] mfd: macsmc: Support T6050 power and pcIO consumers Add the T6050 SMC consumer profile, described pcIO GPIO outputs, power-supply identity sizing, and charging policy. Preserve the newer shared SRAM accessor and metadata serialization. Source snapshot: t6050-gpu ee68abb105200e99636a5edfc647a5cfc1d89954 Integration base: 9cbdd0734d9e186c5f5f5949a21b909d10c2571b Signed-off-by: Dj <43033058+DjDeveloperr@users.noreply.github.com> --- .../bindings/gpio/apple,smc-pcio-gpio.yaml | 40 ++++++++++ .../devicetree/bindings/mfd/apple,smc.yaml | 7 +- drivers/gpio/gpio-macsmc.c | 73 ++++++++++++++++++- drivers/mfd/macsmc.c | 35 +++++++-- drivers/power/supply/macsmc-power.c | 68 ++++++++++++----- .../aurora-apif/smc_charge_policy.py | 40 ++++++++++ .../selftests/aurora-apif/smc_identity.py | 40 ++++++++++ .../selftests/aurora-apif/smc_metadata.py | 62 ++++++++++++++++ 8 files changed, 339 insertions(+), 26 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/apple,smc-pcio-gpio.yaml create mode 100755 tools/testing/selftests/aurora-apif/smc_charge_policy.py create mode 100644 tools/testing/selftests/aurora-apif/smc_identity.py create mode 100755 tools/testing/selftests/aurora-apif/smc_metadata.py diff --git a/Documentation/devicetree/bindings/gpio/apple,smc-pcio-gpio.yaml b/Documentation/devicetree/bindings/gpio/apple,smc-pcio-gpio.yaml new file mode 100644 index 0000000000000..94d9672189fc6 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/apple,smc-pcio-gpio.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/apple,smc-pcio-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SMC pcIO output GPIOs + +maintainers: + - Dj + +description: | + The writable eight-byte pcIO SMC key controls outputs through board + command templates. Each template has its low 16 bits clear; the driver + inserts a boolean output level and sends the little-endian command. + Input reads and mode changes are not supported by this interface. + +properties: + compatible: + const: apple,smc-pcio-gpio + + gpio-controller: true + + '#gpio-cells': + const: 2 + + apple,output-commands: + $ref: /schemas/types.yaml#/definitions/uint64-array + minItems: 1 + maxItems: 64 + uniqueItems: true + description: Unique command templates with low 16 bits clear, in GPIO order + +required: + - compatible + - gpio-controller + - '#gpio-cells' + - apple,output-commands + +additionalProperties: false diff --git a/Documentation/devicetree/bindings/mfd/apple,smc.yaml b/Documentation/devicetree/bindings/mfd/apple,smc.yaml index 89ecdb0f47067..8e32ec816ee07 100644 --- a/Documentation/devicetree/bindings/mfd/apple,smc.yaml +++ b/Documentation/devicetree/bindings/mfd/apple,smc.yaml @@ -1,4 +1,4 @@ -# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) AND GPL-2.0-only %YAML 1.2 --- $id: http://devicetree.org/schemas/mfd/apple,smc.yaml# @@ -16,6 +16,7 @@ description: properties: compatible: oneOf: + - const: apple,t6050-smc - items: - enum: - apple,t6020-smc @@ -47,7 +48,9 @@ properties: maxItems: 1 gpio: - $ref: /schemas/gpio/apple,smc-gpio.yaml + oneOf: + - $ref: /schemas/gpio/apple,smc-gpio.yaml + - $ref: /schemas/gpio/apple,smc-pcio-gpio.yaml reboot: $ref: /schemas/power/reset/apple,smc-reboot.yaml diff --git a/drivers/gpio/gpio-macsmc.c b/drivers/gpio/gpio-macsmc.c index b9500afad3ce4..1e099ede17cfb 100644 --- a/drivers/gpio/gpio-macsmc.c +++ b/drivers/gpio/gpio-macsmc.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) AND GPL-2.0-only /* * Apple SMC GPIO driver * Copyright The Asahi Linux Contributors @@ -15,6 +15,7 @@ #include #include #include +#include #define MAX_GPIO 64 @@ -101,6 +102,7 @@ struct macsmc_gpio { struct apple_smc *smc; struct gpio_chip gc; + u64 *output_commands; int first_index; smc_key base_key; @@ -342,6 +344,72 @@ static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc, return 0; } +/* Each DT template identifies one output; its low 16 bits carry the value. */ +static int macsmc_pcio_set(struct gpio_chip *gc, unsigned int offset, int value) +{ + struct macsmc_gpio *smcgp = gpiochip_get_data(gc); + u8 command[8]; + int ret; + + put_unaligned_le64(smcgp->output_commands[offset] | !!value, command); + ret = apple_smc_write(smcgp->smc, SMC_KEY(pcIO), command, sizeof(command)); + /* SMC writes return status, not a byte count. */ + return ret < 0 ? ret : 0; +} + +static int macsmc_pcio_get(struct gpio_chip *gc, unsigned int offset) +{ + /* The native output command has no established input/readback operation. */ + return -EOPNOTSUPP; +} + +static int macsmc_pcio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int macsmc_pcio_probe(struct macsmc_gpio *smcgp) +{ + struct device *dev = smcgp->dev; + struct apple_smc_key_info info; + int count, ret; + + ret = apple_smc_get_key_info(smcgp->smc, SMC_KEY(pcIO), &info); + if (ret < 0) + return ret; + if (info.size != 8 || !(info.flags & APPLE_SMC_WRITABLE)) + return dev_err_probe(dev, -EINVAL, "pcIO is not an eight-byte output key\n"); + count = of_property_count_u64_elems(dev->of_node, "apple,output-commands"); + if (count <= 0 || count > MAX_GPIO) + return -EINVAL; + smcgp->output_commands = devm_kcalloc(dev, count, sizeof(u64), GFP_KERNEL); + if (!smcgp->output_commands) + return -ENOMEM; + ret = of_property_read_u64_array(dev->of_node, "apple,output-commands", + smcgp->output_commands, count); + if (ret) + return ret; + for (int i = 0; i < count; i++) { + if (smcgp->output_commands[i] & 0xffff) + return -EINVAL; + for (int j = 0; j < i; j++) + if (smcgp->output_commands[i] == smcgp->output_commands[j]) + return -EINVAL; + } + + smcgp->gc.label = "macsmc-pcio-output"; + smcgp->gc.owner = THIS_MODULE; + smcgp->gc.get = macsmc_pcio_get; + smcgp->gc.set = macsmc_pcio_set; + smcgp->gc.direction_output = macsmc_pcio_set; + smcgp->gc.get_direction = macsmc_pcio_get_direction; + smcgp->gc.can_sleep = true; + smcgp->gc.ngpio = count; + smcgp->gc.base = -1; + smcgp->gc.parent = dev; + return devm_gpiochip_add_data(dev, &smcgp->gc, smcgp); +} + struct macsmc_gpio_of_match_data { smc_key base_key; }; @@ -360,6 +428,8 @@ static int macsmc_gpio_probe(struct platform_device *pdev) smcgp->dev = &pdev->dev; smcgp->smc = smc; + if (device_is_compatible(&pdev->dev, "apple,smc-pcio-gpio")) + return macsmc_pcio_probe(smcgp); smcgp->base_key = data ? data->base_key : _SMC_KEY("gP\0\0"); smcgp->j700_pcio = of_machine_is_compatible("apple,j700") && device_is_compatible(&pdev->dev, "apple,smc-gpio"); @@ -405,6 +475,7 @@ static const struct macsmc_gpio_of_match_data macsmc_gpio_low_data = { }; static const struct of_device_id macsmc_gpio_of_table[] = { + { .compatible = "apple,smc-pcio-gpio" }, { .compatible = "apple,smc-gpio", .data = &macsmc_gpio_up_data, diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c index ac9d821bf865c..5138d6d1c22b9 100644 --- a/drivers/mfd/macsmc.c +++ b/drivers/mfd/macsmc.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT +// SPDX-License-Identifier: (GPL-2.0-only OR MIT) AND GPL-2.0-only /* * Apple SMC (System Management Controller) MFD driver * @@ -56,6 +56,16 @@ static const struct mfd_cell apple_smc_devs[] = { MFD_CELL_OF("macsmc-rtc", NULL, NULL, 0, 0, "apple,smc-rtc"), }; +/* This profile exposes notifications and the explicitly described outputs. */ +static const struct mfd_cell apple_smc_t6050_devs[] = { + MFD_CELL_NAME("macsmc-input"), + MFD_CELL_NAME("macsmc-power"), + MFD_CELL_OF("macsmc-hwmon", NULL, NULL, 0, 0, "apple,smc-hwmon"), +}; + +static const struct mfd_cell apple_smc_pcio_dev = + MFD_CELL_OF("macsmc-gpio", NULL, NULL, 0, 0, "apple,smc-pcio-gpio"); + /* The second GPIO bank ('gp' keys) only exists on some SMCs. */ static const struct mfd_cell apple_smc_low_gpio_dev = MFD_CELL_OF("macsmc-low-gpio", NULL, NULL, 0, 0, "apple,smc-low-gpio"); @@ -242,8 +252,8 @@ int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_ u8 key_info[6]; int ret; + /* Keep the shared reply buffer stable until its metadata is copied. */ guard(mutex)(&smc->mutex); - ret = apple_smc_cmd_locked(smc, SMC_MSG_GET_KEY_INFO, key, 0, 0, NULL); if (ret >= 0 && info) { apple_smc_read_sram(smc, key_info, sizeof(key_info)); @@ -529,12 +539,26 @@ static int apple_smc_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE, - apple_smc_devs, ARRAY_SIZE(apple_smc_devs), - NULL, 0, NULL); + if (of_device_is_compatible(dev->of_node, "apple,t6050-smc")) + ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE, + apple_smc_t6050_devs, + ARRAY_SIZE(apple_smc_t6050_devs), NULL, 0, NULL); + else + ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE, + apple_smc_devs, ARRAY_SIZE(apple_smc_devs), + NULL, 0, NULL); if (ret) return dev_err_probe(smc->dev, ret, "Failed to register sub-devices"); + np = of_get_compatible_child(dev->of_node, "apple,smc-pcio-gpio"); + if (np) { + of_node_put(np); + ret = devm_mfd_add_devices(smc->dev, PLATFORM_DEVID_NONE, + &apple_smc_pcio_dev, 1, NULL, 0, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to register pcIO outputs\n"); + } + /* * Register the second GPIO bank only where the device tree describes * it: the MFD core would otherwise create an unbound device and warn @@ -554,6 +578,7 @@ static int apple_smc_probe(struct platform_device *pdev) } static const struct of_device_id apple_smc_of_match[] = { + { .compatible = "apple,t6050-smc" }, { .compatible = "apple,t8103-smc" }, { .compatible = "apple,smc" }, {}, diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c index 36d59e5ea315a..f788b35169dd6 100644 --- a/drivers/power/supply/macsmc-power.c +++ b/drivers/power/supply/macsmc-power.c @@ -979,10 +979,34 @@ static int macsmc_power_event(struct notifier_block *nb, unsigned long event, vo return NOTIFY_DONE; } +static int macsmc_read_identity(struct apple_smc *smc, smc_key key, + char *out, size_t capacity) +{ + struct apple_smc_key_info info; + int ret; + + if (!capacity) + return -EINVAL; + out[0] = '\0'; + ret = apple_smc_get_key_info(smc, key, &info); + if (ret < 0) + return ret; + if (!info.size || info.size >= capacity || !(info.flags & APPLE_SMC_READABLE)) + return -EINVAL; + /* SMC read requests must describe this key, not the destination buffer. */ + ret = apple_smc_read(smc, key, out, info.size); + if (ret != info.size) { + out[0] = '\0'; + return ret < 0 ? ret : -EIO; + } + out[info.size] = '\0'; + return 0; +} + static int macsmc_power_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct apple_smc *smc; struct power_supply_config psy_cfg = {}; struct apple_smc_key_info info; struct macsmc_power *power; @@ -995,6 +1019,9 @@ static int macsmc_power_probe(struct platform_device *pdev) enum power_supply_property *props; size_t nprops; + if (!dev->parent) + return dev_err_probe(dev, -ENODEV, "Missing SMC parent\n"); + smc = dev_get_drvdata(dev->parent); if (!smc) return -ENODEV; @@ -1108,17 +1135,22 @@ static int macsmc_power_probe(struct platform_device *pdev) of_machine_is_compatible("apple,j700"); power->status_from_current = of_machine_is_compatible("apple,j700"); - /* Reset "Optimised Battery Charging" flags to default state */ - if (power->has_chte) - apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); - else if (power->has_ch0c) - apple_smc_write_u8(smc, SMC_KEY(CH0C), 0); - - if (power->has_ch0i) - apple_smc_write_u8(smc, SMC_KEY(CH0I), 0); - - apple_smc_write_u8(smc, SMC_KEY(CH0K), 0); - apple_smc_write_u8(smc, SMC_KEY(CH0B), 0); + /* T6050 telemetry discovery must preserve firmware charge policy. + * Do not reset charging controls merely to publish the battery. + */ + if (!of_device_is_compatible(dev->parent->of_node, "apple,t6050-smc")) { + /* Reset "Optimised Battery Charging" flags to default state. */ + if (power->has_chte) + apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); + else if (power->has_ch0c) + apple_smc_write_u8(smc, SMC_KEY(CH0C), 0); + + if (power->has_ch0i) + apple_smc_write_u8(smc, SMC_KEY(CH0I), 0); + + apple_smc_write_u8(smc, SMC_KEY(CH0K), 0); + apple_smc_write_u8(smc, SMC_KEY(CH0B), 0); + } /* Detect charge limit method (CHWA vs CHLS) */ if (apple_smc_read_flag(power->smc, SMC_KEY(CHWA), &flag) == 0) @@ -1150,12 +1182,12 @@ static int macsmc_power_probe(struct platform_device *pdev) power->batt_desc.num_properties = nprops; /* Fetch identity strings */ - apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, - sizeof(power->model_name) - 1); - apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, - sizeof(power->serial_number) - 1); - apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, - sizeof(power->mfg_date) - 1); + macsmc_read_identity(smc, SMC_KEY(BMDN), power->model_name, + sizeof(power->model_name)); + macsmc_read_identity(smc, SMC_KEY(BMSN), power->serial_number, + sizeof(power->serial_number)); + macsmc_read_identity(smc, SMC_KEY(BMDT), power->mfg_date, + sizeof(power->mfg_date)); apple_smc_read_u8(power->smc, SMC_KEY(BNCB), &power->num_cells); power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells; diff --git a/tools/testing/selftests/aurora-apif/smc_charge_policy.py b/tools/testing/selftests/aurora-apif/smc_charge_policy.py new file mode 100755 index 0000000000000..5261717c720f9 --- /dev/null +++ b/tools/testing/selftests/aurora-apif/smc_charge_policy.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Ensure T6050 battery discovery cannot reset charging policy.""" +from pathlib import Path +import subprocess, tempfile +from common import block + +ROOT=Path(__file__).resolve().parents[4] +source=(ROOT/'drivers/power/supply/macsmc-power.c').read_text() +guarded=block(source,r'^\t\tif \(!of_device_is_compatible\(dev->parent->of_node, "apple,t6050-smc"\)\)') +probe=block(source,r'^static int macsmc_power_probe\(') +assert probe.count('apple_smc_write_')==guarded.count('apple_smc_write_')==5 +program=r''' +#include +#include +#include +#include +struct device { struct device *parent; bool *of_node; }; +struct macsmc_power { bool has_chte,has_ch0c,has_ch0i; }; +static int calls; +static bool of_device_is_compatible(bool *node,const char *name){assert(!strcmp(name,"apple,t6050-smc"));return *node;} +#define SMC_KEY(k) #k +static void apple_smc_write_u32(void *smc,const char *key,uint32_t value){(void)smc;assert(value==0);assert(!strcmp(key,"CHTE"));calls++;} +static void apple_smc_write_u8(void *smc,const char *key,uint8_t value){(void)smc;(void)key;assert(value==0);calls++;} +static void policy(struct device *dev, struct macsmc_power *power){void *smc=0; +''' + guarded + r''' +} +int main(void) { + bool t6050=true;struct device parent={.of_node=&t6050},dev={.parent=&parent}; + struct macsmc_power p={true,true,true};policy(&dev,&p);assert(calls==0); + t6050=false;policy(&dev,&p);assert(calls==4); + calls=0;p.has_chte=false;policy(&dev,&p);assert(calls==4); + calls=0;p.has_ch0c=false;p.has_ch0i=false;policy(&dev,&p);assert(calls==2); +} +''' +with tempfile.TemporaryDirectory(prefix='smc-charge-') as directory: + d=Path(directory);(d/'test.c').write_text(program) + subprocess.run(['cc','-std=gnu11','-Wall','-Wextra','-Werror','-fsanitize=address,undefined',str(d/'test.c'),'-o',str(d/'test')],check=True) + subprocess.run([str(d/'test')],check=True) +print('T6050 probe preserves charge policy; older-platform reset behavior unchanged') diff --git a/tools/testing/selftests/aurora-apif/smc_identity.py b/tools/testing/selftests/aurora-apif/smc_identity.py new file mode 100644 index 0000000000000..822440297e4de --- /dev/null +++ b/tools/testing/selftests/aurora-apif/smc_identity.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Exercise exact-length SMC identity reads from the production helper.""" +from pathlib import Path +import subprocess, tempfile +from common import block +ROOT=Path(__file__).resolve().parents[4] +helper=block((ROOT/'drivers/power/supply/macsmc-power.c').read_text(),r'^static int macsmc_read_identity\(') +program=r''' +#include +#include +#include +#include +#include +typedef uint32_t smc_key; +struct apple_smc {int dummy;}; +struct apple_smc_key_info {uint8_t size,flags;}; +#define APPLE_SMC_READABLE 0x80 +static int calls, metadata_error, reply; +static struct apple_smc_key_info info; +static int apple_smc_get_key_info(struct apple_smc *s,smc_key key,struct apple_smc_key_info *i){(void)s;(void)key;*i=info;return metadata_error;} +static int apple_smc_read(struct apple_smc *s,smc_key key,char *out,size_t n){(void)s;(void)key;calls++;assert(n==info.size);memset(out,'A',n);return reply;} +''' + helper + r''' +int main(void) { + struct apple_smc s={0};char out[8];memset(out,0xcc,sizeof(out)); + info=(struct apple_smc_key_info){3,0x80};reply=3; + assert(!macsmc_read_identity(&s,1,out,sizeof(out)) && calls==1 && !memcmp(out,"AAA\0",4)); + calls=0;info.size=8;assert(macsmc_read_identity(&s,1,out,8)==-EINVAL && !calls && !out[0]); + info.size=0;assert(macsmc_read_identity(&s,1,out,8)==-EINVAL && !calls); + info.size=3;info.flags=0;assert(macsmc_read_identity(&s,1,out,8)==-EINVAL && !calls); + info.flags=0x80;metadata_error=-ENOENT;assert(macsmc_read_identity(&s,1,out,8)==-ENOENT && !calls); + metadata_error=0;reply=2;assert(macsmc_read_identity(&s,1,out,8)==-EIO && calls==1 && !out[0]); + calls=0;assert(macsmc_read_identity(&s,1,out,0)==-EINVAL && !calls); +} +''' +with tempfile.TemporaryDirectory(prefix='smc-identity-') as directory: + d=Path(directory);(d/'test.c').write_text(program) + subprocess.run(['cc','-std=gnu11','-Wall','-Wextra','-Werror','-fsanitize=address,undefined',str(d/'test.c'),'-o',str(d/'test')],check=True) + subprocess.run([str(d/'test')],check=True) +print('SMC identity: exact length, terminator, missing/unreadable/oversized keys and short reply passed') diff --git a/tools/testing/selftests/aurora-apif/smc_metadata.py b/tools/testing/selftests/aurora-apif/smc_metadata.py new file mode 100755 index 0000000000000..07d13cce58f34 --- /dev/null +++ b/tools/testing/selftests/aurora-apif/smc_metadata.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Model a competing shared-buffer reply at the SMC metadata copy boundary.""" +from pathlib import Path +import os, subprocess, tempfile +from common import block + +ROOT=Path(__file__).resolve().parents[4] +function=block((ROOT/'drivers/mfd/macsmc.c').read_text(), r'^int apple_smc_get_key_info\(') +prefix=r''' +#include +#include +#include +#include +#include +typedef uint8_t u8; +typedef uint32_t smc_key; +struct mutex { int held; }; +struct apple_smc { struct mutex mutex; struct { void *iomem; } shmem; }; +struct apple_smc_key_info { u8 size; uint32_t type_code; u8 flags; }; +struct lock_guard { struct mutex *m; }; +static struct lock_guard acquire_guard(struct mutex *m) { assert(!m->held);m->held=1;return (struct lock_guard){m}; } +static void release_guard(struct lock_guard *g) { assert(g->m->held);g->m->held=0; } +#define guard(type) struct lock_guard __attribute__((cleanup(release_guard))) scope_guard=acquire_guard +#define SMC_MSG_GET_KEY_INFO 0x13 +static int response; +static struct apple_smc *current; +static int apple_smc_cmd_locked(struct apple_smc *s,int command,smc_key key,int a,int b,void *out) { + (void)key;(void)a;(void)b;(void)out;assert(command==SMC_MSG_GET_KEY_INFO && s->mutex.held); + const u8 reply[]={2,'u','i','1','6',0x80};memcpy(s->shmem.iomem,reply,6);current=s;return response; +} +static __attribute__((unused)) int apple_smc_cmd(struct apple_smc *s,int c,smc_key k,int a,int b,void *out) { + guard(mutex)(&s->mutex);return apple_smc_cmd_locked(s,c,k,a,b,out); +} +static void apple_smc_read_sram(struct apple_smc *smc,void *out,size_t n) { + void *in=smc->shmem.iomem; + /* The contender can replace the shared reply as soon as the mutex is free. */ + if(!current->mutex.held) memset(in,0xee,n); + memcpy(out,in,n); +} +static uint32_t get_unaligned_be32(const u8 *p) {return ((uint32_t)p[0]<<24)|((uint32_t)p[1]<<16)|(p[2]<<8)|p[3];} +''' +suffix=r''' +int main(void) { + u8 data[6];struct apple_smc s={.shmem.iomem=data};struct apple_smc_key_info i={0}; + response=6;if(apple_smc_get_key_info(&s,1,&i)!=6 || i.size!=2 || i.type_code!=0x75693136 || i.flags!=0x80) return 42; + assert(!s.mutex.held); + response=-EIO;i=(struct apple_smc_key_info){.size=9,.flags=7}; + assert(apple_smc_get_key_info(&s,1,&i)==-EIO && i.size==9 && i.flags==7 && !s.mutex.held); + response=0;assert(!apple_smc_get_key_info(&s,1,NULL) && !s.mutex.held); +} +''' +old=function.replace('\t/* Keep the shared reply buffer stable until its metadata is copied. */\n\tguard(mutex)(&smc->mutex);\n\tret = apple_smc_cmd_locked', '\tret = apple_smc_cmd') +assert old!=function +with tempfile.TemporaryDirectory(prefix='smc-metadata-') as directory: + d=Path(directory) + for name,body in [('fixed',function),('old',old)]: + (d/(name+'.c')).write_text(prefix+body+suffix) + subprocess.run(['cc','-std=gnu11','-Wall','-Wextra','-Werror','-fsanitize=address,undefined',str(d/(name+'.c')),'-o',str(d/name)],check=True) + result=subprocess.run([str(d/name)],env={**os.environ,'LD_PRELOAD':''}) + assert result.returncode==(0 if name=='fixed' else 42) +print('SMC metadata: concurrent reply protected; errors and NULL metadata unlock correctly; old path fails the race control')