[PATCH] firmware: smccc: Support optional Arm SMC SOC_ID name

Paul Benoit
2024-11-14T03:19:19Z

Issue Number 1.6 of the Arm SMC Calling Convention introduces an
optional SOC_ID name string. If available, point the 'machine' field of
the SoC Device Attributes at this string so that it will appear under
/sys/bus/soc/devices/soc0/machine. On Arm SMC compliant SoCs, this will
allow things like 'lscpu' to eventually get a SoC provider model name
from there rather than each tool/utility needing to get a possibly
inconsistent, obsolete, or incorrect model/machine name from its own
hardcoded model/machine name table.

Signed-off-by: Paul Benoit <paul@os.amperecomputing.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: linux-arm-kernel@lists.infradead.org
---
drivers/firmware/smccc/smccc.c | 70 +++++++++++++++++++++++++++++++++
drivers/firmware/smccc/soc_id.c | 1 +
include/linux/arm-smccc.h | 10 +++++
3 files changed, 81 insertions(+)

diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
index a74600d9f2d7..1c7084b0b8d7 100644
--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -18,10 +18,12 @@ static enum arm_smccc_conduit smccc_conduit = SMCCC_CONDUIT_NONE;
bool __ro_after_init smccc_trng_available = false;
s32 __ro_after_init smccc_soc_id_version = SMCCC_RET_NOT_SUPPORTED;
s32 __ro_after_init smccc_soc_id_revision = SMCCC_RET_NOT_SUPPORTED;
+char __ro_after_init smccc_soc_id_name[136] = "";

void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
{
struct arm_smccc_res res;
+ struct arm_smccc_1_2_regs regs_1_2;

smccc_version = version;
smccc_conduit = conduit;
@@ -37,6 +39,66 @@ void __init arm_smccc_version_init(u32 version, enum arm_smccc_conduit conduit)
smccc_soc_id_version = (s32)res.a0;
arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_SOC_ID, 1, &res);
smccc_soc_id_revision = (s32)res.a0;
+
+ /* Issue Number 1.6 of the Arm SMC Calling Convention
+ * specification introduces an optional "name" string
+ * to the ARM_SMCCC_ARCH_SOC_ID function. Fetch it if
+ * available.
+ */
+ regs_1_2.a0 = ARM_SMCCC_ARCH_SOC_ID;
+ regs_1_2.a1 = 2; /* SOC_ID name */
+ arm_smccc_1_2_smc(
+ (const struct arm_smccc_1_2_regs *)&regs_1_2,
+ (struct arm_smccc_1_2_regs *)&regs_1_2);
+
+ if ((u32)regs_1_2.a0 == 0) {
+ unsigned long *destination =
+ (unsigned long *)smccc_soc_id_name;
+
+ /*
+ * Copy regs_1_2.a1..regs_1_2.a17 to the
+ * smccc_soc_id_name string with consideration
+ * to the endianness of the values in a1..a17.
+ * As per Issue 1.6 of the Arm SMC Calling
+ * Convention, the string will be NUL terminated
+ * and padded, from the end of the string to
+ * the end of the 136 byte buffer, with NULs.
+ */
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a1);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a2);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a3);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a4);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a5);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a6);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a7);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a8);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a9);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a10);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a11);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a12);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a13);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a14);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a15);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a16);
+ *destination++ =
+ cpu_to_le64p((const __u64 *)&regs_1_2.a17);
+ }
}
}
}
@@ -67,6 +129,14 @@ s32 arm_smccc_get_soc_id_revision(void)
}
EXPORT_SYMBOL_GPL(arm_smccc_get_soc_id_revision);

+char *arm_smccc_get_soc_id_name(void)
+{
+ if (strnlen(smccc_soc_id_name, sizeof(smccc_soc_id_name)))
+ return smccc_soc_id_name;
+
+ return NULL;
+}
+
static int __init smccc_devices_init(void)
{
struct platform_device *pdev;
diff --git a/drivers/firmware/smccc/soc_id.c b/drivers/firmware/smccc/soc_id.c
index 1990263fbba0..6f698c703868 100644
--- a/drivers/firmware/smccc/soc_id.c
+++ b/drivers/firmware/smccc/soc_id.c
@@ -72,6 +72,7 @@ static int __init smccc_soc_init(void)
soc_dev_attr->soc_id = soc_id_str;
soc_dev_attr->revision = soc_id_rev_str;
soc_dev_attr->family = soc_id_jep106_id_str;
+ soc_dev_attr->machine = arm_smccc_get_soc_id_name();

soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev)) {
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h
index 67f6fdf2e7cd..5935cf636135 100644
--- a/include/linux/arm-smccc.h
+++ b/include/linux/arm-smccc.h
@@ -333,6 +333,16 @@ s32 arm_smccc_get_soc_id_version(void);
*/
s32 arm_smccc_get_soc_id_revision(void);

+/**
+ * arm_smccc_get_soc_id_name()
+ *
+ * Returns the SOC ID name.
+ *
+ * When ARM_SMCCC_ARCH_SOC_ID name is not present, returns NULL.
+ */
+char *arm_smccc_get_soc_id_name(void);
+
+
/**
* struct arm_smccc_res - Result from SMC/HVC call
* @a0-a3 result values from registers 0 to 3
--
2.46.2