summaryrefslogtreecommitdiff
path: root/drivers/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/Kconfig1
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/arm_scmi/base.c6
-rw-r--r--drivers/firmware/arm_scmi/clock.c8
-rw-r--r--drivers/firmware/arm_scmi/driver.c24
-rw-r--r--drivers/firmware/arm_scmi/optee.c4
-rw-r--r--drivers/firmware/arm_scmi/perf.c48
-rw-r--r--drivers/firmware/arm_scmi/power.c8
-rw-r--r--drivers/firmware/arm_scmi/powercap.c8
-rw-r--r--drivers/firmware/arm_scmi/protocols.h11
-rw-r--r--drivers/firmware/arm_scmi/reset.c9
-rw-r--r--drivers/firmware/arm_scmi/sensors.c8
-rw-r--r--drivers/firmware/arm_scmi/system.c6
-rw-r--r--drivers/firmware/arm_scmi/voltage.c8
-rw-r--r--drivers/firmware/efi/Kconfig15
-rw-r--r--drivers/firmware/efi/Makefile1
-rw-r--r--drivers/firmware/efi/dev-path-parser.c7
-rw-r--r--drivers/firmware/efi/efi.c18
-rw-r--r--drivers/firmware/efi/libstub/Makefile.zboot4
-rw-r--r--drivers/firmware/efi/libstub/loongarch-stub.c11
-rw-r--r--drivers/firmware/efi/libstub/loongarch-stub.h4
-rw-r--r--drivers/firmware/efi/libstub/loongarch.c8
-rw-r--r--drivers/firmware/efi/libstub/x86-5lvl.c4
-rw-r--r--drivers/firmware/efi/libstub/x86-stub.c33
-rw-r--r--drivers/firmware/efi/memmap.c8
-rw-r--r--drivers/firmware/efi/stmm/mm_communication.h236
-rw-r--r--drivers/firmware/efi/stmm/tee_stmm_efi.c616
-rw-r--r--drivers/firmware/efi/vars.c8
-rw-r--r--drivers/firmware/google/coreboot_table.c5
-rw-r--r--drivers/firmware/google/framebuffer-coreboot.c3
-rw-r--r--drivers/firmware/meson/meson_sm.c19
-rw-r--r--drivers/firmware/microchip/Kconfig12
-rw-r--r--drivers/firmware/microchip/Makefile3
-rw-r--r--drivers/firmware/microchip/mpfs-auto-update.c494
-rw-r--r--drivers/firmware/qcom/qcom_qseecom_uefisecapp.c20
-rw-r--r--drivers/firmware/ti_sci.c10
36 files changed, 1597 insertions, 92 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 4a98a859d44d..61eba7646edc 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -272,6 +272,7 @@ source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
source "drivers/firmware/imx/Kconfig"
source "drivers/firmware/meson/Kconfig"
+source "drivers/firmware/microchip/Kconfig"
source "drivers/firmware/psci/Kconfig"
source "drivers/firmware/qcom/Kconfig"
source "drivers/firmware/smccc/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 5f9dab82e1a0..d305d66bc820 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -28,6 +28,7 @@ obj-y += arm_scmi/
obj-y += broadcom/
obj-y += cirrus/
obj-y += meson/
+obj-y += microchip/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-y += efi/
obj-y += imx/
diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index a52f084a6a87..97254de35ab0 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -13,6 +13,9 @@
#include "common.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+
#define SCMI_BASE_NUM_SOURCES 1
#define SCMI_BASE_MAX_CMD_ERR_COUNT 1024
@@ -385,7 +388,7 @@ static int scmi_base_protocol_init(const struct scmi_protocol_handle *ph)
rev->major_ver = PROTOCOL_REV_MAJOR(version),
rev->minor_ver = PROTOCOL_REV_MINOR(version);
- ph->set_priv(ph, rev);
+ ph->set_priv(ph, rev, version);
ret = scmi_base_attributes_get(ph);
if (ret)
@@ -423,6 +426,7 @@ static const struct scmi_protocol scmi_base = {
.instance_init = &scmi_base_protocol_init,
.ops = NULL,
.events = &base_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(base, scmi_base)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 42b81c181d68..c0644558042a 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -12,6 +12,9 @@
#include "protocols.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001
+
enum scmi_clock_protocol_cmd {
CLOCK_ATTRIBUTES = 0x3,
CLOCK_DESCRIBE_RATES = 0x4,
@@ -318,7 +321,7 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x2) {
if (SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph, CLOCK_NAME_GET, clk_id,
- clk->name,
+ NULL, clk->name,
SCMI_MAX_STR_SIZE);
if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
@@ -961,7 +964,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph)
}
cinfo->version = version;
- return ph->set_priv(ph, cinfo);
+ return ph->set_priv(ph, cinfo, version);
}
static const struct scmi_protocol scmi_clock = {
@@ -970,6 +973,7 @@ static const struct scmi_protocol scmi_clock = {
.instance_init = &scmi_clock_protocol_init,
.ops = &clk_proto_ops,
.events = &clk_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(clock, scmi_clock)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 09371f40d61f..a9f70e6e58ac 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -85,6 +85,7 @@ struct scmi_xfers_info {
* @gid: A reference for per-protocol devres management.
* @users: A refcount to track effective users of this protocol.
* @priv: Reference for optional protocol private data.
+ * @version: Protocol version supported by the platform as detected at runtime.
* @ph: An embedded protocol handle that will be passed down to protocol
* initialization code to identify this instance.
*
@@ -97,6 +98,7 @@ struct scmi_protocol_instance {
void *gid;
refcount_t users;
void *priv;
+ unsigned int version;
struct scmi_protocol_handle ph;
};
@@ -1392,15 +1394,17 @@ static int version_get(const struct scmi_protocol_handle *ph, u32 *version)
*
* @ph: A reference to the protocol handle.
* @priv: The private data to set.
+ * @version: The detected protocol version for the core to register.
*
* Return: 0 on Success
*/
static int scmi_set_protocol_priv(const struct scmi_protocol_handle *ph,
- void *priv)
+ void *priv, u32 version)
{
struct scmi_protocol_instance *pi = ph_to_pi(ph);
pi->priv = priv;
+ pi->version = version;
return 0;
}
@@ -1438,6 +1442,7 @@ struct scmi_msg_resp_domain_name_get {
* @ph: A protocol handle reference.
* @cmd_id: The specific command ID to use.
* @res_id: The specific resource ID to use.
+ * @flags: A pointer to specific flags to use, if any.
* @name: A pointer to the preallocated area where the retrieved name will be
* stored as a NULL terminated string.
* @len: The len in bytes of the @name char array.
@@ -1445,19 +1450,22 @@ struct scmi_msg_resp_domain_name_get {
* Return: 0 on Succcess
*/
static int scmi_common_extended_name_get(const struct scmi_protocol_handle *ph,
- u8 cmd_id, u32 res_id, char *name,
- size_t len)
+ u8 cmd_id, u32 res_id, u32 *flags,
+ char *name, size_t len)
{
int ret;
+ size_t txlen;
struct scmi_xfer *t;
struct scmi_msg_resp_domain_name_get *resp;
- ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(res_id),
- sizeof(*resp), &t);
+ txlen = !flags ? sizeof(res_id) : sizeof(res_id) + sizeof(*flags);
+ ret = ph->xops->xfer_get_init(ph, cmd_id, txlen, sizeof(*resp), &t);
if (ret)
goto out;
put_unaligned_le32(res_id, t->tx.buf);
+ if (flags)
+ put_unaligned_le32(*flags, t->tx.buf + sizeof(res_id));
resp = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
@@ -1845,6 +1853,12 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
devres_close_group(handle->dev, pi->gid);
dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
+ if (pi->version > proto->supported_version)
+ dev_warn(handle->dev,
+ "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X."
+ "Backward compatibility is NOT assured.\n",
+ pi->version, pi->proto->id);
+
return pi;
clean:
diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c
index e123de6e8c67..25bfb465484d 100644
--- a/drivers/firmware/arm_scmi/optee.c
+++ b/drivers/firmware/arm_scmi/optee.c
@@ -440,6 +440,10 @@ static int scmi_optee_chan_setup(struct scmi_chan_info *cinfo, struct device *de
if (ret)
goto err_free_shm;
+ ret = tee_client_system_session(scmi_optee_private->tee_ctx, channel->tee_session);
+ if (ret)
+ dev_warn(dev, "Could not switch to system session, do best effort\n");
+
ret = get_channel(channel);
if (ret)
goto err_close_sess;
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
index e11555de99ab..8ea2a7b3d35d 100644
--- a/drivers/firmware/arm_scmi/perf.c
+++ b/drivers/firmware/arm_scmi/perf.c
@@ -24,7 +24,10 @@
#include "protocols.h"
#include "notify.h"
-#define MAX_OPPS 16
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x40000
+
+#define MAX_OPPS 32
enum scmi_performance_protocol_cmd {
PERF_DOMAIN_ATTRIBUTES = 0x3,
@@ -289,7 +292,7 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags))
ph->hops->extended_name_get(ph, PERF_DOMAIN_NAME_GET,
- dom_info->id, dom_info->info.name,
+ dom_info->id, NULL, dom_info->info.name,
SCMI_MAX_STR_SIZE);
if (dom_info->level_indexing_mode) {
@@ -505,6 +508,9 @@ static int scmi_perf_limits_set(const struct scmi_protocol_handle *ph,
if (IS_ERR(dom))
return PTR_ERR(dom);
+ if (!dom->set_limits)
+ return -EOPNOTSUPP;
+
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x3 && !max_perf && !min_perf)
return -EINVAL;
@@ -655,6 +661,9 @@ static int scmi_perf_level_set(const struct scmi_protocol_handle *ph,
if (IS_ERR(dom))
return PTR_ERR(dom);
+ if (!dom->info.set_perf)
+ return -EOPNOTSUPP;
+
if (dom->level_indexing_mode) {
struct scmi_opp *opp;
@@ -754,7 +763,7 @@ static int scmi_perf_level_limits_notify(const struct scmi_protocol_handle *ph,
}
static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
- u32 domain, struct scmi_fc_info **p_fc)
+ struct perf_dom_info *dom)
{
struct scmi_fc_info *fc;
@@ -763,24 +772,26 @@ static void scmi_perf_domain_init_fc(const struct scmi_protocol_handle *ph,
return;
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LEVEL_SET, 4, domain,
- &fc[PERF_FC_LEVEL].set_addr,
- &fc[PERF_FC_LEVEL].set_db);
-
- ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LEVEL_GET, 4, domain,
+ PERF_LEVEL_GET, 4, dom->id,
&fc[PERF_FC_LEVEL].get_addr, NULL);
ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LIMITS_SET, 8, domain,
- &fc[PERF_FC_LIMIT].set_addr,
- &fc[PERF_FC_LIMIT].set_db);
-
- ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
- PERF_LIMITS_GET, 8, domain,
+ PERF_LIMITS_GET, 8, dom->id,
&fc[PERF_FC_LIMIT].get_addr, NULL);
- *p_fc = fc;
+ if (dom->info.set_perf)
+ ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
+ PERF_LEVEL_SET, 4, dom->id,
+ &fc[PERF_FC_LEVEL].set_addr,
+ &fc[PERF_FC_LEVEL].set_db);
+
+ if (dom->set_limits)
+ ph->hops->fastchannel_init(ph, PERF_DESCRIBE_FASTCHANNEL,
+ PERF_LIMITS_SET, 8, dom->id,
+ &fc[PERF_FC_LIMIT].set_addr,
+ &fc[PERF_FC_LIMIT].set_db);
+
+ dom->fc_info = fc;
}
static int scmi_dvfs_device_opps_add(const struct scmi_protocol_handle *ph,
@@ -1091,14 +1102,14 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph)
scmi_perf_describe_levels_get(ph, dom, version);
if (dom->perf_fastchannels)
- scmi_perf_domain_init_fc(ph, dom->id, &dom->fc_info);
+ scmi_perf_domain_init_fc(ph, dom);
}
ret = devm_add_action_or_reset(ph->dev, scmi_perf_xa_destroy, pinfo);
if (ret)
return ret;
- return ph->set_priv(ph, pinfo);
+ return ph->set_priv(ph, pinfo, version);
}
static const struct scmi_protocol scmi_perf = {
@@ -1107,6 +1118,7 @@ static const struct scmi_protocol scmi_perf = {
.instance_init = &scmi_perf_protocol_init,
.ops = &perf_proto_ops,
.events = &perf_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(perf, scmi_perf)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 356e83631664..c2e6b9b4d941 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -13,6 +13,9 @@
#include "protocols.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
+
enum scmi_power_protocol_cmd {
POWER_DOMAIN_ATTRIBUTES = 0x3,
POWER_STATE_SET = 0x4,
@@ -133,7 +136,7 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags)) {
ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
- domain, dom_info->name,
+ domain, NULL, dom_info->name,
SCMI_MAX_STR_SIZE);
}
@@ -328,7 +331,7 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
pinfo->version = version;
- return ph->set_priv(ph, pinfo);
+ return ph->set_priv(ph, pinfo, version);
}
static const struct scmi_protocol scmi_power = {
@@ -337,6 +340,7 @@ static const struct scmi_protocol scmi_power = {
.instance_init = &scmi_power_protocol_init,
.ops = &power_proto_ops,
.events = &power_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)
diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c
index cb5617443a14..a4c6cd4716fe 100644
--- a/drivers/firmware/arm_scmi/powercap.c
+++ b/drivers/firmware/arm_scmi/powercap.c
@@ -17,6 +17,9 @@
#include "protocols.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+
enum scmi_powercap_protocol_cmd {
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
POWERCAP_CAP_GET = 0x4,
@@ -270,7 +273,7 @@ clean:
*/
if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
- domain, dom_info->name,
+ domain, NULL, dom_info->name,
SCMI_MAX_STR_SIZE);
return ret;
@@ -975,7 +978,7 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
}
pinfo->version = version;
- return ph->set_priv(ph, pinfo);
+ return ph->set_priv(ph, pinfo, version);
}
static const struct scmi_protocol scmi_powercap = {
@@ -984,6 +987,7 @@ static const struct scmi_protocol scmi_powercap = {
.instance_init = &scmi_powercap_protocol_init,
.ops = &powercap_proto_ops,
.events = &powercap_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index 78e1a01eb656..e683c26f24eb 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -174,7 +174,8 @@ struct scmi_protocol_handle {
struct device *dev;
const struct scmi_xfer_ops *xops;
const struct scmi_proto_helpers_ops *hops;
- int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv);
+ int (*set_priv)(const struct scmi_protocol_handle *ph, void *priv,
+ u32 version);
void *(*get_priv)(const struct scmi_protocol_handle *ph);
};
@@ -256,7 +257,8 @@ struct scmi_fc_info {
*/
struct scmi_proto_helpers_ops {
int (*extended_name_get)(const struct scmi_protocol_handle *ph,
- u8 cmd_id, u32 res_id, char *name, size_t len);
+ u8 cmd_id, u32 res_id, u32 *flags, char *name,
+ size_t len);
void *(*iter_response_init)(const struct scmi_protocol_handle *ph,
struct scmi_iterator_ops *ops,
unsigned int max_resources, u8 msg_id,
@@ -310,6 +312,10 @@ typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
* @ops: Optional reference to the operations provided by the protocol and
* exposed in scmi_protocol.h.
* @events: An optional reference to the events supported by this protocol.
+ * @supported_version: The highest version currently supported for this
+ * protocol by the agent. Each protocol implementation
+ * in the agent is supposed to downgrade to match the
+ * protocol version supported by the platform.
*/
struct scmi_protocol {
const u8 id;
@@ -318,6 +324,7 @@ struct scmi_protocol {
const scmi_prot_init_ph_fn_t instance_deinit;
const void *ops;
const struct scmi_protocol_events *events;
+ unsigned int supported_version;
};
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \
diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c
index e9afa8cab730..19970d9f9e36 100644
--- a/drivers/firmware/arm_scmi/reset.c
+++ b/drivers/firmware/arm_scmi/reset.c
@@ -13,6 +13,9 @@
#include "protocols.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
+
enum scmi_reset_protocol_cmd {
RESET_DOMAIN_ATTRIBUTES = 0x3,
RESET = 0x4,
@@ -128,7 +131,8 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
- dom_info->name, SCMI_MAX_STR_SIZE);
+ NULL, dom_info->name,
+ SCMI_MAX_STR_SIZE);
return ret;
}
@@ -342,7 +346,7 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
}
pinfo->version = version;
- return ph->set_priv(ph, pinfo);
+ return ph->set_priv(ph, pinfo, version);
}
static const struct scmi_protocol scmi_reset = {
@@ -351,6 +355,7 @@ static const struct scmi_protocol scmi_reset = {
.instance_init = &scmi_reset_protocol_init,
.ops = &reset_proto_ops,
.events = &reset_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)
diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c
index 0b5853fa9d87..311149965370 100644
--- a/drivers/firmware/arm_scmi/sensors.c
+++ b/drivers/firmware/arm_scmi/sensors.c
@@ -14,6 +14,9 @@
#include "protocols.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
+
#define SCMI_MAX_NUM_SENSOR_AXIS 63
#define SCMIv2_SENSOR_PROTOCOL 0x10000
@@ -644,7 +647,7 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph,
if (PROTOCOL_REV_MAJOR(si->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(attrl))
ph->hops->extended_name_get(ph, SENSOR_NAME_GET, s->id,
- s->name, SCMI_MAX_STR_SIZE);
+ NULL, s->name, SCMI_MAX_STR_SIZE);
if (s->extended_scalar_attrs) {
s->sensor_power = le32_to_cpu(sdesc->power);
@@ -1138,7 +1141,7 @@ static int scmi_sensors_protocol_init(const struct scmi_protocol_handle *ph)
if (ret)
return ret;
- return ph->set_priv(ph, sinfo);
+ return ph->set_priv(ph, sinfo, version);
}
static const struct scmi_protocol scmi_sensors = {
@@ -1147,6 +1150,7 @@ static const struct scmi_protocol scmi_sensors = {
.instance_init = &scmi_sensors_protocol_init,
.ops = &sensor_proto_ops,
.events = &sensor_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(sensors, scmi_sensors)
diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c
index 9383d7584539..1621da97bcbb 100644
--- a/drivers/firmware/arm_scmi/system.c
+++ b/drivers/firmware/arm_scmi/system.c
@@ -13,6 +13,9 @@
#include "protocols.h"
#include "notify.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+
#define SCMI_SYSTEM_NUM_SOURCES 1
enum scmi_system_protocol_cmd {
@@ -144,7 +147,7 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph)
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
pinfo->graceful_timeout_supported = true;
- return ph->set_priv(ph, pinfo);
+ return ph->set_priv(ph, pinfo, version);
}
static const struct scmi_protocol scmi_system = {
@@ -153,6 +156,7 @@ static const struct scmi_protocol scmi_system = {
.instance_init = &scmi_system_protocol_init,
.ops = NULL,
.events = &system_protocol_events,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(system, scmi_system)
diff --git a/drivers/firmware/arm_scmi/voltage.c b/drivers/firmware/arm_scmi/voltage.c
index eaa8d944926a..2175ffd6cef5 100644
--- a/drivers/firmware/arm_scmi/voltage.c
+++ b/drivers/firmware/arm_scmi/voltage.c
@@ -10,6 +10,9 @@
#include "protocols.h"
+/* Updated only after ALL the mandatory features for that version are merged */
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
+
#define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0)
#define REMAINING_LEVELS_MASK GENMASK(31, 16)
#define RETURNED_LEVELS_MASK GENMASK(11, 0)
@@ -242,7 +245,7 @@ static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph,
if (SUPPORTS_EXTENDED_NAMES(attributes))
ph->hops->extended_name_get(ph,
VOLTAGE_DOMAIN_NAME_GET,
- v->id, v->name,
+ v->id, NULL, v->name,
SCMI_MAX_STR_SIZE);
if (SUPPORTS_ASYNC_LEVEL_SET(attributes))
v->async_level_set = true;
@@ -432,7 +435,7 @@ static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph)
dev_warn(ph->dev, "No Voltage domains found.\n");
}
- return ph->set_priv(ph, vinfo);
+ return ph->set_priv(ph, vinfo, version);
}
static const struct scmi_protocol scmi_voltage = {
@@ -440,6 +443,7 @@ static const struct scmi_protocol scmi_voltage = {
.owner = THIS_MODULE,
.instance_init = &scmi_voltage_protocol_init,
.ops = &voltage_proto_ops,
+ .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
};
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(voltage, scmi_voltage)
diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig
index cb374b2da9b7..72f2537d90ca 100644
--- a/drivers/firmware/efi/Kconfig
+++ b/drivers/firmware/efi/Kconfig
@@ -301,3 +301,18 @@ config UEFI_CPER_X86
bool
depends on UEFI_CPER && X86
default y
+
+config TEE_STMM_EFI
+ tristate "TEE-based EFI runtime variable service driver"
+ depends on EFI && OPTEE
+ help
+ Select this config option if TEE is compiled to include StandAloneMM
+ as a separate secure partition. It has the ability to check and store
+ EFI variables on an RPMB or any other non-volatile medium used by
+ StandAloneMM.
+
+ Enabling this will change the EFI runtime services from the firmware
+ provided functions to TEE calls.
+
+ To compile this driver as a module, choose M here: the module
+ will be called tee_stmm_efi.
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
index e489fefd23da..a2d0009560d0 100644
--- a/drivers/firmware/efi/Makefile
+++ b/drivers/firmware/efi/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_EFI_EARLYCON) += earlycon.o
obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o
obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o
obj-$(CONFIG_UNACCEPTED_MEMORY) += unaccepted_memory.o
+obj-$(CONFIG_TEE_STMM_EFI) += stmm/tee_stmm_efi.o
diff --git a/drivers/firmware/efi/dev-path-parser.c b/drivers/firmware/efi/dev-path-parser.c
index f80d87c199c3..937be269fee8 100644
--- a/drivers/firmware/efi/dev-path-parser.c
+++ b/drivers/firmware/efi/dev-path-parser.c
@@ -18,8 +18,6 @@ static long __init parse_acpi_path(const struct efi_dev_path *node,
struct acpi_device *adev;
struct device *phys_dev;
char hid[ACPI_ID_LEN];
- u64 uid;
- int ret;
if (node->header.length != 12)
return -EINVAL;
@@ -31,10 +29,9 @@ static long __init parse_acpi_path(const struct efi_dev_path *node,
node->acpi.hid >> 16);
for_each_acpi_dev_match(adev, hid, NULL, -1) {
- ret = acpi_dev_uid_to_integer(adev, &uid);
- if (ret == 0 && node->acpi.uid == uid)
+ if (acpi_dev_uid_match(adev, node->acpi.uid))
break;
- if (ret == -ENODATA && node->acpi.uid == 0)
+ if (!acpi_device_uid(adev) && node->acpi.uid == 0)
break;
}
if (!adev)
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 9d3910d1abe1..4fcda50acfa4 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -32,6 +32,7 @@
#include <linux/ucs2_string.h>
#include <linux/memblock.h>
#include <linux/security.h>
+#include <linux/notifier.h>
#include <asm/early_ioremap.h>
@@ -187,6 +188,9 @@ static const struct attribute_group efi_subsys_attr_group = {
.is_visible = efi_attr_is_visible,
};
+struct blocking_notifier_head efivar_ops_nh;
+EXPORT_SYMBOL_GPL(efivar_ops_nh);
+
static struct efivars generic_efivars;
static struct efivar_operations generic_ops;
@@ -231,6 +235,18 @@ static void generic_ops_unregister(void)
efivars_unregister(&generic_efivars);
}
+void efivars_generic_ops_register(void)
+{
+ generic_ops_register();
+}
+EXPORT_SYMBOL_GPL(efivars_generic_ops_register);
+
+void efivars_generic_ops_unregister(void)
+{
+ generic_ops_unregister();
+}
+EXPORT_SYMBOL_GPL(efivars_generic_ops_unregister);
+
#ifdef CONFIG_EFI_CUSTOM_SSDT_OVERLAYS
#define EFIVAR_SSDT_NAME_MAX 16UL
static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
@@ -419,6 +435,8 @@ static int __init efisubsys_init(void)
platform_device_register_simple("efivars", 0, NULL, 0);
}
+ BLOCKING_INIT_NOTIFIER_HEAD(&efivar_ops_nh);
+
error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group);
if (error) {
pr_err("efi: Sysfs attribute export failed with error %d.\n",
diff --git a/drivers/firmware/efi/libstub/Makefile.zboot b/drivers/firmware/efi/libstub/Makefile.zboot
index 2c489627a807..65ffd0b760b2 100644
--- a/drivers/firmware/efi/libstub/Makefile.zboot
+++ b/drivers/firmware/efi/libstub/Makefile.zboot
@@ -5,8 +5,8 @@
# EFI_ZBOOT_FORWARD_CFI
quiet_cmd_copy_and_pad = PAD $@
- cmd_copy_and_pad = cp $< $@ && \
- truncate -s $(shell hexdump -s16 -n4 -e '"%u"' $<) $@
+ cmd_copy_and_pad = cp $< $@; \
+ truncate -s $$(hexdump -s16 -n4 -e '"%u"' $<) $@
# Pad the file to the size of the uncompressed image in memory, including BSS
$(obj)/vmlinux.bin: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE
diff --git a/drivers/firmware/efi/libstub/loongarch-stub.c b/drivers/firmware/efi/libstub/loongarch-stub.c
index 72c71ae201f0..736b6aae323d 100644
--- a/drivers/firmware/efi/libstub/loongarch-stub.c
+++ b/drivers/firmware/efi/libstub/loongarch-stub.c
@@ -8,10 +8,10 @@
#include <asm/efi.h>
#include <asm/addrspace.h>
#include "efistub.h"
+#include "loongarch-stub.h"
extern int kernel_asize;
extern int kernel_fsize;
-extern int kernel_offset;
extern int kernel_entry;
efi_status_t handle_kernel_image(unsigned long *image_addr,
@@ -24,7 +24,7 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
efi_status_t status;
unsigned long kernel_addr = 0;
- kernel_addr = (unsigned long)&kernel_offset - kernel_offset;
+ kernel_addr = (unsigned long)image->image_base;
status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize,
EFI_KIMG_PREFERRED_ADDRESS, efi_get_kimg_min_align(), 0x0);
@@ -35,9 +35,10 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
return status;
}
-unsigned long kernel_entry_address(void)
+unsigned long kernel_entry_address(unsigned long kernel_addr,
+ efi_loaded_image_t *image)
{
- unsigned long base = (unsigned long)&kernel_offset - kernel_offset;
+ unsigned long base = (unsigned long)image->image_base;
- return (unsigned long)&kernel_entry - base + VMLINUX_LOAD_ADDRESS;
+ return (unsigned long)&kernel_entry - base + kernel_addr;
}
diff --git a/drivers/firmware/efi/libstub/loongarch-stub.h b/drivers/firmware/efi/libstub/loongarch-stub.h
new file mode 100644
index 000000000000..cd015955a015
--- /dev/null
+++ b/drivers/firmware/efi/libstub/loongarch-stub.h
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+unsigned long kernel_entry_address(unsigned long kernel_addr,
+ efi_loaded_image_t *image);
diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c
index 807cba2693fc..684c9354637c 100644
--- a/drivers/firmware/efi/libstub/loongarch.c
+++ b/drivers/firmware/efi/libstub/loongarch.c
@@ -8,6 +8,7 @@
#include <asm/efi.h>
#include <asm/addrspace.h>
#include "efistub.h"
+#include "loongarch-stub.h"
typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline,
unsigned long systab);
@@ -37,9 +38,10 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv)
return EFI_SUCCESS;
}
-unsigned long __weak kernel_entry_address(void)
+unsigned long __weak kernel_entry_address(unsigned long kernel_addr,
+ efi_loaded_image_t *image)
{
- return *(unsigned long *)(PHYSADDR(VMLINUX_LOAD_ADDRESS) + 8);
+ return *(unsigned long *)(kernel_addr + 8) - VMLINUX_LOAD_ADDRESS + kernel_addr;
}
efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image,
@@ -73,7 +75,7 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image,
csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0);
csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1);
- real_kernel_entry = (void *)kernel_entry_address();
+ real_kernel_entry = (void *)kernel_entry_address(kernel_addr, image);
real_kernel_entry(true, (unsigned long)cmdline_ptr,
(unsigned long)efi_system_table);
diff --git a/drivers/firmware/efi/libstub/x86-5lvl.c b/drivers/firmware/efi/libstub/x86-5lvl.c
index 479dd445acdc..77359e802181 100644
--- a/drivers/firmware/efi/libstub/x86-5lvl.c
+++ b/drivers/firmware/efi/libstub/x86-5lvl.c
@@ -13,8 +13,8 @@ bool efi_no5lvl;
static void (*la57_toggle)(void *cr3);
static const struct desc_struct gdt[] = {
- [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(0xc09b, 0, 0xfffff),
- [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(0xa09b, 0, 0xfffff),
+ [GDT_ENTRY_KERNEL32_CS] = GDT_ENTRY_INIT(DESC_CODE32, 0, 0xfffff),
+ [GDT_ENTRY_KERNEL_CS] = GDT_ENTRY_INIT(DESC_CODE64, 0, 0xfffff),
};
/*
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index 1bfdae34df39..0d510c9a06a4 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -307,17 +307,20 @@ static void setup_unaccepted_memory(void)
efi_err("Memory acceptance protocol failed\n");
}
+static efi_char16_t *efistub_fw_vendor(void)
+{
+ unsigned long vendor = efi_table_attr(efi_system_table, fw_vendor);
+
+ return (efi_char16_t *)vendor;
+}
+
static const efi_char16_t apple[] = L"Apple";
static void setup_quirks(struct boot_params *boot_params)
{
- efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
- efi_table_attr(efi_system_table, fw_vendor);
-
- if (!memcmp(fw_vendor, apple, sizeof(apple))) {
- if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
- retrieve_apple_device_properties(boot_params);
- }
+ if (IS_ENABLED(CONFIG_APPLE_PROPERTIES) &&
+ !memcmp(efistub_fw_vendor(), apple, sizeof(apple)))
+ retrieve_apple_device_properties(boot_params);
}
/*
@@ -765,11 +768,27 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry)
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && !efi_nokaslr) {
u64 range = KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR - kernel_total_size;
+ static const efi_char16_t ami[] = L"American Megatrends";
efi_get_seed(seed, sizeof(seed));
virt_addr += (range * seed[1]) >> 32;
virt_addr &= ~(CONFIG_PHYSICAL_ALIGN - 1);
+
+ /*
+ * Older Dell systems with AMI UEFI firmware v2.0 may hang
+ * while decompressing the kernel if physical address
+ * randomization is enabled.
+ *
+ * https://bugzilla.kernel.org/show_bug.cgi?id=218173
+ */
+ if (efi_system_table->hdr.revision <= EFI_2_00_SYSTEM_TABLE_REVISION &&
+ !memcmp(efistub_fw_vendor(), ami, sizeof(ami))) {
+ efi_debug("AMI firmware v2.0 or older detected - disabling physical KASLR\n");
+ seed[0] = 0;
+ }
+
+ boot_params_ptr->hdr.loadflags |= KASLR_FLAG;
}
status = efi_random_alloc(alloc_size, CONFIG_PHYSICAL_ALIGN, &addr,
diff --git a/drivers/firmware/efi/memmap.c b/drivers/firmware/efi/memmap.c
index a1180461a445..3365944f7965 100644
--- a/drivers/firmware/efi/memmap.c
+++ b/drivers/firmware/efi/memmap.c
@@ -32,7 +32,7 @@
* space isn't setup. Once the kernel is fully booted we can fallback
* to the more robust memremap*() API.
*
- * Returns zero on success, a negative error code on failure.
+ * Returns: zero on success, a negative error code on failure.
*/
int __init __efi_memmap_init(struct efi_memory_map_data *data)
{
@@ -77,6 +77,8 @@ int __init __efi_memmap_init(struct efi_memory_map_data *data)
*
* Use early_memremap() to map the passed in EFI memory map and assign
* it to efi.memmap.
+ *
+ * Returns: zero on success, a negative error code on failure.
*/
int __init efi_memmap_init_early(struct efi_memory_map_data *data)
{
@@ -107,7 +109,7 @@ void __init efi_memmap_unmap(void)
/**
* efi_memmap_init_late - Map efi.memmap with memremap()
- * @phys_addr: Physical address of the new EFI memory map
+ * @addr: Physical address of the new EFI memory map
* @size: Size in bytes of the new EFI memory map
*
* Setup a mapping of the EFI memory map using ioremap_cache(). This
@@ -126,7 +128,7 @@ void __init efi_memmap_unmap(void)
* runtime so that things like efi_mem_desc_lookup() and
* efi_mem_attributes() always work.
*
- * Returns zero on success, a negative error code on failure.
+ * Returns: zero on success, a negative error code on failure.
*/
int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
{
diff --git a/drivers/firmware/efi/stmm/mm_communication.h b/drivers/firmware/efi/stmm/mm_communication.h
new file mode 100644
index 000000000000..52a1f32cd1eb
--- /dev/null
+++ b/drivers/firmware/efi/stmm/mm_communication.h
@@ -0,0 +1,236 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Headers for EFI variable service via StandAloneMM, EDK2 application running
+ * in OP-TEE. Most of the structs and defines resemble the EDK2 naming.
+ *
+ * Copyright (c) 2017, Intel Corporation. All rights reserved.
+ * Copyright (C) 2020 Linaro Ltd.
+ */
+
+#ifndef _MM_COMMUNICATION_H_
+#define _MM_COMMUNICATION_H_
+
+/*
+ * Interface to the pseudo Trusted Application (TA), which provides a
+ * communication channel with the Standalone MM (Management Mode)
+ * Secure Partition running at Secure-EL0
+ */
+
+#define PTA_STMM_CMD_COMMUNICATE 0
+
+/*
+ * Defined in OP-TEE, this UUID is used to identify the pseudo-TA.
+ * OP-TEE is using big endian GUIDs while UEFI uses little endian ones
+ */
+#define PTA_STMM_UUID \
+ UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
+ 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
+
+#define EFI_MM_VARIABLE_GUID \
+ EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
+ 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
+
+/**
+ * struct efi_mm_communicate_header - Header used for SMM variable communication
+
+ * @header_guid: header use for disambiguation of content
+ * @message_len: length of the message. Does not include the size of the
+ * header
+ * @data: payload of the message
+ *
+ * Defined in the PI spec as EFI_MM_COMMUNICATE_HEADER.
+ * To avoid confusion in interpreting frames, the communication buffer should
+ * always begin with efi_mm_communicate_header.
+ */
+struct efi_mm_communicate_header {
+ efi_guid_t header_guid;
+ size_t message_len;
+ u8 data[];
+} __packed;
+
+#define MM_COMMUNICATE_HEADER_SIZE \
+ (sizeof(struct efi_mm_communicate_header))
+
+/* SPM return error codes */
+#define ARM_SVC_SPM_RET_SUCCESS 0
+#define ARM_SVC_SPM_RET_NOT_SUPPORTED -1
+#define ARM_SVC_SPM_RET_INVALID_PARAMS -2
+#define ARM_SVC_SPM_RET_DENIED -3
+#define ARM_SVC_SPM_RET_NO_MEMORY -5
+
+#define SMM_VARIABLE_FUNCTION_GET_VARIABLE 1
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
+ */
+#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME 2
+/*
+ * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
+ */
+#define SMM_VARIABLE_FUNCTION_SET_VARIABLE 3
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
+ */
+#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO 4
+/*
+ * It is a notify event, no extra payload for this function.
+ */
+#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT 5
+/*
+ * It is a notify event, no extra payload for this function.
+ */
+#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE 6
+/*
+ * The payload for this function is VARIABLE_INFO_ENTRY.
+ * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
+ */
+#define SMM_VARIABLE_FUNCTION_GET_STATISTICS 7
+/*
+ * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
+ */
+#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE 8
+
+#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET 9
+
+#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET 10
+
+#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE 11
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
+ */
+#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
+
+#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE 13
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
+ */
+#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO 14
+
+/**
+ * struct smm_variable_communicate_header - Used for SMM variable communication
+
+ * @function: function to call in Smm.
+ * @ret_status: return status
+ * @data: payload
+ */
+struct smm_variable_communicate_header {
+ size_t function;
+ efi_status_t ret_status;
+ u8 data[];
+};
+
+#define MM_VARIABLE_COMMUNICATE_SIZE \
+ (sizeof(struct smm_variable_communicate_header))
+
+/**
+ * struct smm_variable_access - Used to communicate with StMM by
+ * SetVariable and GetVariable.
+
+ * @guid: vendor GUID
+ * @data_size: size of EFI variable data
+ * @name_size: size of EFI name
+ * @attr: attributes
+ * @name: variable name
+ *
+ */
+struct smm_variable_access {
+ efi_guid_t guid;
+ size_t data_size;
+ size_t name_size;
+ u32 attr;
+ u16 name[];
+};
+
+#define MM_VARIABLE_ACCESS_HEADER_SIZE \
+ (sizeof(struct smm_variable_access))
+/**
+ * struct smm_variable_payload_size - Used to get the max allowed
+ * payload used in StMM.
+ *
+ * @size: size to fill in
+ *
+ */
+struct smm_variable_payload_size {
+ size_t size;
+};
+
+/**
+ * struct smm_variable_getnext - Used to communicate with StMM for
+ * GetNextVariableName.
+ *
+ * @guid: vendor GUID
+ * @name_size: size of the name of the variable
+ * @name: variable name
+ *
+ */
+struct smm_variable_getnext {
+ efi_guid_t guid;
+ size_t name_size;
+ u16 name[];
+};
+
+#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
+ (sizeof(struct smm_variable_getnext))
+
+/**
+ * struct smm_variable_query_info - Used to communicate with StMM for
+ * QueryVariableInfo.
+ *
+ * @max_variable_storage: max available storage
+ * @remaining_variable_storage: remaining available storage
+ * @max_variable_size: max variable supported size
+ * @attr: attributes to query storage for
+ *
+ */
+struct smm_variable_query_info {
+ u64 max_variable_storage;
+ u64 remaining_variable_storage;
+ u64 max_variable_size;
+ u32 attr;
+};
+
+#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
+#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
+/**
+ * struct var_check_property - Used to store variable properties in StMM
+ *
+ * @revision: magic revision number for variable property checking
+ * @property: properties mask for the variable used in StMM.
+ * Currently RO flag is supported
+ * @attributes: variable attributes used in StMM checking when properties
+ * for a variable are enabled
+ * @minsize: minimum allowed size for variable payload checked against
+ * smm_variable_access->datasize in StMM
+ * @maxsize: maximum allowed size for variable payload checked against
+ * smm_variable_access->datasize in StMM
+ *
+ */
+struct var_check_property {
+ u16 revision;
+ u16 property;
+ u32 attributes;
+ size_t minsize;
+ size_t maxsize;
+};
+
+/**
+ * struct smm_variable_var_check_property - Used to communicate variable
+ * properties with StMM
+ *
+ * @guid: vendor GUID
+ * @name_size: size of EFI name
+ * @property: variable properties struct
+ * @name: variable name
+ *
+ */
+struct smm_variable_var_check_property {
+ efi_guid_t guid;
+ size_t name_size;
+ struct var_check_property property;
+ u16 name[];
+};
+
+#endif /* _MM_COMMUNICATION_H_ */
diff --git a/drivers/firmware/efi/stmm/tee_stmm_efi.c b/drivers/firmware/efi/stmm/tee_stmm_efi.c
new file mode 100644
index 000000000000..f741ca279052
--- /dev/null
+++ b/drivers/firmware/efi/stmm/tee_stmm_efi.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * EFI variable service via TEE
+ *
+ * Copyright (C) 2022 Linaro
+ */
+
+#include <linux/efi.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/tee.h>
+#include <linux/tee_drv.h>
+#include <linux/ucs2_string.h>
+#include "mm_communication.h"
+
+static struct efivars tee_efivars;
+static struct efivar_operations tee_efivar_ops;
+
+static size_t max_buffer_size; /* comm + var + func + data */
+static size_t max_payload_size; /* func + data */
+
+struct tee_stmm_efi_private {
+ struct tee_context *ctx;
+ u32 session;
+ struct device *dev;
+};
+
+static struct tee_stmm_efi_private pvt_data;
+
+/* UUID of the stmm PTA */
+static const struct tee_client_device_id tee_stmm_efi_id_table[] = {
+ {PTA_STMM_UUID},
+ {}
+};
+
+static int tee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+ /* currently only OP-TEE is supported as a communication path */
+ if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * tee_mm_communicate() - Pass a buffer to StandaloneMM running in TEE
+ *
+ * @comm_buf: locally allocated communication buffer
+ * @dsize: buffer size
+ * Return: status code
+ */
+static efi_status_t tee_mm_communicate(void *comm_buf, size_t dsize)
+{
+ size_t buf_size;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct tee_ioctl_invoke_arg arg;
+ struct tee_param param[4];
+ struct tee_shm *shm = NULL;
+ int rc;
+
+ if (!comm_buf)
+ return EFI_INVALID_PARAMETER;
+
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+ buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
+
+ if (dsize != buf_size)
+ return EFI_INVALID_PARAMETER;
+
+ shm = tee_shm_register_kernel_buf(pvt_data.ctx, comm_buf, buf_size);
+ if (IS_ERR(shm)) {
+ dev_err(pvt_data.dev, "Unable to register shared memory\n");
+ return EFI_UNSUPPORTED;
+ }
+
+ memset(&arg, 0, sizeof(arg));
+ arg.func = PTA_STMM_CMD_COMMUNICATE;
+ arg.session = pvt_data.session;
+ arg.num_params = 4;
+
+ memset(param, 0, sizeof(param));
+ param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[0].u.memref.size = buf_size;
+ param[0].u.memref.shm = shm;
+ param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+ param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+ param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+
+ rc = tee_client_invoke_func(pvt_data.ctx, &arg, param);
+ tee_shm_free(shm);
+
+ if (rc < 0 || arg.ret != 0) {
+ dev_err(pvt_data.dev,
+ "PTA_STMM_CMD_COMMUNICATE invoke error: 0x%x\n", arg.ret);
+ return EFI_DEVICE_ERROR;
+ }
+
+ switch (param[1].u.value.a) {
+ case ARM_SVC_SPM_RET_SUCCESS:
+ return EFI_SUCCESS;
+
+ case ARM_SVC_SPM_RET_INVALID_PARAMS:
+ return EFI_INVALID_PARAMETER;
+
+ case ARM_SVC_SPM_RET_DENIED:
+ return EFI_ACCESS_DENIED;
+
+ case ARM_SVC_SPM_RET_NO_MEMORY:
+ return EFI_OUT_OF_RESOURCES;
+
+ default:
+ return EFI_ACCESS_DENIED;
+ }
+}
+
+/**
+ * mm_communicate() - Adjust the communication buffer to StandAlonneMM and send
+ * it to TEE
+ *
+ * @comm_buf: locally allocated communication buffer, buffer should
+ * be enough big to have some headers and payload
+ * @payload_size: payload size
+ * Return: status code
+ */
+static efi_status_t mm_communicate(u8 *comm_buf, size_t payload_size)
+{
+ size_t dsize;
+ efi_status_t ret;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct smm_variable_communicate_header *var_hdr;
+
+ dsize = payload_size + MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE;
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+
+ ret = tee_mm_communicate(comm_buf, dsize);
+ if (ret != EFI_SUCCESS) {
+ dev_err(pvt_data.dev, "%s failed!\n", __func__);
+ return ret;
+ }
+
+ return var_hdr->ret_status;
+}
+
+/**
+ * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the
+ * header data.
+ *
+ * @dptr: pointer address to store allocated buffer
+ * @payload_size: payload size
+ * @func: standAloneMM function number
+ * @ret: EFI return code
+ * Return: pointer to corresponding StandAloneMM function buffer or NULL
+ */
+static void *setup_mm_hdr(u8 **dptr, size_t payload_size, size_t func,
+ efi_status_t *ret)
+{
+ const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
+ struct efi_mm_communicate_header *mm_hdr;
+ struct smm_variable_communicate_header *var_hdr;
+ u8 *comm_buf;
+
+ /* In the init function we initialize max_buffer_size with
+ * get_max_payload(). So skip the test if max_buffer_size is initialized
+ * StandAloneMM will perform similar checks and drop the buffer if it's
+ * too long
+ */
+ if (max_buffer_size &&
+ max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
+ *ret = EFI_INVALID_PARAMETER;
+ return NULL;
+ }
+
+ comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
+ GFP_KERNEL);
+ if (!comm_buf) {
+ *ret = EFI_OUT_OF_RESOURCES;
+ return NULL;
+ }
+
+ mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+ memcpy(&mm_hdr->header_guid, &mm_var_guid, sizeof(mm_hdr->header_guid));
+ mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
+
+ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+ var_hdr->function = func;
+ if (dptr)
+ *dptr = comm_buf;
+ *ret = EFI_SUCCESS;
+
+ return var_hdr->data;
+}
+
+/**
+ * get_max_payload() - Get variable payload size from StandAloneMM.
+ *
+ * @size: size of the variable in storage
+ * Return: status code
+ */
+static efi_status_t get_max_payload(size_t *size)
+{
+ struct smm_variable_payload_size *var_payload = NULL;
+ size_t payload_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ if (!size)
+ return EFI_INVALID_PARAMETER;
+
+ payload_size = sizeof(*var_payload);
+ var_payload = setup_mm_hdr(&comm_buf, payload_size,
+ SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE,
+ &ret);
+ if (!var_payload)
+ return EFI_OUT_OF_RESOURCES;
+
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ /* Make sure the buffer is big enough for storing variables */
+ if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ *size = var_payload->size;
+ /*
+ * There seems to be a bug in EDK2 miscalculating the boundaries and
+ * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
+ * it up here to ensure backwards compatibility with older versions
+ * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
+ * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
+ * flexible array member).
+ *
+ * size is guaranteed to be > 2 due to checks on the beginning.
+ */
+ *size -= 2;
+out:
+ kfree(comm_buf);
+ return ret;
+}
+
+static efi_status_t get_property_int(u16 *name, size_t name_size,
+ const efi_guid_t *vendor,
+ struct var_check_property *var_property)
+{
+ struct smm_variable_var_check_property *smm_property;
+ size_t payload_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ memset(var_property, 0, sizeof(*var_property));
+ payload_size = sizeof(*smm_property) + name_size;
+ if (payload_size > max_payload_size)
+ return EFI_INVALID_PARAMETER;
+
+ smm_property = setup_mm_hdr(
+ &comm_buf, payload_size,
+ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
+ if (!smm_property)
+ return EFI_OUT_OF_RESOURCES;
+
+ memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
+ smm_property->name_size = name_size;
+ memcpy(smm_property->name, name, name_size);
+
+ ret = mm_communicate(comm_buf, payload_size);
+ /*
+ * Currently only R/O property is supported in StMM.
+ * Variables that are not set to R/O will not set the property in StMM
+ * and the call will return EFI_NOT_FOUND. We are setting the
+ * properties to 0x0 so checking against that is enough for the
+ * EFI_NOT_FOUND case.
+ */
+ if (ret == EFI_NOT_FOUND)
+ ret = EFI_SUCCESS;
+ if (ret != EFI_SUCCESS)
+ goto out;
+ memcpy(var_property, &smm_property->property, sizeof(*var_property));
+
+out:
+ kfree(comm_buf);
+ return ret;
+}
+
+static efi_status_t tee_get_variable(u16 *name, efi_guid_t *vendor,
+ u32 *attributes, unsigned long *data_size,
+ void *data)
+{
+ struct var_check_property var_property;
+ struct smm_variable_access *var_acc;
+ size_t payload_size;
+ size_t name_size;
+ size_t tmp_dsize;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ if (!name || !vendor || !data_size)
+ return EFI_INVALID_PARAMETER;
+
+ name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+ if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE)
+ return EFI_INVALID_PARAMETER;
+
+ /* Trim output buffer size */
+ tmp_dsize = *data_size;
+ if (name_size + tmp_dsize >
+ max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+ tmp_dsize = max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE -
+ name_size;
+ }
+
+ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
+ var_acc = setup_mm_hdr(&comm_buf, payload_size,
+ SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
+ if (!var_acc)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Fill in contents */
+ memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
+ var_acc->data_size = tmp_dsize;
+ var_acc->name_size = name_size;
+ var_acc->attr = attributes ? *attributes : 0;
+ memcpy(var_acc->name, name, name_size);
+
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
+ /* Update with reported data size for trimmed case */
+ *data_size = var_acc->data_size;
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ ret = get_property_int(name, name_size, vendor, &var_property);
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ if (attributes)
+ *attributes = var_acc->attr;
+
+ if (!data) {
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
+ var_acc->data_size);
+out:
+ kfree(comm_buf);
+ return ret;
+}
+
+static efi_status_t tee_get_next_variable(unsigned long *name_size,
+ efi_char16_t *name, efi_guid_t *guid)
+{
+ struct smm_variable_getnext *var_getnext;
+ size_t payload_size;
+ size_t out_name_size;
+ size_t in_name_size;
+ u8 *comm_buf = NULL;
+ efi_status_t ret;
+
+ if (!name_size || !name || !guid)
+ return EFI_INVALID_PARAMETER;
+
+ out_name_size = *name_size;
+ in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+
+ if (out_name_size < in_name_size)
+ return EFI_INVALID_PARAMETER;
+
+ if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
+ return EFI_INVALID_PARAMETER;
+
+ /* Trim output buffer size */
+ if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
+ out_name_size =
+ max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE;
+
+ payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
+ var_getnext = setup_mm_hdr(&comm_buf, payload_size,
+ SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
+ &ret);
+ if (!var_getnext)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* Fill in contents */
+ memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
+ var_getnext->name_size = out_name_size;
+ memcpy(var_getnext->name, name, in_name_size);
+ memset((u8 *)var_getnext->name + in_name_size, 0x0,
+ out_name_size - in_name_size);
+
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+ /* Update with reported data size for trimmed case */
+ *name_size = var_getnext->name_size;
+ }
+ if (ret != EFI_SUCCESS)
+ goto out;
+
+ memcpy(guid, &var_getnext->guid, sizeof(*guid));
+ memcpy(name, var_getnext->name, var_getnext->name_size);
+
+out:
+ kfree(comm_buf);
+ return ret;
+}
+
+static efi_status_t tee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
+ u32 attributes, unsigned long data_size,
+ void *data)
+{
+ efi_status_t ret;
+ struct var_check_property var_property;
+ struct smm_variable_access *var_acc;
+ size_t payload_size;
+ size_t name_size;
+ u8 *comm_buf = NULL;
+
+ if (!name || name[0] == 0 || !vendor)
+ return EFI_INVALID_PARAMETER;
+
+ if (data_size > 0 && !data)
+ return EFI_INVALID_PARAMETER;
+
+ /* Check payload size */
+ name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
+ if (payload_size > max_payload_size)
+ return EFI_INVALID_PARAMETER;
+
+ /*
+ * Allocate the buffer early, before switching to RW (if needed)
+ * so we won't need to account for any failures in reading/setting
+ * the properties, if the allocation fails
+ */
+ var_acc = setup_mm_hdr(&comm_buf, payload_size,
+ SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
+ if (!var_acc)
+ return EFI_OUT_OF_RESOURCES;
+
+ /*
+ * The API has the ability to override RO flags. If no RO check was
+ * requested switch the variable to RW for the duration of this call
+ */
+ ret = get_property_int(name, name_size, vendor, &var_property);
+ if (ret != EFI_SUCCESS) {
+ dev_err(pvt_data.dev, "Getting variable property failed\n");
+ goto out;
+ }
+
+ if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
+ ret = EFI_WRITE_PROTECTED;
+ goto out;
+ }
+
+ /* Fill in contents */
+ memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
+ var_acc->data_size = data_size;
+ var_acc->name_size = name_size;
+ var_acc->attr = attributes;
+ memcpy(var_acc->name, name, name_size);
+ memcpy((u8 *)var_acc->name + name_size, data, data_size);
+
+ ret = mm_communicate(comm_buf, payload_size);
+ dev_dbg(pvt_data.dev, "Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
+out:
+ kfree(comm_buf);
+ return ret;
+}
+
+static efi_status_t tee_set_variable_nonblocking(efi_char16_t *name,
+ efi_guid_t *vendor,
+ u32 attributes,
+ unsigned long data_size,
+ void *data)
+{
+ return EFI_UNSUPPORTED;
+}
+
+static efi_status_t tee_query_variable_info(u32 attributes,
+ u64 *max_variable_storage_size,
+ u64 *remain_variable_storage_size,
+ u64 *max_variable_size)
+{
+ struct smm_variable_query_info *mm_query_info;
+ size_t payload_size;
+ efi_status_t ret;
+ u8 *comm_buf;
+
+ payload_size = sizeof(*mm_query_info);
+ mm_query_info = setup_mm_hdr(&comm_buf, payload_size,
+ SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
+ &ret);
+ if (!mm_query_info)
+ return EFI_OUT_OF_RESOURCES;
+
+ mm_query_info->attr = attributes;
+ ret = mm_communicate(comm_buf, payload_size);
+ if (ret != EFI_SUCCESS)
+ goto out;
+ *max_variable_storage_size = mm_query_info->max_variable_storage;
+ *remain_variable_storage_size =
+ mm_query_info->remaining_variable_storage;
+ *max_variable_size = mm_query_info->max_variable_size;
+
+out:
+ kfree(comm_buf);
+ return ret;
+}
+
+static void tee_stmm_efi_close_context(void *data)
+{
+ tee_client_close_context(pvt_data.ctx);
+}
+
+static void tee_stmm_efi_close_session(void *data)
+{
+ tee_client_close_session(pvt_data.ctx, pvt_data.session);
+}
+
+static void tee_stmm_restore_efivars_generic_ops(void)
+{
+ efivars_unregister(&tee_efivars);
+ efivars_generic_ops_register();
+}
+
+static int tee_stmm_efi_probe(struct device *dev)
+{
+ struct tee_ioctl_open_session_arg sess_arg;
+ efi_status_t ret;
+ int rc;
+
+ pvt_data.ctx = tee_client_open_context(NULL, tee_ctx_match, NULL, NULL);
+ if (IS_ERR(pvt_data.ctx))
+ return -ENODEV;
+
+ rc = devm_add_action_or_reset(dev, tee_stmm_efi_close_context, NULL);
+ if (rc)
+ return rc;
+
+ /* Open session with StMM PTA */
+ memset(&sess_arg, 0, sizeof(sess_arg));
+ export_uuid(sess_arg.uuid, &tee_stmm_efi_id_table[0].uuid);
+ rc = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
+ if ((rc < 0) || (sess_arg.ret != 0)) {
+ dev_err(dev, "tee_client_open_session failed, err: %x\n",
+ sess_arg.ret);
+ return -EINVAL;
+ }
+ pvt_data.session = sess_arg.session;
+ pvt_data.dev = dev;
+ rc = devm_add_action_or_reset(dev, tee_stmm_efi_close_session, NULL);
+ if (rc)
+ return rc;
+
+ ret = get_max_payload(&max_payload_size);
+ if (ret != EFI_SUCCESS)
+ return -EIO;
+
+ max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
+ MM_VARIABLE_COMMUNICATE_SIZE +
+ max_payload_size;
+
+ tee_efivar_ops.get_variable = tee_get_variable;
+ tee_efivar_ops.get_next_variable = tee_get_next_variable;
+ tee_efivar_ops.set_variable = tee_set_variable;
+ tee_efivar_ops.set_variable_nonblocking = tee_set_variable_nonblocking;
+ tee_efivar_ops.query_variable_store = efi_query_variable_store;
+ tee_efivar_ops.query_variable_info = tee_query_variable_info;
+
+ efivars_generic_ops_unregister();
+ pr_info("Using TEE-based EFI runtime variable services\n");
+ efivars_register(&tee_efivars, &tee_efivar_ops);
+
+ return 0;
+}
+
+static int tee_stmm_efi_remove(struct device *dev)
+{
+ tee_stmm_restore_efivars_generic_ops();
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(tee, tee_stmm_efi_id_table);
+
+static struct tee_client_driver tee_stmm_efi_driver = {
+ .id_table = tee_stmm_efi_id_table,
+ .driver = {
+ .name = "tee-stmm-efi",
+ .bus = &tee_bus_type,
+ .probe = tee_stmm_efi_probe,
+ .remove = tee_stmm_efi_remove,
+ },
+};
+
+static int __init tee_stmm_efi_mod_init(void)
+{
+ return driver_register(&tee_stmm_efi_driver.driver);
+}
+
+static void __exit tee_stmm_efi_mod_exit(void)
+{
+ driver_unregister(&tee_stmm_efi_driver.driver);
+}
+
+module_init(tee_stmm_efi_mod_init);
+module_exit(tee_stmm_efi_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
+MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
+MODULE_DESCRIPTION("TEE based EFI runtime variable service driver");
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index e9dc7116daf1..f654e6f6af87 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -63,6 +63,7 @@ int efivars_register(struct efivars *efivars,
const struct efivar_operations *ops)
{
int rv;
+ int event;
if (down_interruptible(&efivars_lock))
return -EINTR;
@@ -77,6 +78,13 @@ int efivars_register(struct efivars *efivars,
__efivars = efivars;
+ if (efivar_supports_writes())
+ event = EFIVAR_OPS_RDWR;
+ else
+ event = EFIVAR_OPS_RDONLY;
+
+ blocking_notifier_call_chain(&efivar_ops_nh, event, NULL);
+
pr_info("Registered efivars operations\n");
rv = 0;
out:
diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c
index 33ae94745aef..2a4469bf1b81 100644
--- a/drivers/firmware/google/coreboot_table.c
+++ b/drivers/firmware/google/coreboot_table.c
@@ -176,10 +176,9 @@ static int __cb_dev_unregister(struct device *dev, void *dummy)
return 0;
}
-static int coreboot_table_remove(struct platform_device *pdev)
+static void coreboot_table_remove(struct platform_device *pdev)
{
bus_for_each_dev(&coreboot_bus_type, NULL, NULL, __cb_dev_unregister);
- return 0;
}
#ifdef CONFIG_ACPI
@@ -201,7 +200,7 @@ MODULE_DEVICE_TABLE(of, coreboot_of_match);
static struct platform_driver coreboot_table_driver = {
.probe = coreboot_table_probe,
- .remove = coreboot_table_remove,
+ .remove_new = coreboot_table_remove,
.driver = {
.name = "coreboot_table",
.acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match),
diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c
index c323a818805c..5c84bbebfef8 100644
--- a/drivers/firmware/google/framebuffer-coreboot.c
+++ b/drivers/firmware/google/framebuffer-coreboot.c
@@ -36,6 +36,9 @@ static int framebuffer_probe(struct coreboot_device *dev)
.format = NULL,
};
+ if (!fb->physical_address)
+ return -ENODEV;
+
for (i = 0; i < ARRAY_SIZE(formats); ++i) {
if (fb->bits_per_pixel == formats[i].bits_per_pixel &&
fb->red_mask_pos == formats[i].red.offset &&
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c
index ed60f1103053..5d7f62fe1d5f 100644
--- a/drivers/firmware/meson/meson_sm.c
+++ b/drivers/firmware/meson/meson_sm.c
@@ -274,14 +274,11 @@ static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RO(serial);
-static struct attribute *meson_sm_sysfs_attributes[] = {
+static struct attribute *meson_sm_sysfs_attrs[] = {
&dev_attr_serial.attr,
NULL,
};
-
-static const struct attribute_group meson_sm_sysfs_attr_group = {
- .attrs = meson_sm_sysfs_attributes,
-};
+ATTRIBUTE_GROUPS(meson_sm_sysfs);
static const struct of_device_id meson_sm_ids[] = {
{ .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
@@ -313,7 +310,7 @@ static int __init meson_sm_probe(struct platform_device *pdev)
fw->sm_shmem_out_base = meson_sm_map_shmem(chip->cmd_shmem_out_base,
chip->shmem_size);
if (WARN_ON(!fw->sm_shmem_out_base))
- goto out_in_base;
+ goto unmap_in_base;
}
fw->chip = chip;
@@ -321,16 +318,15 @@ static int __init meson_sm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fw);
if (devm_of_platform_populate(dev))
- goto out_in_base;
-
- if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
- goto out_in_base;
+ goto unmap_out_base;
pr_info("secure-monitor enabled\n");
return 0;
-out_in_base:
+unmap_out_base:
+ iounmap(fw->sm_shmem_out_base);
+unmap_in_base:
iounmap(fw->sm_shmem_in_base);
out:
return -EINVAL;
@@ -340,6 +336,7 @@ static struct platform_driver meson_sm_driver = {
.driver = {
.name = "meson-sm",
.of_match_table = of_match_ptr(meson_sm_ids),
+ .dev_groups = meson_sm_sysfs_groups,
},
};
module_platform_driver_probe(meson_sm_driver, meson_sm_probe);
diff --git a/drivers/firmware/microchip/Kconfig b/drivers/firmware/microchip/Kconfig
new file mode 100644
index 000000000000..434b923e08c2
--- /dev/null
+++ b/drivers/firmware/microchip/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config POLARFIRE_SOC_AUTO_UPDATE
+ tristate "Microchip PolarFire SoC AUTO UPDATE"
+ depends on POLARFIRE_SOC_SYS_CTRL
+ select FW_LOADER
+ select FW_UPLOAD
+ help
+ Support for reprogramming PolarFire SoC from within Linux, using the
+ Auto Upgrade feature of the system controller.
+
+ If built as a module, it will be called mpfs-auto-update.
diff --git a/drivers/firmware/microchip/Makefile b/drivers/firmware/microchip/Makefile
new file mode 100644
index 000000000000..38796fd82893
--- /dev/null
+++ b/drivers/firmware/microchip/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_POLARFIRE_SOC_AUTO_UPDATE) += mpfs-auto-update.o
diff --git a/drivers/firmware/microchip/mpfs-auto-update.c b/drivers/firmware/microchip/mpfs-auto-update.c
new file mode 100644
index 000000000000..81f5f62e34fc
--- /dev/null
+++ b/drivers/firmware/microchip/mpfs-auto-update.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip Polarfire SoC "Auto Update" FPGA reprogramming.
+ *
+ * Documentation of this functionality is available in the "PolarFire® FPGA and
+ * PolarFire SoC FPGA Programming" User Guide.
+ *
+ * Copyright (c) 2022-2023 Microchip Corporation. All rights reserved.
+ *
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ */
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+
+#include <soc/microchip/mpfs.h>
+
+#define AUTO_UPDATE_DEFAULT_MBOX_OFFSET 0u
+#define AUTO_UPDATE_DEFAULT_RESP_OFFSET 0u
+
+#define AUTO_UPDATE_FEATURE_CMD_OPCODE 0x05u
+#define AUTO_UPDATE_FEATURE_CMD_DATA_SIZE 0u
+#define AUTO_UPDATE_FEATURE_RESP_SIZE 33u
+#define AUTO_UPDATE_FEATURE_CMD_DATA NULL
+#define AUTO_UPDATE_FEATURE_ENABLED BIT(5)
+
+#define AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE 0x22u
+#define AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE 0u
+#define AUTO_UPDATE_AUTHENTICATE_RESP_SIZE 1u
+#define AUTO_UPDATE_AUTHENTICATE_CMD_DATA NULL
+
+#define AUTO_UPDATE_PROGRAM_CMD_OPCODE 0x46u
+#define AUTO_UPDATE_PROGRAM_CMD_DATA_SIZE 0u
+#define AUTO_UPDATE_PROGRAM_RESP_SIZE 1u
+#define AUTO_UPDATE_PROGRAM_CMD_DATA NULL
+
+/*
+ * SPI Flash layout example:
+ * |------------------------------| 0x0000000
+ * | 1 KiB |
+ * | SPI "directories" |
+ * |------------------------------| 0x0000400
+ * | 1 MiB |
+ * | Reserved area |
+ * | Used for bitstream info |
+ * |------------------------------| 0x0100400
+ * | 20 MiB |
+ * | Golden Image |
+ * |------------------------------| 0x1500400
+ * | 20 MiB |
+ * | Auto Upgrade Image |
+ * |------------------------------| 0x2900400
+ * | 20 MiB |
+ * | Reserved for multi-image IAP |
+ * | Unused for Auto Upgrade |
+ * |------------------------------| 0x3D00400
+ * | ? B |
+ * | Unused |
+ * |------------------------------| 0x?
+ */
+#define AUTO_UPDATE_DIRECTORY_BASE 0u
+#define AUTO_UPDATE_DIRECTORY_WIDTH 4u
+#define AUTO_UPDATE_GOLDEN_INDEX 0u
+#define AUTO_UPDATE_UPGRADE_INDEX 1u
+#define AUTO_UPDATE_BLANK_INDEX 2u
+#define AUTO_UPDATE_GOLDEN_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_GOLDEN_INDEX)
+#define AUTO_UPDATE_UPGRADE_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_UPGRADE_INDEX)
+#define AUTO_UPDATE_BLANK_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_BLANK_INDEX)
+#define AUTO_UPDATE_DIRECTORY_SIZE SZ_1K
+#define AUTO_UPDATE_RESERVED_SIZE SZ_1M
+#define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_RESERVED_SIZE)
+
+#define AUTO_UPDATE_TIMEOUT_MS 60000
+
+struct mpfs_auto_update_priv {
+ struct mpfs_sys_controller *sys_controller;
+ struct device *dev;
+ struct mtd_info *flash;
+ struct fw_upload *fw_uploader;
+ struct completion programming_complete;
+ size_t size_per_bitstream;
+ bool cancel_request;
+};
+
+static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader, const u8 *data,
+ u32 size)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE;
+
+ /*
+ * Verifying the Golden Image is idealistic. It will be evaluated
+ * against the currently programmed image and thus may fail - due to
+ * either rollback protection (if its an older version than that in use)
+ * or if the version is the same as that of the in-use image.
+ * Extracting the information as to why a failure occurred is not
+ * currently possible due to limitations of the system controller
+ * driver. If those are fixed, verification of the Golden Image should
+ * be added here.
+ */
+
+ priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller);
+ if (!priv->flash)
+ return FW_UPLOAD_ERR_HW_ERROR;
+
+ erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
+
+ /*
+ * We need to calculate if we have enough space in the flash for the
+ * new image.
+ * First, chop off the first 1 KiB as it's reserved for the directory.
+ * The 1 MiB reserved for design info needs to be ignored also.
+ * All that remains is carved into 3 & rounded down to the erasesize.
+ * If this is smaller than the image size, we abort.
+ * There's also no need to consume more than 20 MiB per image.
+ */
+ priv->size_per_bitstream = priv->flash->size - SZ_1K - SZ_1M;
+ priv->size_per_bitstream = round_down(priv->size_per_bitstream / 3, erase_size);
+ if (priv->size_per_bitstream > 20 * SZ_1M)
+ priv->size_per_bitstream = 20 * SZ_1M;
+
+ if (priv->size_per_bitstream < size) {
+ dev_err(priv->dev,
+ "flash device has insufficient capacity to store this bitstream\n");
+ return FW_UPLOAD_ERR_INVALID_SIZE;
+ }
+
+ priv->cancel_request = false;
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void mpfs_auto_update_cancel(struct fw_upload *fw_uploader)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+
+ priv->cancel_request = true;
+}
+
+static enum fw_upload_err mpfs_auto_update_poll_complete(struct fw_upload *fw_uploader)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ int ret;
+
+ /*
+ * There is no meaningful way to get the status of the programming while
+ * it is in progress, so attempting anything other than waiting for it
+ * to complete would be misplaced.
+ */
+ ret = wait_for_completion_timeout(&priv->programming_complete,
+ msecs_to_jiffies(AUTO_UPDATE_TIMEOUT_MS));
+ if (ret)
+ return FW_UPLOAD_ERR_TIMEOUT;
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static int mpfs_auto_update_verify_image(struct fw_upload *fw_uploader)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ struct mpfs_mss_response *response;
+ struct mpfs_mss_msg *message;
+ u32 *response_msg;
+ int ret;
+
+ response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(response_msg),
+ GFP_KERNEL);
+ if (!response_msg)
+ return -ENOMEM;
+
+ response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL);
+ if (!response) {
+ ret = -ENOMEM;
+ goto free_response_msg;
+ }
+
+ message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL);
+ if (!message) {
+ ret = -ENOMEM;
+ goto free_response;
+ }
+
+ /*
+ * The system controller can verify that an image in the flash is valid.
+ * Rather than duplicate the check in this driver, call the relevant
+ * service from the system controller instead.
+ * This service has no command data and no response data. It overloads
+ * mbox_offset with the image index in the flash's SPI directory where
+ * the bitstream is located.
+ */
+ response->resp_msg = response_msg;
+ response->resp_size = AUTO_UPDATE_AUTHENTICATE_RESP_SIZE;
+ message->cmd_opcode = AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE;
+ message->cmd_data_size = AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE;
+ message->response = response;
+ message->cmd_data = AUTO_UPDATE_AUTHENTICATE_CMD_DATA;
+ message->mbox_offset = AUTO_UPDATE_UPGRADE_INDEX;
+ message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET;
+
+ dev_info(priv->dev, "Running verification of Upgrade Image\n");
+ ret = mpfs_blocking_transaction(priv->sys_controller, message);
+ if (ret | response->resp_status) {
+ dev_warn(priv->dev, "Verification of Upgrade Image failed!\n");
+ ret = ret ? ret : -EBADMSG;
+ }
+
+ dev_info(priv->dev, "Verification of Upgrade Image passed!\n");
+
+ devm_kfree(priv->dev, message);
+free_response:
+ devm_kfree(priv->dev, response);
+free_response_msg:
+ devm_kfree(priv->dev, response_msg);
+
+ return ret;
+}
+
+static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv, char *buffer,
+ u32 image_address, loff_t directory_address)
+{
+ struct erase_info erase;
+ size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE;
+ size_t bytes_written = 0, bytes_read = 0;
+ int ret;
+
+ erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
+
+ erase.addr = AUTO_UPDATE_DIRECTORY_BASE;
+ erase.len = erase_size;
+
+ /*
+ * We need to write the "SPI DIRECTORY" to the first 1 KiB, telling
+ * the system controller where to find the actual bitstream. Since
+ * this is spi-nor, we have to read the first eraseblock, erase that
+ * portion of the flash, modify the data and then write it back.
+ * There's no need to do this though if things are already the way they
+ * should be, so check and save the write in that case.
+ */
+ ret = mtd_read(priv->flash, AUTO_UPDATE_DIRECTORY_BASE, erase_size, &bytes_read,
+ (u_char *)buffer);
+ if (ret)
+ return ret;
+
+ if (bytes_read != erase_size)
+ return -EIO;
+
+ if ((*(u32 *)(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY) == image_address) &&
+ !(*(u32 *)(buffer + AUTO_UPDATE_BLANK_DIRECTORY)))
+ return 0;
+
+ ret = mtd_erase(priv->flash, &erase);
+ if (ret)
+ return ret;
+
+ /*
+ * Populate the image address and then zero out the next directory so
+ * that the system controller doesn't complain if in "Single Image"
+ * mode.
+ */
+ memcpy(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY, &image_address,
+ AUTO_UPDATE_DIRECTORY_WIDTH);
+ memset(buffer + AUTO_UPDATE_BLANK_DIRECTORY, 0x0, AUTO_UPDATE_DIRECTORY_WIDTH);
+
+ dev_info(priv->dev, "Writing the image address (%x) to the flash directory (%llx)\n",
+ image_address, directory_address);
+
+ ret = mtd_write(priv->flash, 0x0, erase_size, &bytes_written, (u_char *)buffer);
+ if (ret)
+ return ret;
+
+ if (bytes_written != erase_size)
+ return ret;
+
+ return 0;
+}
+
+static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const u8 *data,
+ u32 offset, u32 size, u32 *written)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ struct erase_info erase;
+ char *buffer;
+ loff_t directory_address = AUTO_UPDATE_UPGRADE_DIRECTORY;
+ size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE;
+ size_t bytes_written = 0;
+ u32 image_address;
+ int ret;
+
+ erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
+
+ image_address = AUTO_UPDATE_BITSTREAM_BASE +
+ AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream;
+
+ buffer = devm_kzalloc(priv->dev, erase_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ ret = mpfs_auto_update_set_image_address(priv, buffer, image_address, directory_address);
+ if (ret) {
+ dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * Now the .spi image itself can be written to the flash. Preservation
+ * of contents here is not important here, unlike the spi "directory"
+ * which must be RMWed.
+ */
+ erase.len = round_up(size, (size_t)priv->flash->erasesize);
+ erase.addr = image_address;
+
+ dev_info(priv->dev, "Erasing the flash at address (%x)\n", image_address);
+ ret = mtd_erase(priv->flash, &erase);
+ if (ret)
+ goto out;
+
+ /*
+ * No parsing etc of the bitstream is required. The system controller
+ * will do all of that itself - including verifying that the bitstream
+ * is valid.
+ */
+ dev_info(priv->dev, "Writing the image to the flash at address (%x)\n", image_address);
+ ret = mtd_write(priv->flash, (loff_t)image_address, size, &bytes_written, data);
+ if (ret)
+ goto out;
+
+ if (bytes_written != size) {
+ ret = -EIO;
+ goto out;
+ }
+
+ *written = bytes_written;
+
+out:
+ devm_kfree(priv->dev, buffer);
+ return ret;
+}
+
+static enum fw_upload_err mpfs_auto_update_write(struct fw_upload *fw_uploader, const u8 *data,
+ u32 offset, u32 size, u32 *written)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ enum fw_upload_err err = FW_UPLOAD_ERR_NONE;
+ int ret;
+
+ reinit_completion(&priv->programming_complete);
+
+ ret = mpfs_auto_update_write_bitstream(fw_uploader, data, offset, size, written);
+ if (ret) {
+ err = FW_UPLOAD_ERR_RW_ERROR;
+ goto out;
+ }
+
+ if (priv->cancel_request) {
+ err = FW_UPLOAD_ERR_CANCELED;
+ goto out;
+ }
+
+ ret = mpfs_auto_update_verify_image(fw_uploader);
+ if (ret)
+ err = FW_UPLOAD_ERR_FW_INVALID;
+
+out:
+ complete(&priv->programming_complete);
+
+ return err;
+}
+
+static const struct fw_upload_ops mpfs_auto_update_ops = {
+ .prepare = mpfs_auto_update_prepare,
+ .write = mpfs_auto_update_write,
+ .poll_complete = mpfs_auto_update_poll_complete,
+ .cancel = mpfs_auto_update_cancel,
+};
+
+static int mpfs_auto_update_available(struct mpfs_auto_update_priv *priv)
+{
+ struct mpfs_mss_response *response;
+ struct mpfs_mss_msg *message;
+ u32 *response_msg;
+ int ret;
+
+ response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(response_msg),
+ GFP_KERNEL);
+ if (!response_msg)
+ return -ENOMEM;
+
+ response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL);
+ if (!response)
+ return -ENOMEM;
+
+ message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL);
+ if (!message)
+ return -ENOMEM;
+
+ /*
+ * To verify that Auto Update is possible, the "Query Security Service
+ * Request" is performed.
+ * This service has no command data & does not overload mbox_offset.
+ */
+ response->resp_msg = response_msg;
+ response->resp_size = AUTO_UPDATE_FEATURE_RESP_SIZE;
+ message->cmd_opcode = AUTO_UPDATE_FEATURE_CMD_OPCODE;
+ message->cmd_data_size = AUTO_UPDATE_FEATURE_CMD_DATA_SIZE;
+ message->response = response;
+ message->cmd_data = AUTO_UPDATE_FEATURE_CMD_DATA;
+ message->mbox_offset = AUTO_UPDATE_DEFAULT_MBOX_OFFSET;
+ message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET;
+
+ ret = mpfs_blocking_transaction(priv->sys_controller, message);
+ if (ret)
+ return ret;
+
+ /*
+ * Currently, the system controller's firmware does not generate any
+ * interrupts for failed services, so mpfs_blocking_transaction() should
+ * time out & therefore return an error.
+ * Hitting this check is highly unlikely at present, but if the system
+ * controller's behaviour changes so that it does generate interrupts
+ * for failed services, it will be required.
+ */
+ if (response->resp_status)
+ return -EIO;
+
+ /*
+ * Bit 5 of byte 1 is "UL_Auto Update" & if it is set, Auto Update is
+ * not possible.
+ */
+ if (response_msg[1] & AUTO_UPDATE_FEATURE_ENABLED)
+ return -EPERM;
+
+ return 0;
+}
+
+static int mpfs_auto_update_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mpfs_auto_update_priv *priv;
+ struct fw_upload *fw_uploader;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->sys_controller = mpfs_sys_controller_get(dev);
+ if (IS_ERR(priv->sys_controller))
+ return dev_err_probe(dev, PTR_ERR(priv->sys_controller),
+ "Could not register as a sub device of the system controller\n");
+
+ priv->dev = dev;
+ platform_set_drvdata(pdev, priv);
+
+ ret = mpfs_auto_update_available(priv);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "The current bitstream does not support auto-update\n");
+
+ init_completion(&priv->programming_complete);
+
+ fw_uploader = firmware_upload_register(THIS_MODULE, dev, "mpfs-auto-update",
+ &mpfs_auto_update_ops, priv);
+ if (IS_ERR(fw_uploader))
+ return dev_err_probe(dev, PTR_ERR(fw_uploader),
+ "Failed to register the bitstream uploader\n");
+
+ priv->fw_uploader = fw_uploader;
+
+ return 0;
+}
+
+static void mpfs_auto_update_remove(struct platform_device *pdev)
+{
+ struct mpfs_auto_update_priv *priv = platform_get_drvdata(pdev);
+
+ firmware_upload_unregister(priv->fw_uploader);
+}
+
+static struct platform_driver mpfs_auto_update_driver = {
+ .driver = {
+ .name = "mpfs-auto-update",
+ },
+ .probe = mpfs_auto_update_probe,
+ .remove_new = mpfs_auto_update_remove,
+};
+module_platform_driver(mpfs_auto_update_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("PolarFire SoC Auto Update FPGA reprogramming");
diff --git a/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c b/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
index a33acdaf7b78..32188f098ef3 100644
--- a/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
+++ b/drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
@@ -325,8 +325,10 @@ static efi_status_t qsee_uefi_get_variable(struct qcuefi_client *qcuefi, const e
req_data->length = req_size;
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
- if (status < 0)
- return EFI_INVALID_PARAMETER;
+ if (status < 0) {
+ efi_status = EFI_INVALID_PARAMETER;
+ goto out_free;
+ }
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
@@ -471,8 +473,10 @@ static efi_status_t qsee_uefi_set_variable(struct qcuefi_client *qcuefi, const e
req_data->length = req_size;
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name, name_length);
- if (status < 0)
- return EFI_INVALID_PARAMETER;
+ if (status < 0) {
+ efi_status = EFI_INVALID_PARAMETER;
+ goto out_free;
+ }
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
@@ -563,8 +567,10 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
memcpy(((void *)req_data) + req_data->guid_offset, guid, req_data->guid_size);
status = ucs2_strscpy(((void *)req_data) + req_data->name_offset, name,
*name_size / sizeof(*name));
- if (status < 0)
- return EFI_INVALID_PARAMETER;
+ if (status < 0) {
+ efi_status = EFI_INVALID_PARAMETER;
+ goto out_free;
+ }
status = qcom_qseecom_app_send(qcuefi->client, req_data, req_size, rsp_data, rsp_size);
if (status) {
@@ -635,7 +641,7 @@ static efi_status_t qsee_uefi_get_next_variable(struct qcuefi_client *qcuefi,
* have already been validated above, causing this function to
* bail with EFI_BUFFER_TOO_SMALL.
*/
- return EFI_DEVICE_ERROR;
+ efi_status = EFI_DEVICE_ERROR;
}
out_free:
diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c
index 7041befc756a..8b9a2556de16 100644
--- a/drivers/firmware/ti_sci.c
+++ b/drivers/firmware/ti_sci.c
@@ -164,7 +164,7 @@ static int ti_sci_debugfs_create(struct platform_device *pdev,
{
struct device *dev = &pdev->dev;
struct resource *res;
- char debug_name[50] = "ti_sci_debug@";
+ char debug_name[50];
/* Debug region is optional */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -181,10 +181,10 @@ static int ti_sci_debugfs_create(struct platform_device *pdev,
/* Setup NULL termination */
info->debug_buffer[info->debug_region_size] = 0;
- info->d = debugfs_create_file(strncat(debug_name, dev_name(dev),
- sizeof(debug_name) -
- sizeof("ti_sci_debug@")),
- 0444, NULL, info, &ti_sci_debug_fops);
+ snprintf(debug_name, sizeof(debug_name), "ti_sci_debug@%s",
+ dev_name(dev));
+ info->d = debugfs_create_file(debug_name, 0444, NULL, info,
+ &ti_sci_debug_fops);
if (IS_ERR(info->d))
return PTR_ERR(info->d);