diff options
Diffstat (limited to 'drivers/firmware')
34 files changed, 2447 insertions, 291 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 7df19d82aa68..bbd2155d8483 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -267,6 +267,23 @@ config TURRIS_MOX_RWTM other manufacturing data and also utilize the Entropy Bit Generator for hardware random number generation. +if TURRIS_MOX_RWTM + +config TURRIS_MOX_RWTM_KEYCTL + bool "Turris Mox rWTM ECDSA message signing" + default y + depends on KEYS + depends on ASYMMETRIC_KEY_TYPE + select CZNIC_PLATFORMS + select TURRIS_SIGNING_KEY + help + Say Y here to add support for ECDSA message signing with board private + key (each Turris Mox has an ECDSA private key generated in the secure + coprocessor when manufactured). This functionality is exposed via the + keyctl() syscall. + +endif # TURRIS_MOX_RWTM + source "drivers/firmware/arm_ffa/Kconfig" source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/cirrus/Kconfig" diff --git a/drivers/firmware/arm_scmi/Kconfig b/drivers/firmware/arm_scmi/Kconfig index dabd874641d0..e3fb36825978 100644 --- a/drivers/firmware/arm_scmi/Kconfig +++ b/drivers/firmware/arm_scmi/Kconfig @@ -69,6 +69,19 @@ config ARM_SCMI_DEBUG_COUNTERS such useful debug counters. This can be helpful for debugging and SCMI monitoring. +config ARM_SCMI_QUIRKS + bool "Enable SCMI Quirks framework" + depends on JUMP_LABEL || COMPILE_TEST + default y + help + Enables support for SCMI Quirks framework to workaround SCMI platform + firmware bugs on system already deployed in the wild. + + The framework allows the definition of platform-specific code quirks + that will be associated and enabled only on the desired platforms + depending on the SCMI firmware advertised versions and/or machine + compatibles. + source "drivers/firmware/arm_scmi/transports/Kconfig" source "drivers/firmware/arm_scmi/vendors/imx/Kconfig" diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 9ac81adff567..780cd62b2f78 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -3,6 +3,7 @@ scmi-bus-y = bus.o scmi-core-objs := $(scmi-bus-y) scmi-driver-y = driver.o notify.o +scmi-driver-$(CONFIG_ARM_SCMI_QUIRKS) += quirks.o scmi-driver-$(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT) += raw_mode.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o scmi-transport-$(CONFIG_ARM_SCMI_HAVE_MSG) += msg.o diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 3a5474015f7d..1adef0389475 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -201,55 +201,51 @@ scmi_protocol_table_unregister(const struct scmi_device_id *id_table) scmi_protocol_device_unrequest(entry); } -static const struct scmi_device_id * -scmi_dev_match_id(struct scmi_device *scmi_dev, const struct scmi_driver *scmi_drv) +static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, + const struct scmi_device_id *id_table) { - const struct scmi_device_id *id = scmi_drv->id_table; - - if (!id) - return NULL; - - for (; id->protocol_id; id++) - if (id->protocol_id == scmi_dev->protocol_id) { - if (!id->name) - return id; - else if (!strcmp(id->name, scmi_dev->name)) - return id; - } + if (!id_table || !id_table->name) + return 0; + + /* Always skip transport devices from matching */ + for (; id_table->protocol_id && id_table->name; id_table++) + if (id_table->protocol_id == scmi_dev->protocol_id && + strncmp(scmi_dev->name, "__scmi_transport_device", 23) && + !strcmp(id_table->name, scmi_dev->name)) + return 1; + return 0; +} - return NULL; +static int scmi_dev_match_id(struct scmi_device *scmi_dev, + const struct scmi_driver *scmi_drv) +{ + return scmi_dev_match_by_id_table(scmi_dev, scmi_drv->id_table); } static int scmi_dev_match(struct device *dev, const struct device_driver *drv) { const struct scmi_driver *scmi_drv = to_scmi_driver(drv); struct scmi_device *scmi_dev = to_scmi_dev(dev); - const struct scmi_device_id *id; - - id = scmi_dev_match_id(scmi_dev, scmi_drv); - if (id) - return 1; - return 0; + return scmi_dev_match_id(scmi_dev, scmi_drv); } static int scmi_match_by_id_table(struct device *dev, const void *data) { - struct scmi_device *sdev = to_scmi_dev(dev); + struct scmi_device *scmi_dev = to_scmi_dev(dev); const struct scmi_device_id *id_table = data; - return sdev->protocol_id == id_table->protocol_id && - (id_table->name && !strcmp(sdev->name, id_table->name)); + return scmi_dev_match_by_id_table(scmi_dev, id_table); } static struct scmi_device *scmi_child_dev_find(struct device *parent, int prot_id, const char *name) { - struct scmi_device_id id_table; + struct scmi_device_id id_table[2] = { 0 }; struct device *dev; - id_table.protocol_id = prot_id; - id_table.name = name; + id_table[0].protocol_id = prot_id; + id_table[0].name = name; dev = device_find_child(parent, &id_table, scmi_match_by_id_table); if (!dev) @@ -463,6 +459,20 @@ put_dev: return NULL; } +static struct scmi_device * +_scmi_device_create(struct device_node *np, struct device *parent, + int protocol, const char *name) +{ + struct scmi_device *sdev; + + sdev = __scmi_device_create(np, parent, protocol, name); + if (!sdev) + pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n", + of_node_full_name(parent->of_node), protocol, name); + + return sdev; +} + /** * scmi_device_create - A method to create one or more SCMI devices * @@ -495,7 +505,7 @@ struct scmi_device *scmi_device_create(struct device_node *np, struct scmi_device *scmi_dev = NULL; if (name) - return __scmi_device_create(np, parent, protocol, name); + return _scmi_device_create(np, parent, protocol, name); mutex_lock(&scmi_requested_devices_mtx); phead = idr_find(&scmi_requested_devices, protocol); @@ -509,18 +519,13 @@ struct scmi_device *scmi_device_create(struct device_node *np, list_for_each_entry(rdev, phead, node) { struct scmi_device *sdev; - sdev = __scmi_device_create(np, parent, - rdev->id_table->protocol_id, - rdev->id_table->name); - /* Report errors and carry on... */ + sdev = _scmi_device_create(np, parent, + rdev->id_table->protocol_id, + rdev->id_table->name); if (sdev) scmi_dev = sdev; - else - pr_err("(%s) Failed to create device for protocol 0x%x (%s)\n", - of_node_full_name(parent->of_node), - rdev->id_table->protocol_id, - rdev->id_table->name); } + mutex_unlock(&scmi_requested_devices_mtx); return scmi_dev; diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 2ed2279388f0..afa7981efe82 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -11,6 +11,7 @@ #include "protocols.h" #include "notify.h" +#include "quirks.h" /* Updated only after ALL the mandatory features for that version are merged */ #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000 @@ -429,6 +430,23 @@ static void iter_clk_describe_prepare_message(void *message, msg->rate_index = cpu_to_le32(desc_index); } +#define QUIRK_OUT_OF_SPEC_TRIPLET \ + ({ \ + /* \ + * A known quirk: a triplet is returned but num_returned != 3 \ + * Check for a safe payload size and fix. \ + */ \ + if (st->num_returned != 3 && st->num_remaining == 0 && \ + st->rx_len == sizeof(*r) + sizeof(__le32) * 2 * 3) { \ + st->num_returned = 3; \ + st->num_remaining = 0; \ + } else { \ + dev_err(p->dev, \ + "Cannot fix out-of-spec reply !\n"); \ + return -EPROTO; \ + } \ + }) + static int iter_clk_describe_update_state(struct scmi_iterator_state *st, const void *response, void *priv) @@ -450,19 +468,8 @@ iter_clk_describe_update_state(struct scmi_iterator_state *st, p->clk->name, st->num_returned, st->num_remaining, st->rx_len); - /* - * A known quirk: a triplet is returned but num_returned != 3 - * Check for a safe payload size and fix. - */ - if (st->num_returned != 3 && st->num_remaining == 0 && - st->rx_len == sizeof(*r) + sizeof(__le32) * 2 * 3) { - st->num_returned = 3; - st->num_remaining = 0; - } else { - dev_err(p->dev, - "Cannot fix out-of-spec reply !\n"); - return -EPROTO; - } + SCMI_QUIRK(clock_rates_triplet_out_of_spec, + QUIRK_OUT_OF_SPEC_TRIPLET); } return 0; diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 10ea7962323e..dab758c5fdea 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -475,6 +475,7 @@ static int __tag##_probe(struct platform_device *pdev) \ if (ret) \ goto err; \ \ + spdev->dev.parent = dev; \ ret = platform_device_add(spdev); \ if (ret) \ goto err; \ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 0390d5ff195e..395fe9289035 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -11,7 +11,7 @@ * various power domain DVFS including the core/cluster, certain system * clocks configuration, thermal sensors and many others. * - * Copyright (C) 2018-2024 ARM Ltd. + * Copyright (C) 2018-2025 ARM Ltd. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -38,6 +38,7 @@ #include "common.h" #include "notify.h" +#include "quirks.h" #include "raw_mode.h" @@ -439,14 +440,8 @@ static void scmi_create_protocol_devices(struct device_node *np, struct scmi_info *info, int prot_id, const char *name) { - struct scmi_device *sdev; - mutex_lock(&info->devreq_mtx); - sdev = scmi_device_create(np, info->dev, prot_id, name); - if (name && !sdev) - dev_err(info->dev, - "failed to create device for protocol 0x%X (%s)\n", - prot_id, name); + scmi_device_create(np, info->dev, prot_id, name); mutex_unlock(&info->devreq_mtx); } @@ -1190,7 +1185,8 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo, * RX path since it will be already queued at the end of the TX * poll loop. */ - if (!xfer->hdr.poll_completion) + if (!xfer->hdr.poll_completion || + xfer->hdr.type == MSG_TYPE_DELAYED_RESP) scmi_raw_message_report(info->raw, xfer, SCMI_RAW_REPLY_QUEUE, cinfo->id); @@ -1738,6 +1734,39 @@ static int scmi_common_get_max_msg_size(const struct scmi_protocol_handle *ph) } /** + * scmi_protocol_msg_check - Check protocol message attributes + * + * @ph: A reference to the protocol handle. + * @message_id: The ID of the message to check. + * @attributes: A parameter to optionally return the retrieved message + * attributes, in case of Success. + * + * An helper to check protocol message attributes for a specific protocol + * and message pair. + * + * Return: 0 on SUCCESS + */ +static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph, + u32 message_id, u32 *attributes) +{ + int ret; + struct scmi_xfer *t; + + ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES, + sizeof(__le32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(message_id, t->tx.buf); + ret = do_xfer(ph, t); + if (!ret && attributes) + *attributes = get_unaligned_le32(t->rx.buf); + xfer_put(ph, t); + + return ret; +} + +/** * struct scmi_iterator - Iterator descriptor * @msg: A reference to the message TX buffer; filled by @prepare_message with * a proper custom command payload for each multi-part command request. @@ -1869,6 +1898,13 @@ struct scmi_msg_resp_desc_fc { __le32 db_preserve_hmask; }; +#define QUIRK_PERF_FC_FORCE \ + ({ \ + if (pi->proto->id == SCMI_PROTOCOL_PERF && \ + message_id == 0x8 /* PERF_LEVEL_GET */) \ + attributes |= BIT(0); \ + }) + static void scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph, u8 describe_id, u32 message_id, u32 valid_size, @@ -1878,6 +1914,7 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph, int ret; u32 flags; u64 phys_addr; + u32 attributes; u8 size; void __iomem *addr; struct scmi_xfer *t; @@ -1886,6 +1923,16 @@ scmi_common_fastchannel_init(const struct scmi_protocol_handle *ph, struct scmi_msg_resp_desc_fc *resp; const struct scmi_protocol_instance *pi = ph_to_pi(ph); + /* Check if the MSG_ID supports fastchannel */ + ret = scmi_protocol_msg_check(ph, message_id, &attributes); + SCMI_QUIRK(perf_level_get_fc_force, QUIRK_PERF_FC_FORCE); + if (ret || !MSG_SUPPORTS_FASTCHANNEL(attributes)) { + dev_dbg(ph->dev, + "Skip FC init for 0x%02X/%d domain:%d - ret:%d\n", + pi->proto->id, message_id, domain, ret); + return; + } + if (!p_addr) { ret = -EINVAL; goto err_out; @@ -2003,39 +2050,6 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db) SCMI_PROTO_FC_RING_DB(64); } -/** - * scmi_protocol_msg_check - Check protocol message attributes - * - * @ph: A reference to the protocol handle. - * @message_id: The ID of the message to check. - * @attributes: A parameter to optionally return the retrieved message - * attributes, in case of Success. - * - * An helper to check protocol message attributes for a specific protocol - * and message pair. - * - * Return: 0 on SUCCESS - */ -static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph, - u32 message_id, u32 *attributes) -{ - int ret; - struct scmi_xfer *t; - - ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES, - sizeof(__le32), 0, &t); - if (ret) - return ret; - - put_unaligned_le32(message_id, t->tx.buf); - ret = do_xfer(ph, t); - if (!ret && attributes) - *attributes = get_unaligned_le32(t->rx.buf); - xfer_put(ph, t); - - return ret; -} - static const struct scmi_proto_helpers_ops helpers_ops = { .extended_name_get = scmi_common_extended_name_get, .get_max_msg_size = scmi_common_get_max_msg_size, @@ -2828,9 +2842,8 @@ static int scmi_bus_notifier(struct notifier_block *nb, struct scmi_info *info = bus_nb_to_scmi_info(nb); struct scmi_device *sdev = to_scmi_dev(data); - /* Skip transport devices and devices of different SCMI instances */ - if (!strncmp(sdev->name, "__scmi_transport_device", 23) || - sdev->dev.parent != info->dev) + /* Skip devices of different SCMI instances */ + if (sdev->dev.parent != info->dev) return NOTIFY_DONE; switch (action) { @@ -3101,6 +3114,18 @@ static const struct scmi_desc *scmi_transport_setup(struct device *dev) return &trans->desc; } +static void scmi_enable_matching_quirks(struct scmi_info *info) +{ + struct scmi_revision_info *rev = &info->version; + + dev_dbg(info->dev, "Looking for quirks matching: %s/%s/0x%08X\n", + rev->vendor_id, rev->sub_vendor_id, rev->impl_ver); + + /* Enable applicable quirks */ + scmi_quirks_enable(info->dev, rev->vendor_id, + rev->sub_vendor_id, rev->impl_ver); +} + static int scmi_probe(struct platform_device *pdev) { int ret; @@ -3222,6 +3247,8 @@ static int scmi_probe(struct platform_device *pdev) list_add_tail(&info->node, &scmi_list); mutex_unlock(&scmi_list_mutex); + scmi_enable_matching_quirks(info); + for_each_available_child_of_node(np, child) { u32 prot_id; @@ -3380,6 +3407,8 @@ static struct dentry *scmi_debugfs_init(void) static int __init scmi_driver_init(void) { + scmi_quirks_initialize(); + /* Bail out if no SCMI transport was configured */ if (WARN_ON(!IS_ENABLED(CONFIG_ARM_SCMI_HAVE_TRANSPORT))) return -EINVAL; diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index aaee57cdcd55..d62c4469d1fd 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -31,6 +31,8 @@ #define SCMI_PROTOCOL_VENDOR_BASE 0x80 +#define MSG_SUPPORTS_FASTCHANNEL(x) ((x) & BIT(0)) + enum scmi_common_cmd { PROTOCOL_VERSION = 0x0, PROTOCOL_ATTRIBUTES = 0x1, diff --git a/drivers/firmware/arm_scmi/quirks.c b/drivers/firmware/arm_scmi/quirks.c new file mode 100644 index 000000000000..03960aca3610 --- /dev/null +++ b/drivers/firmware/arm_scmi/quirks.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Message Protocol Quirks + * + * Copyright (C) 2025 ARM Ltd. + */ + +/** + * DOC: Theory of operation + * + * A framework to define SCMI quirks and their activation conditions based on + * existing static_keys kernel facilities. + * + * Quirks are named and their activation conditions defined using the macro + * DEFINE_SCMI_QUIRK() in this file. + * + * After a quirk is defined, a corresponding entry must also be added to the + * global @scmi_quirks_table in this file using __DECLARE_SCMI_QUIRK_ENTRY(). + * + * Additionally a corresponding quirk declaration must be added also to the + * quirk.h file using DECLARE_SCMI_QUIRK(). + * + * The needed quirk code-snippet itself will be defined local to the SCMI code + * that is meant to fix and will be associated to the previously defined quirk + * and related activation conditions using the macro SCMI_QUIRK(). + * + * At runtime, during the SCMI stack probe sequence, once the SCMI Server had + * advertised the running platform Vendor, SubVendor and Implementation Version + * data, all the defined quirks matching the activation conditions will be + * enabled. + * + * Example + * + * quirk.c + * ------- + * DEFINE_SCMI_QUIRK(fix_me, "vendor", "subvend", "0x12000-0x30000", + * "someone,plat_A", "another,plat_b", "vend,sku"); + * + * static struct scmi_quirk *scmi_quirks_table[] = { + * ... + * __DECLARE_SCMI_QUIRK_ENTRY(fix_me), + * NULL + * }; + * + * quirk.h + * ------- + * DECLARE_SCMI_QUIRK(fix_me); + * + * <somewhere_in_the_scmi_stack.c> + * ------------------------------ + * + * #define QUIRK_CODE_SNIPPET_FIX_ME() \ + * ({ \ + * if (p->condition) \ + * a_ptr->calculated_val = 123; \ + * }) + * + * + * int some_function_to_fix(int param, struct something *p) + * { + * struct some_strut *a_ptr; + * + * a_ptr = some_load_func(p); + * SCMI_QUIRK(fix_me, QUIRK_CODE_SNIPPET_FIX_ME); + * some_more_func(a_ptr); + * ... + * + * return 0; + * } + * + */ + +#include <linux/ctype.h> +#include <linux/device.h> +#include <linux/export.h> +#include <linux/hashtable.h> +#include <linux/kstrtox.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/static_key.h> +#include <linux/string.h> +#include <linux/stringhash.h> +#include <linux/types.h> + +#include "quirks.h" + +#define SCMI_QUIRKS_HT_SZ 4 + +struct scmi_quirk { + bool enabled; + const char *name; + char *vendor; + char *sub_vendor_id; + char *impl_ver_range; + u32 start_range; + u32 end_range; + struct static_key_false *key; + struct hlist_node hash; + unsigned int hkey; + const char *const compats[]; +}; + +#define __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ...) \ + static struct scmi_quirk scmi_quirk_entry_ ## _qn = { \ + .name = __stringify(quirk_ ## _qn), \ + .vendor = _ven, \ + .sub_vendor_id = _sub, \ + .impl_ver_range = _impl, \ + .key = &(scmi_quirk_ ## _qn), \ + .compats = { __VA_ARGS__ __VA_OPT__(,) NULL }, \ + } + +#define __DECLARE_SCMI_QUIRK_ENTRY(_qn) (&(scmi_quirk_entry_ ## _qn)) + +/* + * Define a quirk by name and provide the matching tokens where: + * + * _qn: A string which will be used to build the quirk and the global + * static_key names. + * _ven : SCMI Vendor ID string match, NULL means any. + * _sub : SCMI SubVendor ID string match, NULL means any. + * _impl : SCMI Implementation Version string match, NULL means any. + * This string can be used to express version ranges which will be + * interpreted as follows: + * + * NULL [0, 0xFFFFFFFF] + * "X" [X, X] + * "X-" [X, 0xFFFFFFFF] + * "-X" [0, X] + * "X-Y" [X, Y] + * + * with X <= Y and <v> in [X, Y] meaning X <= <v> <= Y + * + * ... : An optional variadic macros argument used to provide a comma-separated + * list of compatible strings matches; when no variadic argument is + * provided, ANY compatible will match this quirk. + * + * This implicitly define also a properly named global static-key that + * will be used to dynamically enable the quirk at initialization time. + * + * Note that it is possible to associate multiple quirks to the same + * matching pattern, if your firmware quality is really astounding :P + * + * Example: + * + * Compatibles list NOT provided, so ANY compatible will match: + * + * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000"); + * + * + * A few compatibles provided to match against: + * + * DEFINE_SCMI_QUIRK(my_new_issue, "Vend", "SVend", "0x12000-0x30000", + * "xvend,plat_a", "xvend,plat_b", "xvend,sku_name"); + */ +#define DEFINE_SCMI_QUIRK(_qn, _ven, _sub, _impl, ...) \ + DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \ + __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__) + +/* + * Same as DEFINE_SCMI_QUIRK but EXPORTED: this is meant to address quirks + * that possibly reside in code that is included in loadable kernel modules + * that needs to be able to access the global static keys at runtime to + * determine if enabled or not. (see SCMI_QUIRK to understand usage) + */ +#define DEFINE_SCMI_QUIRK_EXPORTED(_qn, _ven, _sub, _impl, ...) \ + DEFINE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn); \ + EXPORT_SYMBOL_GPL(scmi_quirk_ ## _qn); \ + __DEFINE_SCMI_QUIRK_ENTRY(_qn, _ven, _sub, _impl, ##__VA_ARGS__) + +/* Global Quirks Definitions */ +DEFINE_SCMI_QUIRK(clock_rates_triplet_out_of_spec, NULL, NULL, NULL); +DEFINE_SCMI_QUIRK(perf_level_get_fc_force, "Qualcomm", NULL, "0x20000-"); + +/* + * Quirks Pointers Array + * + * This is filled at compile-time with the list of pointers to all the currently + * defined quirks descriptors. + */ +static struct scmi_quirk *scmi_quirks_table[] = { + __DECLARE_SCMI_QUIRK_ENTRY(clock_rates_triplet_out_of_spec), + __DECLARE_SCMI_QUIRK_ENTRY(perf_level_get_fc_force), + NULL +}; + +/* + * Quirks HashTable + * + * A run-time populated hashtable containing all the defined quirks descriptors + * hashed by matching pattern. + */ +static DEFINE_READ_MOSTLY_HASHTABLE(scmi_quirks_ht, SCMI_QUIRKS_HT_SZ); + +static unsigned int scmi_quirk_signature(const char *vend, const char *sub_vend) +{ + char *signature, *p; + unsigned int hash32; + unsigned long hash = 0; + + /* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */ + signature = kasprintf(GFP_KERNEL, "|%s|%s|", vend ?: "", sub_vend ?: ""); + if (!signature) + return 0; + + pr_debug("SCMI Quirk Signature >>>%s<<<\n", signature); + + p = signature; + while (*p) + hash = partial_name_hash(tolower(*p++), hash); + hash32 = end_name_hash(hash); + + kfree(signature); + + return hash32; +} + +static int scmi_quirk_range_parse(struct scmi_quirk *quirk) +{ + const char *last, *first = quirk->impl_ver_range; + size_t len; + char *sep; + int ret; + + quirk->start_range = 0; + quirk->end_range = 0xFFFFFFFF; + len = quirk->impl_ver_range ? strlen(quirk->impl_ver_range) : 0; + if (!len) + return 0; + + last = first + len - 1; + sep = strchr(quirk->impl_ver_range, '-'); + if (sep) + *sep = '\0'; + + if (sep == first) /* -X */ + ret = kstrtouint(first + 1, 0, &quirk->end_range); + else /* X OR X- OR X-y */ + ret = kstrtouint(first, 0, &quirk->start_range); + if (ret) + return ret; + + if (!sep) + quirk->end_range = quirk->start_range; + else if (sep != last) /* x-Y */ + ret = kstrtouint(sep + 1, 0, &quirk->end_range); + + if (quirk->start_range > quirk->end_range) + return -EINVAL; + + return ret; +} + +void scmi_quirks_initialize(void) +{ + struct scmi_quirk *quirk; + int i; + + for (i = 0, quirk = scmi_quirks_table[0]; quirk; + i++, quirk = scmi_quirks_table[i]) { + int ret; + + ret = scmi_quirk_range_parse(quirk); + if (ret) { + pr_err("SCMI skip QUIRK [%s] - BAD RANGE - |%s|\n", + quirk->name, quirk->impl_ver_range); + continue; + } + quirk->hkey = scmi_quirk_signature(quirk->vendor, + quirk->sub_vendor_id); + + hash_add(scmi_quirks_ht, &quirk->hash, quirk->hkey); + + pr_debug("Registered SCMI QUIRK [%s] -- %p - Key [0x%08X] - %s/%s/[0x%08X-0x%08X]\n", + quirk->name, quirk, quirk->hkey, + quirk->vendor, quirk->sub_vendor_id, + quirk->start_range, quirk->end_range); + } + + pr_debug("SCMI Quirks initialized\n"); +} + +void scmi_quirks_enable(struct device *dev, const char *vend, + const char *subv, const u32 impl) +{ + for (int i = 3; i >= 0; i--) { + struct scmi_quirk *quirk; + unsigned int hkey; + + hkey = scmi_quirk_signature(i > 1 ? vend : NULL, + i > 2 ? subv : NULL); + + /* + * Note that there could be multiple matches so we + * will enable multiple quirk part of a hash collision + * domain...BUT we cannot assume that ALL quirks on the + * same collision domain are a full match. + */ + hash_for_each_possible(scmi_quirks_ht, quirk, hash, hkey) { + if (quirk->enabled || quirk->hkey != hkey || + impl < quirk->start_range || + impl > quirk->end_range) + continue; + + if (quirk->compats[0] && + !of_machine_compatible_match(quirk->compats)) + continue; + + dev_info(dev, "Enabling SCMI Quirk [%s]\n", + quirk->name); + + dev_dbg(dev, + "Quirk matched on: %s/%s/%s/[0x%08X-0x%08X]\n", + quirk->compats[0], quirk->vendor, + quirk->sub_vendor_id, + quirk->start_range, quirk->end_range); + + static_branch_enable(quirk->key); + quirk->enabled = true; + } + } +} diff --git a/drivers/firmware/arm_scmi/quirks.h b/drivers/firmware/arm_scmi/quirks.h new file mode 100644 index 000000000000..a71fde85a527 --- /dev/null +++ b/drivers/firmware/arm_scmi/quirks.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * System Control and Management Interface (SCMI) Message Protocol Quirks + * + * Copyright (C) 2025 ARM Ltd. + */ +#ifndef _SCMI_QUIRKS_H +#define _SCMI_QUIRKS_H + +#include <linux/static_key.h> +#include <linux/types.h> + +#ifdef CONFIG_ARM_SCMI_QUIRKS + +#define DECLARE_SCMI_QUIRK(_qn) \ + DECLARE_STATIC_KEY_FALSE(scmi_quirk_ ## _qn) + +/* + * A helper to associate the actual code snippet to use as a quirk + * named as _qn. + */ +#define SCMI_QUIRK(_qn, _blk) \ + do { \ + if (static_branch_unlikely(&(scmi_quirk_ ## _qn))) \ + (_blk); \ + } while (0) + +void scmi_quirks_initialize(void); +void scmi_quirks_enable(struct device *dev, const char *vend, + const char *subv, const u32 impl); + +#else + +#define DECLARE_SCMI_QUIRK(_qn) +/* Force quirks compilation even when SCMI Quirks are disabled */ +#define SCMI_QUIRK(_qn, _blk) \ + do { \ + if (0) \ + (_blk); \ + } while (0) + +static inline void scmi_quirks_initialize(void) { } +static inline void scmi_quirks_enable(struct device *dev, const char *vend, + const char *sub_vend, const u32 impl) { } + +#endif /* CONFIG_ARM_SCMI_QUIRKS */ + +/* Quirk delarations */ +DECLARE_SCMI_QUIRK(clock_rates_triplet_out_of_spec); +DECLARE_SCMI_QUIRK(perf_level_get_fc_force); + +#endif /* _SCMI_QUIRKS_H */ diff --git a/drivers/firmware/arm_scmi/raw_mode.c b/drivers/firmware/arm_scmi/raw_mode.c index 7cc0d616b8de..3d543b1d8947 100644 --- a/drivers/firmware/arm_scmi/raw_mode.c +++ b/drivers/firmware/arm_scmi/raw_mode.c @@ -671,11 +671,13 @@ static int scmi_do_xfer_raw_start(struct scmi_raw_mode_info *raw, * @len: Length of the message in @buf. * @chan_id: The channel ID to use. * @async: A flag stating if an asynchronous command is required. + * @poll: A flag stating if a polling transmission is required. * * Return: 0 on Success */ static int scmi_raw_message_send(struct scmi_raw_mode_info *raw, - void *buf, size_t len, u8 chan_id, bool async) + void *buf, size_t len, u8 chan_id, + bool async, bool poll) { int ret; struct scmi_xfer *xfer; @@ -684,6 +686,16 @@ static int scmi_raw_message_send(struct scmi_raw_mode_info *raw, if (ret) return ret; + if (poll) { + if (is_transport_polling_capable(raw->desc)) { + xfer->hdr.poll_completion = true; + } else { + dev_err(raw->handle->dev, + "Failed to send RAW message - Polling NOT supported\n"); + return -EINVAL; + } + } + ret = scmi_do_xfer_raw_start(raw, xfer, chan_id, async); if (ret) scmi_xfer_raw_put(raw->handle, xfer); @@ -801,7 +813,7 @@ static ssize_t scmi_dbg_raw_mode_common_read(struct file *filp, static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos, - bool async) + bool async, bool poll) { int ret; struct scmi_dbg_raw_data *rd = filp->private_data; @@ -831,7 +843,7 @@ static ssize_t scmi_dbg_raw_mode_common_write(struct file *filp, } ret = scmi_raw_message_send(rd->raw, rd->tx.buf, rd->tx_size, - rd->chan_id, async); + rd->chan_id, async, poll); /* Reset ppos for next message ... */ rd->tx_size = 0; @@ -875,7 +887,8 @@ static ssize_t scmi_dbg_raw_mode_message_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { - return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, false); + return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, + false, false); } static __poll_t scmi_dbg_raw_mode_message_poll(struct file *filp, @@ -964,7 +977,8 @@ static ssize_t scmi_dbg_raw_mode_message_async_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { - return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, true); + return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, + true, false); } static const struct file_operations scmi_dbg_raw_mode_message_async_fops = { @@ -976,6 +990,40 @@ static const struct file_operations scmi_dbg_raw_mode_message_async_fops = { .owner = THIS_MODULE, }; +static ssize_t scmi_dbg_raw_mode_message_poll_write(struct file *filp, + const char __user *buf, + size_t count, loff_t *ppos) +{ + return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, + false, true); +} + +static const struct file_operations scmi_dbg_raw_mode_message_poll_fops = { + .open = scmi_dbg_raw_mode_open, + .release = scmi_dbg_raw_mode_release, + .read = scmi_dbg_raw_mode_message_read, + .write = scmi_dbg_raw_mode_message_poll_write, + .poll = scmi_dbg_raw_mode_message_poll, + .owner = THIS_MODULE, +}; + +static ssize_t scmi_dbg_raw_mode_message_poll_async_write(struct file *filp, + const char __user *buf, + size_t count, loff_t *ppos) +{ + return scmi_dbg_raw_mode_common_write(filp, buf, count, ppos, + true, true); +} + +static const struct file_operations scmi_dbg_raw_mode_message_poll_async_fops = { + .open = scmi_dbg_raw_mode_open, + .release = scmi_dbg_raw_mode_release, + .read = scmi_dbg_raw_mode_message_read, + .write = scmi_dbg_raw_mode_message_poll_async_write, + .poll = scmi_dbg_raw_mode_message_poll, + .owner = THIS_MODULE, +}; + static ssize_t scmi_test_dbg_raw_mode_notif_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) @@ -1199,6 +1247,12 @@ void *scmi_raw_mode_init(const struct scmi_handle *handle, debugfs_create_file("message_async", 0600, raw->dentry, raw, &scmi_dbg_raw_mode_message_async_fops); + debugfs_create_file("message_poll", 0600, raw->dentry, raw, + &scmi_dbg_raw_mode_message_poll_fops); + + debugfs_create_file("message_poll_async", 0600, raw->dentry, raw, + &scmi_dbg_raw_mode_message_poll_async_fops); + debugfs_create_file("notification", 0400, raw->dentry, raw, &scmi_dbg_raw_mode_notification_fops); @@ -1230,6 +1284,14 @@ void *scmi_raw_mode_init(const struct scmi_handle *handle, debugfs_create_file_aux_num("message_async", 0600, chd, raw, channels[i], &scmi_dbg_raw_mode_message_async_fops); + + debugfs_create_file_aux_num("message_poll", 0600, chd, + raw, channels[i], + &scmi_dbg_raw_mode_message_poll_fops); + + debugfs_create_file_aux_num("message_poll_async", 0600, + chd, raw, channels[i], + &scmi_dbg_raw_mode_message_poll_async_fops); } } diff --git a/drivers/firmware/arm_scmi/vendors/imx/Kconfig b/drivers/firmware/arm_scmi/vendors/imx/Kconfig index a01bf5e47301..c34c8c837441 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/Kconfig +++ b/drivers/firmware/arm_scmi/vendors/imx/Kconfig @@ -12,6 +12,30 @@ config IMX_SCMI_BBM_EXT To compile this driver as a module, choose M here: the module will be called imx-sm-bbm. +config IMX_SCMI_CPU_EXT + tristate "i.MX SCMI CPU EXTENSION" + depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) + depends on IMX_SCMI_CPU_DRV + default y if ARCH_MXC + help + This enables i.MX System CPU Protocol to manage cpu + start, stop and etc. + + To compile this driver as a module, choose M here: the + module will be called imx-sm-cpu. + +config IMX_SCMI_LMM_EXT + tristate "i.MX SCMI LMM EXTENSION" + depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) + depends on IMX_SCMI_LMM_DRV + default y if ARCH_MXC + help + This enables i.MX System Logical Machine Protocol to + manage Logical Machines boot, shutdown and etc. + + To compile this driver as a module, choose M here: the + module will be called imx-sm-lmm. + config IMX_SCMI_MISC_EXT tristate "i.MX SCMI MISC EXTENSION" depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) diff --git a/drivers/firmware/arm_scmi/vendors/imx/Makefile b/drivers/firmware/arm_scmi/vendors/imx/Makefile index d3ee6d544924..e3a5ea46345c 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/Makefile +++ b/drivers/firmware/arm_scmi/vendors/imx/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_IMX_SCMI_BBM_EXT) += imx-sm-bbm.o +obj-$(CONFIG_IMX_SCMI_CPU_EXT) += imx-sm-cpu.o +obj-$(CONFIG_IMX_SCMI_LMM_EXT) += imx-sm-lmm.o obj-$(CONFIG_IMX_SCMI_MISC_EXT) += imx-sm-misc.o diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c new file mode 100644 index 000000000000..66f47f5371e5 --- /dev/null +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System control and Management Interface (SCMI) NXP CPU Protocol + * + * Copyright 2025 NXP + */ + +#include <linux/bits.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/scmi_protocol.h> +#include <linux/scmi_imx_protocol.h> + +#include "../../protocols.h" +#include "../../notify.h" + +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 + +enum scmi_imx_cpu_protocol_cmd { + SCMI_IMX_CPU_ATTRIBUTES = 0x3, + SCMI_IMX_CPU_START = 0x4, + SCMI_IMX_CPU_STOP = 0x5, + SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6, + SCMI_IMX_CPU_INFO_GET = 0xC, +}; + +struct scmi_imx_cpu_info { + u32 nr_cpu; +}; + +#define SCMI_IMX_CPU_NR_CPU_MASK GENMASK(15, 0) +struct scmi_msg_imx_cpu_protocol_attributes { + __le32 attributes; +}; + +struct scmi_msg_imx_cpu_attributes_out { + __le32 attributes; +#define CPU_MAX_NAME 16 + u8 name[CPU_MAX_NAME]; +}; + +struct scmi_imx_cpu_reset_vector_set_in { + __le32 cpuid; +#define CPU_VEC_FLAGS_RESUME BIT(31) +#define CPU_VEC_FLAGS_START BIT(30) +#define CPU_VEC_FLAGS_BOOT BIT(29) + __le32 flags; + __le32 resetvectorlow; + __le32 resetvectorhigh; +}; + +struct scmi_imx_cpu_info_get_out { +#define CPU_RUN_MODE_START 0 +#define CPU_RUN_MODE_HOLD 1 +#define CPU_RUN_MODE_STOP 2 +#define CPU_RUN_MODE_SLEEP 3 + __le32 runmode; + __le32 sleepmode; + __le32 resetvectorlow; + __le32 resetvectorhigh; +}; + +static int scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle *ph, + u32 cpuid) +{ + struct scmi_imx_cpu_info *info = ph->get_priv(ph); + + if (cpuid >= info->nr_cpu) + return -EINVAL; + + return 0; +} + +static int scmi_imx_cpu_start(const struct scmi_protocol_handle *ph, + u32 cpuid, bool start) +{ + struct scmi_xfer *t; + u8 msg_id; + int ret; + + ret = scmi_imx_cpu_validate_cpuid(ph, cpuid); + if (ret) + return ret; + + if (start) + msg_id = SCMI_IMX_CPU_START; + else + msg_id = SCMI_IMX_CPU_STOP; + + ret = ph->xops->xfer_get_init(ph, msg_id, sizeof(u32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(cpuid, t->tx.buf); + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph, + u32 cpuid, u64 vector, bool start, + bool boot, bool resume) +{ + struct scmi_imx_cpu_reset_vector_set_in *in; + struct scmi_xfer *t; + int ret; + + ret = scmi_imx_cpu_validate_cpuid(ph, cpuid); + if (ret) + return ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in), + 0, &t); + if (ret) + return ret; + + in = t->tx.buf; + in->cpuid = cpu_to_le32(cpuid); + in->flags = cpu_to_le32(0); + if (start) + in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_START); + if (boot) + in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_BOOT); + if (resume) + in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_RESUME); + in->resetvectorlow = cpu_to_le32(lower_32_bits(vector)); + in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector)); + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid, + bool *started) +{ + struct scmi_imx_cpu_info_get_out *out; + struct scmi_xfer *t; + u32 mode; + int ret; + + if (!started) + return -EINVAL; + + *started = false; + ret = scmi_imx_cpu_validate_cpuid(ph, cpuid); + if (ret) + return ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32), + 0, &t); + if (ret) + return ret; + + put_unaligned_le32(cpuid, t->tx.buf); + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + out = t->rx.buf; + mode = le32_to_cpu(out->runmode); + if (mode == CPU_RUN_MODE_START || mode == CPU_RUN_MODE_SLEEP) + *started = true; + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static const struct scmi_imx_cpu_proto_ops scmi_imx_cpu_proto_ops = { + .cpu_reset_vector_set = scmi_imx_cpu_reset_vector_set, + .cpu_start = scmi_imx_cpu_start, + .cpu_started = scmi_imx_cpu_started, +}; + +static int scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle *ph, + struct scmi_imx_cpu_info *info) +{ + struct scmi_msg_imx_cpu_protocol_attributes *attr; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, + sizeof(*attr), &t); + if (ret) + return ret; + + attr = t->rx.buf; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + info->nr_cpu = le32_get_bits(attr->attributes, SCMI_IMX_CPU_NR_CPU_MASK); + dev_info(ph->dev, "i.MX SM CPU: %d cpus\n", + info->nr_cpu); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph, + u32 cpuid) +{ + struct scmi_msg_imx_cpu_attributes_out *out; + char name[SCMI_SHORT_NAME_MAX_SIZE] = {'\0'}; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_ATTRIBUTES, sizeof(u32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(cpuid, t->tx.buf); + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + out = t->rx.buf; + strscpy(name, out->name, SCMI_SHORT_NAME_MAX_SIZE); + dev_info(ph->dev, "i.MX CPU: name: %s\n", name); + } else { + dev_err(ph->dev, "i.MX cpu: Failed to get info of cpu(%u)\n", cpuid); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph) +{ + struct scmi_imx_cpu_info *info; + u32 version; + int ret, i; + + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; + + dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = scmi_imx_cpu_protocol_attributes_get(ph, info); + if (ret) + return ret; + + for (i = 0; i < info->nr_cpu; i++) { + ret = scmi_imx_cpu_attributes_get(ph, i); + if (ret) + return ret; + } + + return ph->set_priv(ph, info, version); +} + +static const struct scmi_protocol scmi_imx_cpu = { + .id = SCMI_PROTOCOL_IMX_CPU, + .owner = THIS_MODULE, + .instance_init = &scmi_imx_cpu_protocol_init, + .ops = &scmi_imx_cpu_proto_ops, + .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, + .vendor_id = SCMI_IMX_VENDOR, + .sub_vendor_id = SCMI_IMX_SUBVENDOR, +}; +module_scmi_protocol(scmi_imx_cpu); + +MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_CPU) "-" SCMI_IMX_VENDOR); +MODULE_DESCRIPTION("i.MX SCMI CPU driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c new file mode 100644 index 000000000000..b519c67fe920 --- /dev/null +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-lmm.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System control and Management Interface (SCMI) NXP LMM Protocol + * + * Copyright 2025 NXP + */ + +#include <linux/bits.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/scmi_protocol.h> +#include <linux/scmi_imx_protocol.h> + +#include "../../protocols.h" +#include "../../notify.h" + +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 + +enum scmi_imx_lmm_protocol_cmd { + SCMI_IMX_LMM_ATTRIBUTES = 0x3, + SCMI_IMX_LMM_BOOT = 0x4, + SCMI_IMX_LMM_RESET = 0x5, + SCMI_IMX_LMM_SHUTDOWN = 0x6, + SCMI_IMX_LMM_WAKE = 0x7, + SCMI_IMX_LMM_SUSPEND = 0x8, + SCMI_IMX_LMM_NOTIFY = 0x9, + SCMI_IMX_LMM_RESET_REASON = 0xA, + SCMI_IMX_LMM_POWER_ON = 0xB, + SCMI_IMX_LMM_RESET_VECTOR_SET = 0xC, +}; + +struct scmi_imx_lmm_priv { + u32 nr_lmm; +}; + +#define SCMI_IMX_LMM_NR_LM_MASK GENMASK(5, 0) +#define SCMI_IMX_LMM_NR_MAX 16 +struct scmi_msg_imx_lmm_protocol_attributes { + __le32 attributes; +}; + +struct scmi_msg_imx_lmm_attributes_out { + __le32 lmid; + __le32 attributes; + __le32 state; + __le32 errstatus; + u8 name[LMM_MAX_NAME]; +}; + +struct scmi_imx_lmm_reset_vector_set_in { + __le32 lmid; + __le32 cpuid; + __le32 flags; /* reserved for future extension */ + __le32 resetvectorlow; + __le32 resetvectorhigh; +}; + +struct scmi_imx_lmm_shutdown_in { + __le32 lmid; +#define SCMI_IMX_LMM_SHUTDOWN_GRACEFUL BIT(0) + __le32 flags; +}; + +static int scmi_imx_lmm_validate_lmid(const struct scmi_protocol_handle *ph, u32 lmid) +{ + struct scmi_imx_lmm_priv *priv = ph->get_priv(ph); + + if (lmid >= priv->nr_lmm) + return -EINVAL; + + return 0; +} + +static int scmi_imx_lmm_attributes(const struct scmi_protocol_handle *ph, + u32 lmid, struct scmi_imx_lmm_info *info) +{ + struct scmi_msg_imx_lmm_attributes_out *out; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_LMM_ATTRIBUTES, sizeof(u32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(lmid, t->tx.buf); + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + out = t->rx.buf; + info->lmid = le32_to_cpu(out->lmid); + info->state = le32_to_cpu(out->state); + info->errstatus = le32_to_cpu(out->errstatus); + strscpy(info->name, out->name); + dev_dbg(ph->dev, "i.MX LMM: Logical Machine(%d), name: %s\n", + info->lmid, info->name); + } else { + dev_err(ph->dev, "i.MX LMM: Failed to get info of Logical Machine(%u)\n", lmid); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int +scmi_imx_lmm_power_boot(const struct scmi_protocol_handle *ph, u32 lmid, bool boot) +{ + struct scmi_xfer *t; + u8 msg_id; + int ret; + + ret = scmi_imx_lmm_validate_lmid(ph, lmid); + if (ret) + return ret; + + if (boot) + msg_id = SCMI_IMX_LMM_BOOT; + else + msg_id = SCMI_IMX_LMM_POWER_ON; + + ret = ph->xops->xfer_get_init(ph, msg_id, sizeof(u32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(lmid, t->tx.buf); + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_lmm_reset_vector_set(const struct scmi_protocol_handle *ph, + u32 lmid, u32 cpuid, u32 flags, u64 vector) +{ + struct scmi_imx_lmm_reset_vector_set_in *in; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_LMM_RESET_VECTOR_SET, sizeof(*in), + 0, &t); + if (ret) + return ret; + + in = t->tx.buf; + in->lmid = cpu_to_le32(lmid); + in->cpuid = cpu_to_le32(cpuid); + in->flags = cpu_to_le32(0); + in->resetvectorlow = cpu_to_le32(lower_32_bits(vector)); + in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector)); + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_lmm_shutdown(const struct scmi_protocol_handle *ph, u32 lmid, + u32 flags) +{ + struct scmi_imx_lmm_shutdown_in *in; + struct scmi_xfer *t; + int ret; + + ret = scmi_imx_lmm_validate_lmid(ph, lmid); + if (ret) + return ret; + + ret = ph->xops->xfer_get_init(ph, SCMI_IMX_LMM_SHUTDOWN, sizeof(*in), + 0, &t); + if (ret) + return ret; + + in = t->tx.buf; + in->lmid = cpu_to_le32(lmid); + if (flags & SCMI_IMX_LMM_SHUTDOWN_GRACEFUL) + in->flags = cpu_to_le32(SCMI_IMX_LMM_SHUTDOWN_GRACEFUL); + else + in->flags = cpu_to_le32(0); + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static const struct scmi_imx_lmm_proto_ops scmi_imx_lmm_proto_ops = { + .lmm_power_boot = scmi_imx_lmm_power_boot, + .lmm_info = scmi_imx_lmm_attributes, + .lmm_reset_vector_set = scmi_imx_lmm_reset_vector_set, + .lmm_shutdown = scmi_imx_lmm_shutdown, +}; + +static int scmi_imx_lmm_protocol_attributes_get(const struct scmi_protocol_handle *ph, + struct scmi_imx_lmm_priv *priv) +{ + struct scmi_msg_imx_lmm_protocol_attributes *attr; + struct scmi_xfer *t; + int ret; + + ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, + sizeof(*attr), &t); + if (ret) + return ret; + + attr = t->rx.buf; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + priv->nr_lmm = le32_get_bits(attr->attributes, SCMI_IMX_LMM_NR_LM_MASK); + if (priv->nr_lmm > SCMI_IMX_LMM_NR_MAX) { + dev_err(ph->dev, "i.MX LMM: %d:Exceed max supported Logical Machines\n", + priv->nr_lmm); + ret = -EINVAL; + } else { + dev_info(ph->dev, "i.MX LMM: %d Logical Machines\n", priv->nr_lmm); + } + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + +static int scmi_imx_lmm_protocol_init(const struct scmi_protocol_handle *ph) +{ + struct scmi_imx_lmm_priv *info; + u32 version; + int ret; + + ret = ph->xops->version_get(ph, &version); + if (ret) + return ret; + + dev_info(ph->dev, "NXP SM LMM Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + ret = scmi_imx_lmm_protocol_attributes_get(ph, info); + if (ret) + return ret; + + return ph->set_priv(ph, info, version); +} + +static const struct scmi_protocol scmi_imx_lmm = { + .id = SCMI_PROTOCOL_IMX_LMM, + .owner = THIS_MODULE, + .instance_init = &scmi_imx_lmm_protocol_init, + .ops = &scmi_imx_lmm_proto_ops, + .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, + .vendor_id = SCMI_IMX_VENDOR, + .sub_vendor_id = SCMI_IMX_SUBVENDOR, +}; +module_scmi_protocol(scmi_imx_lmm); + +MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_LMM) "-" SCMI_IMX_VENDOR); +MODULE_DESCRIPTION("i.MX SCMI LMM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx95.rst b/drivers/firmware/arm_scmi/vendors/imx/imx95.rst index b2dfd6c46ca2..4e246a78a042 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx95.rst +++ b/drivers/firmware/arm_scmi/vendors/imx/imx95.rst @@ -32,6 +32,518 @@ port, and deploy the SM on supported processors. The SM implements an interface compliant with the Arm SCMI Specification with additional vendor specific extensions. +System Control and Management Logical Machine Management Vendor Protocol +======================================================================== + +The SM adds the concept of logical machines (LMs). These are analogous to +VMs and each has its own instance of SCMI. All normal SCMI calls only apply +the LM running the calling agent. That includes boot, shutdown, reset, +suspend, wake, etc. If a caller makes the SCMI base call to get a list +of agents, it will only get those on that LM. Each LM is completely isolated +from the others. This is mandatory for these to operate independently. + +This protocol is intended to support boot, shutdown, and reset of other logical +machines (LM). It is usually used to allow one LM(e.g. OSPM) to manage +another LM which is usually an offload or accelerator engine. Notifications +from this protocol can also be used to manage a communication link to another +LM. The LMM protocol provides commands to: + +- Describe the protocol version. +- Discover implementation attributes. +- Discover all the LMs defined in the system. +- Boot a target LM. +- Shutdown a target LM (gracefully or forcibly). +- Reset a target LM (gracefully or forcibly). +- Wake a target LM from suspend. +- Suspend a target LM (gracefully). +- Read boot/shutdown/reset information for a target LM. +- Get notifications when a target LM boots or shuts down (e.g. LM 'X' requested + notification of LM 'Y' boots or shuts down, when LM 'Y' boots or shuts down, + SCMI firmware will send notification to LM 'X'). + +'Graceful' means asking LM itself to shutdown/reset/etc (e.g. sending +notification to Linux, Then Linux reboots or powers down itself). It is async +command that the SUCCESS of the command just means the command successfully +return, not means reboot/reset successfully finished. + +'Forceful' means the SM will force shutdown/reset/etc the LM. It is sync +command that the SUCCESS of the command means the LM has been successfully +shutdown/reset/etc. +If the commands not have Graceful/Forceful flag settings, such as WAKE, SUSEND, +it is a Graceful command. + +Commands: +_________ + +PROTOCOL_VERSION +~~~~~~~~~~~~~~~~ + +message_id: 0x0 +protocol_id: 0x80 +This command is mandatory. + ++---------------+--------------------------------------------------------------+ +|Return values | ++---------------+--------------------------------------------------------------+ +|Name |Description | ++---------------+--------------------------------------------------------------+ +|int32 status | See ARM SCMI Specification for status code definitions. | ++---------------+--------------------------------------------------------------+ +|uint32 version | For this revision of the specification, this value must be | +| | 0x10000. | ++---------------+--------------------------------------------------------------+ + +PROTOCOL_ATTRIBUTES +~~~~~~~~~~~~~~~~~~~ + +message_id: 0x1 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status | See ARM SCMI Specification for status code definitions. | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |Protocol attributes: | +| |Bits[31:5] Reserved, must be zero. | +| |Bits[4:0] Number of Logical Machines | +| |Note that due to both hardware limitations and reset reason| +| |field limitations, the max number of LM is 16. The minimum | +| |is 1. | ++------------------+-----------------------------------------------------------+ + +PROTOCOL_MESSAGE_ATTRIBUTES +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x2 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: in case the message is implemented and available | +| |to use. | +| |NOT_FOUND: if the message identified by message_id is | +| |invalid or not implemented | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |Flags that are associated with a specific command in the | +| |protocol. For all commands in this protocol, this | +| |parameter has a value of 0 | ++------------------+-----------------------------------------------------------+ + +LMM_ATTRIBUTES +~~~~~~~~~~~~~~ + +message_id: 0x3 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if valid attributes are returned. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |DENIED: if the agent does not have permission to get info | +| |for the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |Identifier of the LM whose identification is requested. | +| |This field is: Populated with the lmid of the calling | +| |agent, when the lmid parameter passed via the command is | +| |0xFFFFFFFF. Identical to the lmid field passed via the | +| |calling parameters, in all other cases | ++------------------+-----------------------------------------------------------+ +|uint32 attributes | Bits[31:0] reserved. must be zero | ++------------------+-----------------------------------------------------------+ +|uint32 state | Current state of the LM | ++------------------+-----------------------------------------------------------+ +|uint32 errStatus | Last error status recorded | ++------------------+-----------------------------------------------------------+ +|char name[16] | A NULL terminated ASCII string with the LM name, of up | +| | to 16 bytes | ++------------------+-----------------------------------------------------------+ + +LMM_BOOT +~~~~~~~~ + +message_id: 0x4 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if LM boots successfully started. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if lmid is same as the caller. | +| |DENIED: if the agent does not have permission to manage the| +| |the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ + +LMM_RESET +~~~~~~~~~ + +message_id: 0x5 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|uint32 flags |Reset flags: | +| |Bits[31:1] Reserved, must be zero. | +| |Bit[0] Graceful request: | +| |Set to 1 if the request is a graceful request. | +| |Set to 0 if the request is a forceful request. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: The LMM RESET command finished successfully in | +| |graceful reset or LM successfully resets in forceful reset.| +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if lmid is same as the caller. | +| |DENIED: if the agent does not have permission to manage the| +| |the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ + +LMM_SHUTDOWN +~~~~~~~~~~~~ + +message_id: 0x6 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|uint32 flags |Reset flags: | +| |Bits[31:1] Reserved, must be zero. | +| |Bit[0] Graceful request: | +| |Set to 1 if the request is a graceful request. | +| |Set to 0 if the request is a forceful request. | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: The LMM shutdown command finished successfully in | +| |graceful request or LM successfully shutdown in forceful | +| |request. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if lmid is same as the caller. | +| |DENIED: if the agent does not have permission to manage the| +| |the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ + +LMM_WAKE +~~~~~~~~ + +message_id: 0x7 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if LM wake command successfully returns. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if lmid is same as the caller. | +| |DENIED: if the agent does not have permission to manage the| +| |the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ + +LMM_SUSPEND +~~~~~~~~~~~ + +message_id: 0x8 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if LM suspend command successfully returns. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if lmid is same as the caller. | +| |DENIED: if the agent does not have permission to manage the| +| |the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ + +LMM_NOTIFY +~~~~~~~~~~ + +message_id: 0x9 +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|uint32 flags |Notification flags: | +| |Bits[31:3] Reserved, must be zero. | +| |Bit[3] Wake (resume) notification: | +| |Set to 1 to send notification. | +| |Set to 0 if no notification. | +| |Bit[2] Suspend (sleep) notification: | +| |Set to 1 to send notification. | +| |Set to 0 if no notification. | +| |Bit[1] Shutdown (off) notification: | +| |Set to 1 to send notification. | +| |Set to 0 if no notification. | +| |Bit[0] Boot (on) notification: | +| |Set to 1 to send notification. | +| |Set to 0 if no notification | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the notification state successfully updated. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if input attributes flag specifies | +| |unsupported or invalid configurations. | +| |DENIED: if the agent does not have permission to request | +| |the notification. | ++------------------+-----------------------------------------------------------+ + +LMM_RESET_REASON +~~~~~~~~~~~~~~~~ + +message_id: 0xA +protocol_id: 0x80 +This command is mandatory. + +This command is to return the reset reason that caused the last reset, such as +POR, WDOG, JTAG and etc. + ++---------------------+--------------------------------------------------------+ +|Parameters | ++---------------------+--------------------------------------------------------+ +|Name |Description | ++---------------------+--------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++---------------------+--------------------------------------------------------+ +|Return values | ++---------------------+--------------------------------------------------------+ +|Name |Description | ++---------------------+--------------------------------------------------------+ +|int32 status |SUCCESS: if the reset reason of the LM successfully | +| |updated. | +| |NOT_FOUND: if lmid not points to a valid logical machine| +| |DENIED: if the agent does not have permission to request| +| |the reset reason. | ++---------------------+--------------------------------------------------------+ +|uint32 bootflags |Boot reason flags. This parameter has the format: | +| |Bits[31] Valid. | +| |Set to 1 if the entire reason is valid. | +| |Set to 0 if the entire reason is not valid. | +| |Bits[30:29] Reserved, must be zero. | +| |Bit[28] Valid origin: | +| |Set to 1 if the origin field is valid. | +| |Set to 0 if the origin field is not valid. | +| |Bits[27:24] Origin. | +| |Logical Machine(LM) ID that causes the BOOT of this LM | +| |Bit[23] Valid err ID: | +| |Set to 1 if the error ID field is valid. | +| |Set to 0 if the error ID field is not valid. | +| |Bits[22:8] Error ID(Agent ID of the system). | +| |Bit[7:0] Reason(WDOG, POR, FCCU and etc): | +| |See the SRESR register description in the System | +| |Reset Controller (SRC) section in SoC reference mannual | +| |One reason maps to BIT(reason) in SRESR | ++---------------------+--------------------------------------------------------+ +|uint32 shutdownflags |Shutdown reason flags. This parameter has the format: | +| |Bits[31] Valid. | +| |Set to 1 if the entire reason is valid. | +| |Set to 0 if the entire reason is not valid. | +| |Bits[30:29] Number of valid extended info words. | +| |Bit[28] Valid origin: | +| |Set to 1 if the origin field is valid. | +| |Set to 0 if the origin field is not valid. | +| |Bits[27:24] Origin. | +| |Logical Machine(LM) ID that causes the BOOT of this LM | +| |Bit[23] Valid err ID: | +| |Set to 1 if the error ID field is valid. | +| |Set to 0 if the error ID field is not valid. | +| |Bits[22:8] Error ID(Agent ID of the System). | +| |Bit[7:0] Reason | +| |See the SRESR register description in the System | +| |Reset Controller (SRC) section in SoC reference mannual | +| |One reason maps to BIT(reason) in SRESR | ++---------------------+--------------------------------------------------------+ +|uint32 extinfo[3] |Array of extended info words(e.g. fault pc) | ++---------------------+--------------------------------------------------------+ + +LMM_POWER_ON +~~~~~~~~~~~~ + +message_id: 0xB +protocol_id: 0x80 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if LM successfully powers on. | +| |NOT_FOUND: if lmid not points to a valid logical machine. | +| |INVALID_PARAMETERS: if lmid is same as the caller. | +| |DENIED: if the agent does not have permission to manage the| +| |the LM specified by lmid. | ++------------------+-----------------------------------------------------------+ + +LMM_RESET_VECTOR_SET +~~~~~~~~~~~~~~~~~~~~ + +message_id: 0xC +protocol_id: 0x80 +This command is mandatory. + ++-----------------------+------------------------------------------------------+ +|Parameters | ++-----------------------+------------------------------------------------------+ +|Name |Description | ++-----------------------+------------------------------------------------------+ +|uint32 lmid |ID of the Logical Machine | ++-----------------------+------------------------------------------------------+ +|uint32 cpuid |ID of the CPU inside the LM | ++-----------------------+------------------------------------------------------+ +|uint32 flags |Reset vector flags | +| |Bits[31:0] Reserved, must be zero. | ++-----------------------+------------------------------------------------------+ +|uint32 resetVectorLow |Lower vector | ++-----------------------+------------------------------------------------------+ +|uint32 resetVectorHigh |Higher vector | ++-----------------------+------------------------------------------------------+ +|Return values | ++-----------------------+------------------------------------------------------+ +|Name |Description | ++-----------------------+------------------------------------------------------+ +|int32 status |SUCCESS: If reset vector is set successfully. | +| |NOT_FOUND: if lmid not points to a valid logical | +| |machine, or cpuId is not valid. | +| |INVALID_PARAMETERS: if reset vector is invalid. | +| |DENIED: if the agent does not have permission to set | +| |the reset vector for the CPU in the LM. | ++-----------------------+------------------------------------------------------+ + +NEGOTIATE_PROTOCOL_VERSION +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x10 +protocol_id: 0x80 +This command is mandatory. + ++--------------------+---------------------------------------------------------+ +|Parameters | ++--------------------+---------------------------------------------------------+ +|Name |Description | ++--------------------+---------------------------------------------------------+ +|uint32 version |The negotiated protocol version the agent intends to use | ++--------------------+---------------------------------------------------------+ +|Return values | ++--------------------+---------------------------------------------------------+ +|Name |Description | ++--------------------+---------------------------------------------------------+ +|int32 status |SUCCESS: if the negotiated protocol version is supported | +| |by the platform. All commands, responses, and | +| |notifications post successful return of this command must| +| |comply with the negotiated version. | +| |NOT_SUPPORTED: if the protocol version is not supported. | ++--------------------+---------------------------------------------------------+ + +Notifications +_____________ + +LMM_EVENT +~~~~~~~~~ + +message_id: 0x0 +protocol_id: 0x80 + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 lmid |Identifier for the LM that caused the transition. | ++------------------+-----------------------------------------------------------+ +|uint32 eventlm |Identifier of the LM this event refers to. | ++------------------+-----------------------------------------------------------+ +|uint32 flags |LM events: | +| |Bits[31:3] Reserved, must be zero. | +| |Bit[3] Wake (resume) event: | +| |1 LM has awakened. | +| |0 not a wake event. | +| |Bit[2] Suspend (sleep) event: | +| |1 LM has suspended. | +| |0 not a suspend event. | +| |Bit[1] Shutdown (off) event: | +| |1 LM has shutdown. | +| |0 not a shutdown event. | +| |Bit[0] Boot (on) event: | +| |1 LM has booted. | +| |0 not a boot event. | ++------------------+-----------------------------------------------------------+ + SCMI_BBM: System Control and Management BBM Vendor Protocol ============================================================== @@ -436,6 +948,322 @@ protocol_id: 0x81 | |0 no button change detected. | +------------------+-----------------------------------------------------------+ +System Control and Management CPU Vendor Protocol +================================================= + +This protocol allows an agent to start or stop a CPU. It is used to manage +auxiliary CPUs in a target LM (e.g. additional cores in an AP cluster or +Cortex-M cores). +Note: + - For cores in AP cluster, PSCI should be used and PSCI firmware will use CPU + protocol to handle them. For cores in non-AP cluster, Operating System(e.g. + Linux OS) could use CPU protocols to control Cortex-M7 cores. + - CPU indicates the core and its auxiliary peripherals(e.g. TCM) inside + i.MX SoC + +There are cases where giving an agent full control of a CPU via the CPU +protocol is not desired. The LMM protocol is more restricted to just boot, +shutdown, etc. So an agent might boot another logical machine but not be +able to directly mess the state of its CPUs. Its also the reason there is an +LMM power on command even though that could have been done through the +power protocol. + +The CPU protocol provides commands to: + +- Describe the protocol version. +- Discover implementation attributes. +- Discover the CPUs defined in the system. +- Start a CPU. +- Stop a CPU. +- Set the boot and resume addresses for a CPU. +- Set the sleep mode of a CPU. +- Configure wake-up sources for a CPU. +- Configure power domain reactions (LPM mode and retention mask) for a CPU. +- The CPU IDs can be found in the CPU section of the SoC DEVICE: SM Device + Interface. They can also be found in the SoC RM. See the CPU Mode Control + (CMC) list in General Power Controller (GPC) section. + +CPU settings are not aggregated and setting their state is normally exclusive +to one client. + +Commands: +_________ + +PROTOCOL_VERSION +~~~~~~~~~~~~~~~~ + +message_id: 0x0 +protocol_id: 0x82 +This command is mandatory. + ++---------------+--------------------------------------------------------------+ +|Return values | ++---------------+--------------------------------------------------------------+ +|Name |Description | ++---------------+--------------------------------------------------------------+ +|int32 status | See ARM SCMI Specification for status code definitions. | ++---------------+--------------------------------------------------------------+ +|uint32 version | For this revision of the specification, this value must be | +| | 0x10000. | ++---------------+--------------------------------------------------------------+ + +PROTOCOL_ATTRIBUTES +~~~~~~~~~~~~~~~~~~~ + +message_id: 0x1 +protocol_id: 0x82 +This command is mandatory. + ++---------------+--------------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status | See ARM SCMI Specification for status code definitions. | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |Protocol attributes: | +| |Bits[31:16] Reserved, must be zero. | +| |Bits[15:0] Number of CPUs | ++------------------+-----------------------------------------------------------+ + +PROTOCOL_MESSAGE_ATTRIBUTES +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x2 +protocol_id: 0x82 +This command is mandatory. + ++---------------+--------------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: in case the message is implemented and available | +| |to use. | +| |NOT_FOUND: if the message identified by message_id is | +| |invalid or not implemented | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |Flags that are associated with a specific command in the | +| |protocol. For all commands in this protocol, this | +| |parameter has a value of 0 | ++------------------+-----------------------------------------------------------+ + +CPU_ATTRIBUTES +~~~~~~~~~~~~~~ + +message_id: 0x4 +protocol_id: 0x82 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 cpuid |Identifier for the CPU | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if valid attributes are returned successfully. | +| |NOT_FOUND: if the cpuid is not valid. | ++------------------+-----------------------------------------------------------+ +|uint32 attributes |Bits[31:0] Reserved, must be zero | ++------------------+-----------------------------------------------------------+ +|char name[16] |NULL terminated ASCII string with CPU name up to 16 bytes | ++------------------+-----------------------------------------------------------+ + +CPU_START +~~~~~~~~~ + +message_id: 0x4 +protocol_id: 0x82 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 cpuid |Identifier for the CPU | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the cpu is started successfully. | +| |NOT_FOUND: if cpuid is not valid. | +| |DENIED: the calling agent is not allowed to start this CPU.| ++------------------+-----------------------------------------------------------+ + +CPU_STOP +~~~~~~~~ + +message_id: 0x5 +protocol_id: 0x82 +This command is mandatory. + ++------------------+-----------------------------------------------------------+ +|Parameters | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|uint32 cpuid |Identifier for the CPU | ++------------------+-----------------------------------------------------------+ +|Return values | ++------------------+-----------------------------------------------------------+ +|Name |Description | ++------------------+-----------------------------------------------------------+ +|int32 status |SUCCESS: if the cpu is started successfully. | +| |NOT_FOUND: if cpuid is not valid. | +| |DENIED: the calling agent is not allowed to stop this CPU. | ++------------------+-----------------------------------------------------------+ + +CPU_RESET_VECTOR_SET +~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x6 +protocol_id: 0x82 +This command is mandatory. + ++----------------------+-------------------------------------------------------+ +|Parameters | ++----------------------+-------------------------------------------------------+ +|Name |Description | ++----------------------+-------------------------------------------------------+ +|uint32 cpuid |Identifier for the CPU | ++----------------------+-------------------------------------------------------+ +|uint32 flags |Reset vector flags: | +| |Bit[31] Resume flag. | +| |Set to 1 to update the reset vector used on resume. | +| |Bit[30] Boot flag. | +| |Set to 1 to update the reset vector used for boot. | +| |Bits[29:1] Reserved, must be zero. | +| |Bit[0] Table flag. | +| |Set to 1 if vector is the vector table base address. | ++----------------------+-------------------------------------------------------+ +|uint32 resetVectorLow |Lower vector: | +| |If bit[0] of flags is 0, the lower 32 bits of the | +| |physical address where the CPU should execute from on | +| |reset. If bit[0] of flags is 1, the lower 32 bits of | +| |the vector table base address | ++----------------------+-------------------------------------------------------+ +|uint32 resetVectorhigh|Upper vector: | +| |If bit[0] of flags is 0, the upper 32 bits of the | +| |physical address where the CPU should execute from on | +| |reset. If bit[0] of flags is 1, the upper 32 bits of | +| |the vector table base address | ++----------------------+-------------------------------------------------------+ +|Return values | ++----------------------+-------------------------------------------------------+ +|Name |Description | ++----------------------+-------------------------------------------------------+ +|int32 status |SUCCESS: if the CPU reset vector is set successfully. | +| |NOT_FOUND: if cpuId does not point to a valid CPU. | +| |INVALID_PARAMETERS: the requested vector type is not | +| |supported by this CPU. | +| |DENIED: the calling agent is not allowed to set the | +| |reset vector of this CPU | ++----------------------+-------------------------------------------------------+ + +CPU_SLEEP_MODE_SET +~~~~~~~~~~~~~~~~~~ + +message_id: 0x7 +protocol_id: 0x82 +This command is mandatory. + ++----------------------+-------------------------------------------------------+ +|Parameters | ++----------------------+-------------------------------------------------------+ +|Name |Description | ++----------------------+-------------------------------------------------------+ +|uint32 cpuid |Identifier for the CPU | ++----------------------+-------------------------------------------------------+ +|uint32 flags |Sleep mode flags: | +| |Bits[31:1] Reserved, must be zero. | +| |Bit[0] IRQ mux: | +| |If set to 1 the wakeup mux source is the GIC, else if 0| +| |then the GPC | ++----------------------+-------------------------------------------------------+ +|uint32 sleepmode |target sleep mode. When CPU runs into WFI, the GPC mode| +| |will be triggered to be in below modes: | +| |RUN: (0) | +| |WAIT: (1) | +| |STOP: (2) | +| |SUSPEND: (3) | ++----------------------+-------------------------------------------------------+ +|Return values | ++----------------------+-------------------------------------------------------+ +|Name |Description | ++----------------------+-------------------------------------------------------+ +|int32 status |SUCCESS: if the CPU sleep mode is set successfully. | +| |NOT_FOUND: if cpuId does not point to a valid CPU. | +| |INVALID_PARAMETERS: the sleepmode or flags is invalid. | +| |DENIED: the calling agent is not allowed to configure | +| |the CPU | ++----------------------+-------------------------------------------------------+ + +CPU_INFO_GET +~~~~~~~~~~~~ + +message_id: 0xC +protocol_id: 0x82 +This command is mandatory. + ++----------------------+-------------------------------------------------------+ +|Parameters | ++----------------------+-------------------------------------------------------+ +|Name |Description | ++----------------------+-------------------------------------------------------+ +|uint32 cpuid |Identifier for the CPU | ++----------------------+-------------------------------------------------------+ +|Return values | ++----------------------+-------------------------------------------------------+ +|Name |Description | ++----------------------+-------------------------------------------------------+ +|int32 status |SUCCESS: if valid attributes are returned successfully.| +| |NOT_FOUND: if the cpuid is not valid. | ++----------------------+-------------------------------------------------------+ +|uint32 runmode |Run mode for the CPU | +| |RUN(0):cpu started | +| |HOLD(1):cpu powered up and reset asserted | +| |STOP(2):cpu reseted and hold cpu | +| |SUSPEND(3):in cpuidle state | ++----------------------+-------------------------------------------------------+ +|uint32 sleepmode |Sleep mode for the CPU, see CPU_SLEEP_MODE_SET | ++----------------------+-------------------------------------------------------+ +|uint32 resetvectorlow |Reset vector low 32 bits for the CPU | ++----------------------+-------------------------------------------------------+ +|uint32 resetvecothigh |Reset vector high 32 bits for the CPU | ++----------------------+-------------------------------------------------------+ + +NEGOTIATE_PROTOCOL_VERSION +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +message_id: 0x10 +protocol_id: 0x82 +This command is mandatory. + ++--------------------+---------------------------------------------------------+ +|Parameters | ++--------------------+---------------------------------------------------------+ +|Name |Description | ++--------------------+---------------------------------------------------------+ +|uint32 version |The negotiated protocol version the agent intends to use | ++--------------------+---------------------------------------------------------+ +|Return values | ++--------------------+---------------------------------------------------------+ +|Name |Description | ++--------------------+---------------------------------------------------------+ +|int32 status |SUCCESS: if the negotiated protocol version is supported | +| |by the platform. All commands, responses, and | +| |notifications post successful return of this command must| +| |comply with the negotiated version. | +| |NOT_SUPPORTED: if the protocol version is not supported. | ++--------------------+---------------------------------------------------------+ + SCMI_MISC: System Control and Management MISC Vendor Protocol ================================================================ diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 5fe61b9ab5f9..db8c5c03d3a2 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -281,6 +281,30 @@ config EFI_EMBEDDED_FIRMWARE bool select CRYPTO_LIB_SHA256 +config EFI_SBAT + def_bool y if EFI_SBAT_FILE!="" + +config EFI_SBAT_FILE + string "Embedded SBAT section file path" + depends on EFI_ZBOOT + help + SBAT section provides a way to improve SecureBoot revocations of UEFI + binaries by introducing a generation-based mechanism. With SBAT, older + UEFI binaries can be prevented from booting by bumping the minimal + required generation for the specific component in the bootloader. + + Note: SBAT information is distribution specific, i.e. the owner of the + signing SecureBoot certificate must define the SBAT policy. Linux + kernel upstream does not define SBAT components and their generations. + + See https://github.com/rhboot/shim/blob/main/SBAT.md for the additional + details. + + Specify a file with SBAT data which is going to be embedded as '.sbat' + section into the kernel. + + If unsure, leave blank. + endmenu config UEFI_CPER diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot index 48842b5c106b..92e3c73502ba 100644 --- a/drivers/firmware/efi/libstub/Makefile.zboot +++ b/drivers/firmware/efi/libstub/Makefile.zboot @@ -44,6 +44,10 @@ AFLAGS_zboot-header.o += -DMACHINE_TYPE=IMAGE_FILE_MACHINE_$(EFI_ZBOOT_MACH_TYPE $(obj)/zboot-header.o: $(srctree)/drivers/firmware/efi/libstub/zboot-header.S FORCE $(call if_changed_rule,as_o_S) +ifneq ($(CONFIG_EFI_SBAT_FILE),) +$(obj)/zboot-header.o: $(CONFIG_EFI_SBAT_FILE) +endif + ZBOOT_DEPS := $(obj)/zboot-header.o $(objtree)/drivers/firmware/efi/libstub/lib.a LDFLAGS_vmlinuz.efi.elf := -T $(srctree)/drivers/firmware/efi/libstub/zboot.lds diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index fd6dc790c5a8..7aa2f9ad2935 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -601,6 +601,7 @@ efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, * @image: EFI loaded image protocol * @soft_limit: preferred address for loading the initrd * @hard_limit: upper limit address for loading the initrd + * @out: pointer to store the address of the initrd table * * Return: status code */ diff --git a/drivers/firmware/efi/libstub/zboot-header.S b/drivers/firmware/efi/libstub/zboot-header.S index fb676ded47fa..b6431edd0fc9 100644 --- a/drivers/firmware/efi/libstub/zboot-header.S +++ b/drivers/firmware/efi/libstub/zboot-header.S @@ -4,17 +4,17 @@ #ifdef CONFIG_64BIT .set .Lextra_characteristics, 0x0 - .set .Lpe_opt_magic, PE_OPT_MAGIC_PE32PLUS + .set .Lpe_opt_magic, IMAGE_NT_OPTIONAL_HDR64_MAGIC #else .set .Lextra_characteristics, IMAGE_FILE_32BIT_MACHINE - .set .Lpe_opt_magic, PE_OPT_MAGIC_PE32 + .set .Lpe_opt_magic, IMAGE_NT_OPTIONAL_HDR32_MAGIC #endif .section ".head", "a" .globl __efistub_efi_zboot_header __efistub_efi_zboot_header: .Ldoshdr: - .long MZ_MAGIC + .long IMAGE_DOS_SIGNATURE .ascii "zimg" // image type .long __efistub__gzdata_start - .Ldoshdr // payload offset .long __efistub__gzdata_size - ZBOOT_SIZE_LEN // payload size @@ -25,7 +25,7 @@ __efistub_efi_zboot_header: .long .Lpehdr - .Ldoshdr // PE header offset .Lpehdr: - .long PE_MAGIC + .long IMAGE_NT_SIGNATURE .short MACHINE_TYPE .short .Lsection_count .long 0 @@ -63,7 +63,7 @@ __efistub_efi_zboot_header: .long .Lefi_header_end - .Ldoshdr .long 0 .short IMAGE_SUBSYSTEM_EFI_APPLICATION - .short IMAGE_DLL_CHARACTERISTICS_NX_COMPAT + .short IMAGE_DLLCHARACTERISTICS_NX_COMPAT #ifdef CONFIG_64BIT .quad 0, 0, 0, 0 #else @@ -123,11 +123,29 @@ __efistub_efi_zboot_header: IMAGE_SCN_MEM_READ | \ IMAGE_SCN_MEM_EXECUTE +#ifdef CONFIG_EFI_SBAT + .ascii ".sbat\0\0\0" + .long __sbat_size + .long _sbat - .Ldoshdr + .long __sbat_size + .long _sbat - .Ldoshdr + + .long 0, 0 + .short 0, 0 + .long IMAGE_SCN_CNT_INITIALIZED_DATA | \ + IMAGE_SCN_MEM_READ | \ + IMAGE_SCN_MEM_DISCARDABLE + + .pushsection ".sbat", "a", @progbits + .incbin CONFIG_EFI_SBAT_FILE + .popsection +#endif + .ascii ".data\0\0\0" .long __data_size - .long _etext - .Ldoshdr + .long _data - .Ldoshdr .long __data_rawsize - .long _etext - .Ldoshdr + .long _data - .Ldoshdr .long 0, 0 .short 0, 0 diff --git a/drivers/firmware/efi/libstub/zboot.lds b/drivers/firmware/efi/libstub/zboot.lds index 9ecc57ff5b45..c3a166675450 100644 --- a/drivers/firmware/efi/libstub/zboot.lds +++ b/drivers/firmware/efi/libstub/zboot.lds @@ -29,7 +29,17 @@ SECTIONS . = _etext; } +#ifdef CONFIG_EFI_SBAT + .sbat : ALIGN(4096) { + _sbat = .; + *(.sbat) + _esbat = ALIGN(4096); + . = _esbat; + } +#endif + .data : ALIGN(4096) { + _data = .; *(.data* .init.data*) _edata = ALIGN(512); . = _edata; @@ -52,3 +62,4 @@ PROVIDE(__efistub__gzdata_size = PROVIDE(__data_rawsize = ABSOLUTE(_edata - _etext)); PROVIDE(__data_size = ABSOLUTE(_end - _etext)); +PROVIDE(__sbat_size = ABSOLUTE(_esbat - _sbat)); diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c index 34109fd86c55..f1c04d7cfd71 100644 --- a/drivers/firmware/efi/memmap.c +++ b/drivers/firmware/efi/memmap.c @@ -43,7 +43,8 @@ int __init __efi_memmap_init(struct efi_memory_map_data *data) map.map = early_memremap(phys_map, data->size); if (!map.map) { - pr_err("Could not map the memory map!\n"); + pr_err("Could not map the memory map! phys_map=%pa, size=0x%lx\n", + &phys_map, data->size); return -ENOMEM; } diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c index 9e2628728aad..77b5f7ac3e20 100644 --- a/drivers/firmware/efi/test/efi_test.c +++ b/drivers/firmware/efi/test/efi_test.c @@ -361,6 +361,10 @@ static long efi_runtime_get_waketime(unsigned long arg) getwakeuptime.enabled)) return -EFAULT; + if (getwakeuptime.pending && put_user(pending, + getwakeuptime.pending)) + return -EFAULT; + if (getwakeuptime.time) { if (copy_to_user(getwakeuptime.time, &efi_time, sizeof(efi_time_t))) diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index c964f4924359..127ad752acf8 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig @@ -23,6 +23,28 @@ config IMX_SCU This driver manages the IPC interface between host CPU and the SCU firmware running on M4. +config IMX_SCMI_CPU_DRV + tristate "IMX SCMI CPU Protocol driver" + depends on ARCH_MXC || COMPILE_TEST + default y if ARCH_MXC + help + The System Controller Management Interface firmware (SCMI FW) is + a low-level system function which runs on a dedicated Cortex-M + core that could provide cpu management features. + + This driver can also be built as a module. + +config IMX_SCMI_LMM_DRV + tristate "IMX SCMI LMM Protocol driver" + depends on ARCH_MXC || COMPILE_TEST + default y if ARCH_MXC + help + The System Controller Management Interface firmware (SCMI FW) is + a low-level system function which runs on a dedicated Cortex-M + core that could provide Logical Machine management features. + + This driver can also be built as a module. + config IMX_SCMI_MISC_DRV tristate "IMX SCMI MISC Protocol driver" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 8d046c341be8..3bbaffa6e347 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_IMX_DSP) += imx-dsp.o obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o rm.o imx-scu-soc.o +obj-${CONFIG_IMX_SCMI_CPU_DRV} += sm-cpu.o obj-${CONFIG_IMX_SCMI_MISC_DRV} += sm-misc.o +obj-${CONFIG_IMX_SCMI_LMM_DRV} += sm-lmm.o diff --git a/drivers/firmware/imx/sm-cpu.c b/drivers/firmware/imx/sm-cpu.c new file mode 100644 index 000000000000..091b014f739f --- /dev/null +++ b/drivers/firmware/imx/sm-cpu.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 NXP + */ + +#include <linux/firmware/imx/sm.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/scmi_protocol.h> +#include <linux/scmi_imx_protocol.h> + +static const struct scmi_imx_cpu_proto_ops *imx_cpu_ops; +static struct scmi_protocol_handle *ph; + +int scmi_imx_cpu_reset_vector_set(u32 cpuid, u64 vector, bool start, bool boot, + bool resume) +{ + if (!ph) + return -EPROBE_DEFER; + + return imx_cpu_ops->cpu_reset_vector_set(ph, cpuid, vector, start, + boot, resume); +} +EXPORT_SYMBOL(scmi_imx_cpu_reset_vector_set); + +int scmi_imx_cpu_start(u32 cpuid, bool start) +{ + if (!ph) + return -EPROBE_DEFER; + + if (start) + return imx_cpu_ops->cpu_start(ph, cpuid, true); + + return imx_cpu_ops->cpu_start(ph, cpuid, false); +}; +EXPORT_SYMBOL(scmi_imx_cpu_start); + +int scmi_imx_cpu_started(u32 cpuid, bool *started) +{ + if (!ph) + return -EPROBE_DEFER; + + if (!started) + return -EINVAL; + + return imx_cpu_ops->cpu_started(ph, cpuid, started); +}; +EXPORT_SYMBOL(scmi_imx_cpu_started); + +static int scmi_imx_cpu_probe(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + + if (!handle) + return -ENODEV; + + if (imx_cpu_ops) { + dev_err(&sdev->dev, "sm cpu already initialized\n"); + return -EEXIST; + } + + imx_cpu_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_CPU, &ph); + if (IS_ERR(imx_cpu_ops)) + return PTR_ERR(imx_cpu_ops); + + return 0; +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_IMX_CPU, "imx-cpu" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_imx_cpu_driver = { + .name = "scmi-imx-cpu", + .probe = scmi_imx_cpu_probe, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_imx_cpu_driver); + +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); +MODULE_DESCRIPTION("IMX SM CPU driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/imx/sm-lmm.c b/drivers/firmware/imx/sm-lmm.c new file mode 100644 index 000000000000..6807bf563c03 --- /dev/null +++ b/drivers/firmware/imx/sm-lmm.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2025 NXP + */ + +#include <linux/firmware/imx/sm.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/scmi_protocol.h> +#include <linux/scmi_imx_protocol.h> + +static const struct scmi_imx_lmm_proto_ops *imx_lmm_ops; +static struct scmi_protocol_handle *ph; + +int scmi_imx_lmm_info(u32 lmid, struct scmi_imx_lmm_info *info) +{ + if (!ph) + return -EPROBE_DEFER; + + if (!info) + return -EINVAL; + + return imx_lmm_ops->lmm_info(ph, lmid, info); +}; +EXPORT_SYMBOL(scmi_imx_lmm_info); + +int scmi_imx_lmm_reset_vector_set(u32 lmid, u32 cpuid, u32 flags, u64 vector) +{ + if (!ph) + return -EPROBE_DEFER; + + return imx_lmm_ops->lmm_reset_vector_set(ph, lmid, cpuid, flags, vector); +} +EXPORT_SYMBOL(scmi_imx_lmm_reset_vector_set); + +int scmi_imx_lmm_operation(u32 lmid, enum scmi_imx_lmm_op op, u32 flags) +{ + if (!ph) + return -EPROBE_DEFER; + + switch (op) { + case SCMI_IMX_LMM_BOOT: + return imx_lmm_ops->lmm_power_boot(ph, lmid, true); + case SCMI_IMX_LMM_POWER_ON: + return imx_lmm_ops->lmm_power_boot(ph, lmid, false); + case SCMI_IMX_LMM_SHUTDOWN: + return imx_lmm_ops->lmm_shutdown(ph, lmid, flags); + default: + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL(scmi_imx_lmm_operation); + +static int scmi_imx_lmm_probe(struct scmi_device *sdev) +{ + const struct scmi_handle *handle = sdev->handle; + + if (!handle) + return -ENODEV; + + if (imx_lmm_ops) { + dev_err(&sdev->dev, "lmm already initialized\n"); + return -EEXIST; + } + + imx_lmm_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_LMM, &ph); + if (IS_ERR(imx_lmm_ops)) + return PTR_ERR(imx_lmm_ops); + + return 0; +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_IMX_LMM, "imx-lmm" }, + { }, +}; +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_imx_lmm_driver = { + .name = "scmi-imx-lmm", + .probe = scmi_imx_lmm_probe, + .id_table = scmi_id_table, +}; +module_scmi_driver(scmi_imx_lmm_driver); + +MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); +MODULE_DESCRIPTION("IMX SM LMM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index fc4d67e4c4a6..f63b716be5b0 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1986,7 +1986,10 @@ EXPORT_SYMBOL_GPL(qcom_scm_qseecom_app_send); */ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "asus,vivobook-s15" }, + { .compatible = "asus,zenbook-a14-ux3407qa" }, + { .compatible = "asus,zenbook-a14-ux3407ra" }, { .compatible = "dell,xps13-9345" }, + { .compatible = "hp,elitebook-ultra-g1q" }, { .compatible = "hp,omnibook-x14" }, { .compatible = "huawei,gaokun3" }, { .compatible = "lenovo,flex-5g" }, diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index 097369d38b84..3133d826f5fa 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -44,8 +44,11 @@ enum qcom_scm_arg_types { /** * struct qcom_scm_desc + * @svc: Service identifier + * @cmd: Command identifier * @arginfo: Metadata describing the arguments in args[] * @args: The array of arguments for the secure syscall + * @owner: Owner identifier */ struct qcom_scm_desc { u32 svc; diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index 92b365178235..94196ad87105 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -79,6 +79,7 @@ static const char *const qcom_tzmem_blacklist[] = { "qcom,sc8180x", "qcom,sdm670", /* failure in GPU firmware loading */ "qcom,sdm845", /* reset in rmtfs memory assignment */ + "qcom,sm7150", /* reset in rmtfs memory assignment */ "qcom,sm8150", /* reset in rmtfs memory assignment */ NULL }; diff --git a/drivers/firmware/samsung/exynos-acpm-pmic.c b/drivers/firmware/samsung/exynos-acpm-pmic.c index 85e90d236da2..39b33a356ebd 100644 --- a/drivers/firmware/samsung/exynos-acpm-pmic.c +++ b/drivers/firmware/samsung/exynos-acpm-pmic.c @@ -43,13 +43,13 @@ static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i) return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK; } -static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, +static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, unsigned int acpm_chan_id) { xfer->txd = cmd; xfer->rxd = cmd; - xfer->txlen = sizeof(cmd); - xfer->rxlen = sizeof(cmd); + xfer->txlen = cmdlen; + xfer->rxlen = cmdlen; xfer->acpm_chan_id = acpm_chan_id; } @@ -71,7 +71,7 @@ int acpm_pmic_read_reg(const struct acpm_handle *handle, int ret; acpm_pmic_init_read_cmd(cmd, type, reg, chan); - acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -104,7 +104,7 @@ int acpm_pmic_bulk_read(const struct acpm_handle *handle, return -EINVAL; acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count); - acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -144,7 +144,7 @@ int acpm_pmic_write_reg(const struct acpm_handle *handle, int ret; acpm_pmic_init_write_cmd(cmd, type, reg, chan, value); - acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -184,7 +184,7 @@ int acpm_pmic_bulk_write(const struct acpm_handle *handle, return -EINVAL; acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf); - acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) @@ -214,7 +214,7 @@ int acpm_pmic_update_reg(const struct acpm_handle *handle, int ret; acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask); - acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); + acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 15e991b99f5a..e02f14f4bd7c 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -15,6 +15,7 @@ #include <linux/firmware/samsung/exynos-acpm-protocol.h> #include <linux/io.h> #include <linux/iopoll.h> +#include <linux/ktime.h> #include <linux/mailbox/exynos-message.h> #include <linux/mailbox_client.h> #include <linux/module.h> @@ -32,8 +33,7 @@ #define ACPM_PROTOCOL_SEQNUM GENMASK(21, 16) -/* The unit of counter is 20 us. 5000 * 20 = 100 ms */ -#define ACPM_POLL_TIMEOUT 5000 +#define ACPM_POLL_TIMEOUT_US (100 * USEC_PER_MSEC) #define ACPM_TX_TIMEOUT_US 500000 #define ACPM_GS101_INITDATA_BASE 0xa000 @@ -300,12 +300,13 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, const struct acpm_xfer *xfer) { struct device *dev = achan->acpm->dev; - unsigned int cnt_20us = 0; + ktime_t timeout; u32 seqnum; int ret; seqnum = FIELD_GET(ACPM_PROTOCOL_SEQNUM, xfer->txd[0]); + timeout = ktime_add_us(ktime_get(), ACPM_POLL_TIMEOUT_US); do { ret = acpm_get_rx(achan, xfer); if (ret) @@ -315,12 +316,11 @@ static int acpm_dequeue_by_polling(struct acpm_chan *achan, return 0; /* Determined experimentally. */ - usleep_range(20, 30); - cnt_20us++; - } while (cnt_20us < ACPM_POLL_TIMEOUT); + udelay(20); + } while (ktime_before(ktime_get(), timeout)); - dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx, cnt_20us = %d.\n", - achan->id, seqnum, achan->bitmap_seqnum[0], cnt_20us); + dev_err(dev, "Timeout! ch:%u s:%u bitmap:%lx.\n", + achan->id, seqnum, achan->bitmap_seqnum[0]); return -ETIME; } @@ -649,7 +649,7 @@ static int acpm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, acpm); - return 0; + return devm_of_platform_populate(dev); } /** @@ -677,43 +677,30 @@ static void devm_acpm_release(struct device *dev, void *res) } /** - * acpm_get_by_phandle() - get the ACPM handle using DT phandle. - * @dev: device pointer requesting ACPM handle. - * @property: property name containing phandle on ACPM node. + * acpm_get_by_node() - get the ACPM handle using node pointer. + * @dev: device pointer requesting ACPM handle. + * @np: ACPM device tree node. * * Return: pointer to handle on success, ERR_PTR(-errno) otherwise. */ -static const struct acpm_handle *acpm_get_by_phandle(struct device *dev, - const char *property) +static const struct acpm_handle *acpm_get_by_node(struct device *dev, + struct device_node *np) { struct platform_device *pdev; - struct device_node *acpm_np; struct device_link *link; struct acpm_info *acpm; - acpm_np = of_parse_phandle(dev->of_node, property, 0); - if (!acpm_np) - return ERR_PTR(-ENODEV); - - pdev = of_find_device_by_node(acpm_np); - if (!pdev) { - dev_err(dev, "Cannot find device node %s\n", acpm_np->name); - of_node_put(acpm_np); + pdev = of_find_device_by_node(np); + if (!pdev) return ERR_PTR(-EPROBE_DEFER); - } - - of_node_put(acpm_np); acpm = platform_get_drvdata(pdev); if (!acpm) { - dev_err(dev, "Cannot get drvdata from %s\n", - dev_name(&pdev->dev)); platform_device_put(pdev); return ERR_PTR(-EPROBE_DEFER); } if (!try_module_get(pdev->dev.driver->owner)) { - dev_err(dev, "Cannot get module reference.\n"); platform_device_put(pdev); return ERR_PTR(-EPROBE_DEFER); } @@ -732,14 +719,14 @@ static const struct acpm_handle *acpm_get_by_phandle(struct device *dev, } /** - * devm_acpm_get_by_phandle() - managed get handle using phandle. - * @dev: device pointer requesting ACPM handle. - * @property: property name containing phandle on ACPM node. + * devm_acpm_get_by_node() - managed get handle using node pointer. + * @dev: device pointer requesting ACPM handle. + * @np: ACPM device tree node. * * Return: pointer to handle on success, ERR_PTR(-errno) otherwise. */ -const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev, - const char *property) +const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, + struct device_node *np) { const struct acpm_handle **ptr, *handle; @@ -747,7 +734,7 @@ const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev, if (!ptr) return ERR_PTR(-ENOMEM); - handle = acpm_get_by_phandle(dev, property); + handle = acpm_get_by_node(dev, np); if (!IS_ERR(handle)) { *ptr = handle; devres_add(dev, ptr); @@ -757,6 +744,7 @@ const struct acpm_handle *devm_acpm_get_by_phandle(struct device *dev, return handle; } +EXPORT_SYMBOL_GPL(devm_acpm_get_by_node); static const struct acpm_match_data acpm_gs101 = { .initdata_base = ACPM_GS101_INITDATA_BASE, diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 806a975fff22..ae5fd1936ad3 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -2,7 +2,7 @@ /* * Texas Instruments System Control Interface Protocol Driver * - * Copyright (C) 2015-2024 Texas Instruments Incorporated - https://www.ti.com/ + * Copyright (C) 2015-2025 Texas Instruments Incorporated - https://www.ti.com/ * Nishanth Menon */ @@ -3670,6 +3670,7 @@ static int __maybe_unused ti_sci_suspend(struct device *dev) struct ti_sci_info *info = dev_get_drvdata(dev); struct device *cpu_dev, *cpu_dev_max = NULL; s32 val, cpu_lat = 0; + u16 cpu_lat_ms; int i, ret; if (info->fw_caps & MSG_FLAG_CAPS_LPM_DM_MANAGED) { @@ -3682,9 +3683,16 @@ static int __maybe_unused ti_sci_suspend(struct device *dev) } } if (cpu_dev_max) { - dev_dbg(cpu_dev_max, "%s: sending max CPU latency=%u\n", __func__, cpu_lat); + /* + * PM QoS latency unit is usecs, device manager uses msecs. + * Convert to msecs and round down for device manager. + */ + cpu_lat_ms = cpu_lat / USEC_PER_MSEC; + dev_dbg(cpu_dev_max, "%s: sending max CPU latency=%u ms\n", __func__, + cpu_lat_ms); ret = ti_sci_cmd_set_latency_constraint(&info->handle, - cpu_lat, TISCI_MSG_CONSTRAINT_SET); + cpu_lat_ms, + TISCI_MSG_CONSTRAINT_SET); if (ret) return ret; } diff --git a/drivers/firmware/turris-mox-rwtm.c b/drivers/firmware/turris-mox-rwtm.c index 47fe6261f5a3..1eac9948148f 100644 --- a/drivers/firmware/turris-mox-rwtm.c +++ b/drivers/firmware/turris-mox-rwtm.c @@ -2,29 +2,31 @@ /* * Turris Mox rWTM firmware driver * - * Copyright (C) 2019, 2024 Marek BehĂșn <kabel@kernel.org> + * Copyright (C) 2019, 2024, 2025 Marek BehĂșn <kabel@kernel.org> */ #include <crypto/sha2.h> #include <linux/align.h> #include <linux/armada-37xx-rwtm-mailbox.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/container_of.h> -#include <linux/debugfs.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/fs.h> #include <linux/hw_random.h> #include <linux/if_ether.h> +#include <linux/key.h> #include <linux/kobject.h> #include <linux/mailbox_client.h> +#include <linux/math.h> #include <linux/minmax.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/sizes.h> #include <linux/sysfs.h> +#include <linux/turris-signing-key.h> #include <linux/types.h> #define DRIVER_NAME "turris-mox-rwtm" @@ -37,10 +39,13 @@ * https://gitlab.labs.nic.cz/turris/mox-boot-builder/tree/master/wtmi. */ -#define MOX_ECC_NUMBER_WORDS 17 -#define MOX_ECC_NUMBER_LEN (MOX_ECC_NUMBER_WORDS * sizeof(u32)) - -#define MOX_ECC_SIGNATURE_WORDS (2 * MOX_ECC_NUMBER_WORDS) +enum { + MOX_ECC_NUM_BITS = 521, + MOX_ECC_NUM_LEN = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 8), + MOX_ECC_NUM_WORDS = DIV_ROUND_UP(MOX_ECC_NUM_BITS, 32), + MOX_ECC_SIG_LEN = 2 * MOX_ECC_NUM_LEN, + MOX_ECC_PUBKEY_LEN = 1 + MOX_ECC_NUM_LEN, +}; #define MBOX_STS_SUCCESS (0 << 30) #define MBOX_STS_FAIL (1 << 30) @@ -77,10 +82,7 @@ enum mbox_cmd { * @ram_size: RAM size of the device * @mac_address1: first MAC address of the device * @mac_address2: second MAC address of the device - * @has_pubkey: whether board ECDSA public key is present * @pubkey: board ECDSA public key - * @last_sig: last ECDSA signature generated with board ECDSA private key - * @last_sig_done: whether the last ECDSA signing is complete */ struct mox_rwtm { struct mbox_client mbox_client; @@ -100,18 +102,8 @@ struct mox_rwtm { int board_version, ram_size; u8 mac_address1[ETH_ALEN], mac_address2[ETH_ALEN]; - bool has_pubkey; - u8 pubkey[135]; - -#ifdef CONFIG_DEBUG_FS - /* - * Signature process. This is currently done via debugfs, because it - * does not conform to the sysfs standard "one file per attribute". - * It should be rewritten via crypto API once akcipher API is available - * from userspace. - */ - u32 last_sig[MOX_ECC_SIGNATURE_WORDS]; - bool last_sig_done; +#ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL + u8 pubkey[MOX_ECC_PUBKEY_LEN]; #endif }; @@ -120,24 +112,23 @@ static inline struct device *rwtm_dev(struct mox_rwtm *rwtm) return rwtm->mbox_client.dev; } -#define MOX_ATTR_RO(name, format, cat) \ +#define MOX_ATTR_RO(name, format) \ static ssize_t \ name##_show(struct device *dev, struct device_attribute *a, \ char *buf) \ { \ struct mox_rwtm *rwtm = dev_get_drvdata(dev); \ - if (!rwtm->has_##cat) \ + if (!rwtm->has_board_info) \ return -ENODATA; \ return sysfs_emit(buf, format, rwtm->name); \ } \ static DEVICE_ATTR_RO(name) -MOX_ATTR_RO(serial_number, "%016llX\n", board_info); -MOX_ATTR_RO(board_version, "%i\n", board_info); -MOX_ATTR_RO(ram_size, "%i\n", board_info); -MOX_ATTR_RO(mac_address1, "%pM\n", board_info); -MOX_ATTR_RO(mac_address2, "%pM\n", board_info); -MOX_ATTR_RO(pubkey, "%s\n", pubkey); +MOX_ATTR_RO(serial_number, "%016llX\n"); +MOX_ATTR_RO(board_version, "%i\n"); +MOX_ATTR_RO(ram_size, "%i\n"); +MOX_ATTR_RO(mac_address1, "%pM\n"); +MOX_ATTR_RO(mac_address2, "%pM\n"); static struct attribute *turris_mox_rwtm_attrs[] = { &dev_attr_serial_number.attr, @@ -145,7 +136,6 @@ static struct attribute *turris_mox_rwtm_attrs[] = { &dev_attr_ram_size.attr, &dev_attr_mac_address1.attr, &dev_attr_mac_address2.attr, - &dev_attr_pubkey.attr, NULL }; ATTRIBUTE_GROUPS(turris_mox_rwtm); @@ -247,24 +237,6 @@ static int mox_get_board_info(struct mox_rwtm *rwtm) pr_info(" burned RAM size %i MiB\n", rwtm->ram_size); } - ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false); - if (ret == -ENODATA) { - dev_warn(dev, "Board has no public key burned!\n"); - } else if (ret == -EOPNOTSUPP) { - dev_notice(dev, - "Firmware does not support the ECDSA_PUB_KEY command\n"); - } else if (ret < 0) { - return ret; - } else { - u32 *s = reply->status; - - rwtm->has_pubkey = true; - sprintf(rwtm->pubkey, - "%06x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x", - ret, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], - s[8], s[9], s[10], s[11], s[12], s[13], s[14], s[15]); - } - return 0; } @@ -306,127 +278,139 @@ unlock_mutex: return ret; } -#ifdef CONFIG_DEBUG_FS -static int rwtm_debug_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; +#ifdef CONFIG_TURRIS_MOX_RWTM_KEYCTL - return nonseekable_open(inode, file); -} - -static ssize_t do_sign_read(struct file *file, char __user *buf, size_t len, - loff_t *ppos) +static void mox_ecc_number_to_bin(void *dst, const u32 *src) { - struct mox_rwtm *rwtm = file->private_data; - ssize_t ret; + __be32 tmp[MOX_ECC_NUM_WORDS]; - /* only allow one read, of whole signature, from position 0 */ - if (*ppos != 0) - return 0; + cpu_to_be32_array(tmp, src, MOX_ECC_NUM_WORDS); - if (len < sizeof(rwtm->last_sig)) - return -EINVAL; + memcpy(dst, (void *)tmp + 2, MOX_ECC_NUM_LEN); +} - if (!rwtm->last_sig_done) - return -ENODATA; +static void mox_ecc_public_key_to_bin(void *dst, u32 src_first, + const u32 *src_rest) +{ + __be32 tmp[MOX_ECC_NUM_WORDS - 1]; + u8 *p = dst; - ret = simple_read_from_buffer(buf, len, ppos, rwtm->last_sig, - sizeof(rwtm->last_sig)); - rwtm->last_sig_done = false; + /* take 3 bytes from the first word */ + *p++ = src_first >> 16; + *p++ = src_first >> 8; + *p++ = src_first; - return ret; + /* take the rest of the words */ + cpu_to_be32_array(tmp, src_rest, MOX_ECC_NUM_WORDS - 1); + memcpy(p, tmp, sizeof(tmp)); } -static ssize_t do_sign_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) +static int mox_rwtm_sign(const struct key *key, const void *data, void *signature) { - struct mox_rwtm *rwtm = file->private_data; - struct armada_37xx_rwtm_tx_msg msg; - loff_t dummy = 0; - ssize_t ret; - - if (len != SHA512_DIGEST_SIZE) - return -EINVAL; - - /* if last result is not zero user has not read that information yet */ - if (rwtm->last_sig_done) - return -EBUSY; + struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key)); + struct armada_37xx_rwtm_tx_msg msg = {}; + u32 offset_r, offset_s; + int ret; - if (!mutex_trylock(&rwtm->busy)) - return -EBUSY; + guard(mutex)(&rwtm->busy); /* - * Here we have to send: - * 1. Address of the input to sign. - * The input is an array of 17 32-bit words, the first (most - * significat) is 0, the rest 16 words are copied from the SHA-512 - * hash given by the user and converted from BE to LE. - * 2. Address of the buffer where ECDSA signature value R shall be - * stored by the rWTM firmware. - * 3. Address of the buffer where ECDSA signature value S shall be - * stored by the rWTM firmware. + * For MBOX_CMD_SIGN command: + * args[0] - must be 1 + * args[1] - address of message M to sign; message is a 521-bit number + * args[2] - address where the R part of the signature will be stored + * args[3] - address where the S part of the signature will be stored + * + * M, R and S are 521-bit numbers encoded as seventeen 32-bit words, + * most significat word first. + * Since the message in @data is a sha512 digest, the most significat + * word is always zero. */ + + offset_r = MOX_ECC_NUM_WORDS * sizeof(u32); + offset_s = 2 * MOX_ECC_NUM_WORDS * sizeof(u32); + memset(rwtm->buf, 0, sizeof(u32)); - ret = simple_write_to_buffer(rwtm->buf + sizeof(u32), - SHA512_DIGEST_SIZE, &dummy, buf, len); - if (ret < 0) - goto unlock_mutex; - be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUMBER_WORDS); + memcpy(rwtm->buf + sizeof(u32), data, SHA512_DIGEST_SIZE); + be32_to_cpu_array(rwtm->buf, rwtm->buf, MOX_ECC_NUM_WORDS); msg.args[0] = 1; msg.args[1] = rwtm->buf_phys; - msg.args[2] = rwtm->buf_phys + MOX_ECC_NUMBER_LEN; - msg.args[3] = rwtm->buf_phys + 2 * MOX_ECC_NUMBER_LEN; + msg.args[2] = rwtm->buf_phys + offset_r; + msg.args[3] = rwtm->buf_phys + offset_s; ret = mox_rwtm_exec(rwtm, MBOX_CMD_SIGN, &msg, true); if (ret < 0) - goto unlock_mutex; + return ret; - /* - * Here we read the R and S values of the ECDSA signature - * computed by the rWTM firmware and convert their words from - * LE to BE. - */ - memcpy(rwtm->last_sig, rwtm->buf + MOX_ECC_NUMBER_LEN, - sizeof(rwtm->last_sig)); - cpu_to_be32_array(rwtm->last_sig, rwtm->last_sig, - MOX_ECC_SIGNATURE_WORDS); - rwtm->last_sig_done = true; + /* convert R and S parts of the signature */ + mox_ecc_number_to_bin(signature, rwtm->buf + offset_r); + mox_ecc_number_to_bin(signature + MOX_ECC_NUM_LEN, rwtm->buf + offset_s); - mutex_unlock(&rwtm->busy); - return len; -unlock_mutex: - mutex_unlock(&rwtm->busy); - return ret; + return 0; } -static const struct file_operations do_sign_fops = { - .owner = THIS_MODULE, - .open = rwtm_debug_open, - .read = do_sign_read, - .write = do_sign_write, -}; - -static void rwtm_debugfs_release(void *root) +static const void *mox_rwtm_get_public_key(const struct key *key) { - debugfs_remove_recursive(root); + struct mox_rwtm *rwtm = dev_get_drvdata(turris_signing_key_get_dev(key)); + + return rwtm->pubkey; } -static void rwtm_register_debugfs(struct mox_rwtm *rwtm) +static const struct turris_signing_key_subtype mox_signing_key_subtype = { + .key_size = MOX_ECC_NUM_BITS, + .data_size = SHA512_DIGEST_SIZE, + .sig_size = MOX_ECC_SIG_LEN, + .public_key_size = MOX_ECC_PUBKEY_LEN, + .hash_algo = "sha512", + .get_public_key = mox_rwtm_get_public_key, + .sign = mox_rwtm_sign, +}; + +static int mox_register_signing_key(struct mox_rwtm *rwtm) { - struct dentry *root; + struct armada_37xx_rwtm_rx_msg *reply = &rwtm->reply; + struct device *dev = rwtm_dev(rwtm); + int ret; - root = debugfs_create_dir("turris-mox-rwtm", NULL); + ret = mox_rwtm_exec(rwtm, MBOX_CMD_ECDSA_PUB_KEY, NULL, false); + if (ret == -ENODATA) { + dev_warn(dev, "Board has no public key burned!\n"); + } else if (ret == -EOPNOTSUPP) { + dev_notice(dev, + "Firmware does not support the ECDSA_PUB_KEY command\n"); + } else if (ret < 0) { + return ret; + } else { + char sn[17] = "unknown"; + char desc[46]; + + if (rwtm->has_board_info) + sprintf(sn, "%016llX", rwtm->serial_number); + + sprintf(desc, "Turris MOX SN %s rWTM ECDSA key", sn); - debugfs_create_file_unsafe("do_sign", 0600, root, rwtm, &do_sign_fops); + mox_ecc_public_key_to_bin(rwtm->pubkey, ret, reply->status); - devm_add_action_or_reset(rwtm_dev(rwtm), rwtm_debugfs_release, root); + ret = devm_turris_signing_key_create(dev, + &mox_signing_key_subtype, + desc); + if (ret) + return dev_err_probe(dev, ret, + "Cannot create signing key\n"); + } + + return 0; } -#else -static inline void rwtm_register_debugfs(struct mox_rwtm *rwtm) + +#else /* CONFIG_TURRIS_MOX_RWTM_KEYCTL */ + +static inline int mox_register_signing_key(struct mox_rwtm *rwtm) { + return 0; } -#endif + +#endif /* !CONFIG_TURRIS_MOX_RWTM_KEYCTL */ static void rwtm_devm_mbox_release(void *mbox) { @@ -477,6 +461,10 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev) if (ret < 0) dev_warn(dev, "Cannot read board information: %i\n", ret); + ret = mox_register_signing_key(rwtm); + if (ret < 0) + return ret; + ret = check_get_random_support(rwtm); if (ret < 0) { dev_notice(dev, @@ -491,8 +479,6 @@ static int turris_mox_rwtm_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "Cannot register HWRNG!\n"); - rwtm_register_debugfs(rwtm); - dev_info(dev, "HWRNG successfully registered\n"); /* |