From befc1bab91171d25de22dbcbf41309582a63ecd7 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Fri, 2 Aug 2019 11:47:27 +0530 Subject: firmware: tegra: Move BPMP resume to noirq phase Modules like PCIe in Tegra194 need BPMP firmware services in noirq phase and hence move BPMP resume to noirq phase. This patch is verified on Tegra210, Tegra186 and Tegra194. Signed-off-by: Vidya Sagar Reviewed-by: Timo Alho Signed-off-by: Thierry Reding --- drivers/firmware/tegra/bpmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 19c56133234b..6741fcda0c37 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -804,7 +804,7 @@ static int __maybe_unused tegra_bpmp_resume(struct device *dev) } static const struct dev_pm_ops tegra_bpmp_pm_ops = { - .resume_early = tegra_bpmp_resume, + .resume_noirq = tegra_bpmp_resume, }; #if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ -- cgit v1.2.3 From beb91681a20abd8db7cfc177cb621864ce4e7967 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Wed, 31 Jul 2019 09:23:36 +0100 Subject: firmware: meson_sm: Mark chip struct as static const No need to be a global struct. Reviewed-by: Jerome Brunet Signed-off-by: Carlo Caione Signed-off-by: Kevin Hilman --- drivers/firmware/meson/meson_sm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 8d908a8e0d20..772ca6726e7b 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -35,7 +35,7 @@ struct meson_sm_chip { struct meson_sm_cmd cmd[]; }; -struct meson_sm_chip gxbb_chip = { +static const struct meson_sm_chip gxbb_chip = { .shmem_size = SZ_4K, .cmd_shmem_in_base = 0x82000020, .cmd_shmem_out_base = 0x82000021, -- cgit v1.2.3 From 8cde3c2153e8f57be884c0e73f18bc4de150e870 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Wed, 31 Jul 2019 09:23:39 +0100 Subject: firmware: meson_sm: Rework driver as a proper platform driver The secure monitor driver is currently a frankenstein driver which is registered as a platform driver but its functionality goes through a global struct accessed by the consumer drivers using exported helper functions. Try to tidy up the driver moving the firmware struct into the driver data and make the consumer drivers referencing the secure-monitor using a new property in the DT. Currently only the nvmem driver is using this API so we can fix it in the same commit. Reviewed-by: Jerome Brunet Signed-off-by: Carlo Caione Signed-off-by: Kevin Hilman --- drivers/firmware/meson/meson_sm.c | 94 ++++++++++++++++++++++++++------------- 1 file changed, 64 insertions(+), 30 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 772ca6726e7b..2e36a2aa274c 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -54,8 +54,6 @@ struct meson_sm_firmware { void __iomem *sm_shmem_out_base; }; -static struct meson_sm_firmware fw; - static u32 meson_sm_get_cmd(const struct meson_sm_chip *chip, unsigned int cmd_index) { @@ -90,6 +88,7 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) /** * meson_sm_call - generic SMC32 call to the secure-monitor * + * @fw: Pointer to secure-monitor firmware * @cmd_index: Index of the SMC32 function ID * @ret: Returned value * @arg0: SMC32 Argument 0 @@ -100,15 +99,15 @@ static void __iomem *meson_sm_map_shmem(u32 cmd_shmem, unsigned int size) * * Return: 0 on success, a negative value on error */ -int meson_sm_call(unsigned int cmd_index, u32 *ret, u32 arg0, - u32 arg1, u32 arg2, u32 arg3, u32 arg4) +int meson_sm_call(struct meson_sm_firmware *fw, unsigned int cmd_index, + u32 *ret, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { u32 cmd, lret; - if (!fw.chip) + if (!fw->chip) return -ENOENT; - cmd = meson_sm_get_cmd(fw.chip, cmd_index); + cmd = meson_sm_get_cmd(fw->chip, cmd_index); if (!cmd) return -EINVAL; @@ -124,6 +123,7 @@ EXPORT_SYMBOL(meson_sm_call); /** * meson_sm_call_read - retrieve data from secure-monitor * + * @fw: Pointer to secure-monitor firmware * @buffer: Buffer to store the retrieved data * @bsize: Size of the buffer * @cmd_index: Index of the SMC32 function ID @@ -137,22 +137,23 @@ EXPORT_SYMBOL(meson_sm_call); * When 0 is returned there is no guarantee about the amount of * data read and bsize bytes are copied in buffer. */ -int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, - u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) +int meson_sm_call_read(struct meson_sm_firmware *fw, void *buffer, + unsigned int bsize, unsigned int cmd_index, u32 arg0, + u32 arg1, u32 arg2, u32 arg3, u32 arg4) { u32 size; int ret; - if (!fw.chip) + if (!fw->chip) return -ENOENT; - if (!fw.chip->cmd_shmem_out_base) + if (!fw->chip->cmd_shmem_out_base) return -EINVAL; - if (bsize > fw.chip->shmem_size) + if (bsize > fw->chip->shmem_size) return -EINVAL; - if (meson_sm_call(cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) + if (meson_sm_call(fw, cmd_index, &size, arg0, arg1, arg2, arg3, arg4) < 0) return -EINVAL; if (size > bsize) @@ -164,7 +165,7 @@ int meson_sm_call_read(void *buffer, unsigned int bsize, unsigned int cmd_index, size = bsize; if (buffer) - memcpy(buffer, fw.sm_shmem_out_base, size); + memcpy(buffer, fw->sm_shmem_out_base, size); return ret; } @@ -173,6 +174,7 @@ EXPORT_SYMBOL(meson_sm_call_read); /** * meson_sm_call_write - send data to secure-monitor * + * @fw: Pointer to secure-monitor firmware * @buffer: Buffer containing data to send * @size: Size of the data to send * @cmd_index: Index of the SMC32 function ID @@ -184,23 +186,24 @@ EXPORT_SYMBOL(meson_sm_call_read); * * Return: size of sent data on success, a negative value on error */ -int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, - u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) +int meson_sm_call_write(struct meson_sm_firmware *fw, void *buffer, + unsigned int size, unsigned int cmd_index, u32 arg0, + u32 arg1, u32 arg2, u32 arg3, u32 arg4) { u32 written; - if (!fw.chip) + if (!fw->chip) return -ENOENT; - if (size > fw.chip->shmem_size) + if (size > fw->chip->shmem_size) return -EINVAL; - if (!fw.chip->cmd_shmem_in_base) + if (!fw->chip->cmd_shmem_in_base) return -EINVAL; - memcpy(fw.sm_shmem_in_base, buffer, size); + memcpy(fw->sm_shmem_in_base, buffer, size); - if (meson_sm_call(cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) + if (meson_sm_call(fw, cmd_index, &written, arg0, arg1, arg2, arg3, arg4) < 0) return -EINVAL; if (!written) @@ -210,6 +213,24 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index, } EXPORT_SYMBOL(meson_sm_call_write); +/** + * meson_sm_get - get pointer to meson_sm_firmware structure. + * + * @sm_node: Pointer to the secure-monitor Device Tree node. + * + * Return: NULL is the secure-monitor device is not ready. + */ +struct meson_sm_firmware *meson_sm_get(struct device_node *sm_node) +{ + struct platform_device *pdev = of_find_device_by_node(sm_node); + + if (!pdev) + return NULL; + + return platform_get_drvdata(pdev); +} +EXPORT_SYMBOL_GPL(meson_sm_get); + #define SM_CHIP_ID_LENGTH 119 #define SM_CHIP_ID_OFFSET 4 #define SM_CHIP_ID_SIZE 12 @@ -217,14 +238,18 @@ EXPORT_SYMBOL(meson_sm_call_write); static ssize_t serial_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct platform_device *pdev = to_platform_device(dev); + struct meson_sm_firmware *fw; uint8_t *id_buf; int ret; + fw = platform_get_drvdata(pdev); + id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL); if (!id_buf) return -ENOMEM; - ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, + ret = meson_sm_call_read(fw, id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID, 0, 0, 0, 0, 0); if (ret < 0) { kfree(id_buf); @@ -268,25 +293,34 @@ static const struct of_device_id meson_sm_ids[] = { static int __init meson_sm_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; const struct meson_sm_chip *chip; + struct meson_sm_firmware *fw; + + fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; - chip = of_match_device(meson_sm_ids, &pdev->dev)->data; + chip = of_match_device(meson_sm_ids, dev)->data; if (chip->cmd_shmem_in_base) { - fw.sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, - chip->shmem_size); - if (WARN_ON(!fw.sm_shmem_in_base)) + fw->sm_shmem_in_base = meson_sm_map_shmem(chip->cmd_shmem_in_base, + chip->shmem_size); + if (WARN_ON(!fw->sm_shmem_in_base)) goto out; } if (chip->cmd_shmem_out_base) { - 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)) + 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; } - fw.chip = chip; + fw->chip = chip; + + platform_set_drvdata(pdev, fw); + pr_info("secure-monitor enabled\n"); if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group)) @@ -295,7 +329,7 @@ static int __init meson_sm_probe(struct platform_device *pdev) return 0; out_in_base: - iounmap(fw.sm_shmem_in_base); + iounmap(fw->sm_shmem_in_base); out: return -EINVAL; } -- cgit v1.2.3 From 9be579f4c41f69c42278980af73e3b201f4159bb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 4 Sep 2019 20:48:35 +0300 Subject: firmware: meson_sm: use %*ph to print small buffer Use %*ph format to print small buffer as hex string. Signed-off-by: Andy Shevchenko Signed-off-by: Kevin Hilman --- drivers/firmware/meson/meson_sm.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 2e36a2aa274c..1d5b4d74f96d 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -256,19 +256,7 @@ static ssize_t serial_show(struct device *dev, struct device_attribute *attr, return ret; } - ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", - id_buf[SM_CHIP_ID_OFFSET + 0], - id_buf[SM_CHIP_ID_OFFSET + 1], - id_buf[SM_CHIP_ID_OFFSET + 2], - id_buf[SM_CHIP_ID_OFFSET + 3], - id_buf[SM_CHIP_ID_OFFSET + 4], - id_buf[SM_CHIP_ID_OFFSET + 5], - id_buf[SM_CHIP_ID_OFFSET + 6], - id_buf[SM_CHIP_ID_OFFSET + 7], - id_buf[SM_CHIP_ID_OFFSET + 8], - id_buf[SM_CHIP_ID_OFFSET + 9], - id_buf[SM_CHIP_ID_OFFSET + 10], - id_buf[SM_CHIP_ID_OFFSET + 11]); + ret = sprintf(buf, "%12phN\n", &id_buf[SM_CHIP_ID_OFFSET]); kfree(id_buf); -- cgit v1.2.3 From cf0fd404455ce13850cc15423a3c2958933de384 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 4 Sep 2019 10:54:58 +0300 Subject: firmware: imx: warn on unexpected RX The imx_scu_call_rpc function returns the result inside the same "msg" struct containing the transmitted message. This is implemented by holding a pointer to msg (which is usually on the stack) in sc_imx_rpc and writing to it from imx_scu_rx_callback. This means that if the have_resp parameter is incorrect or SCU sends an unexpected response for any reason the most likely result is kernel stack corruption. Fix this by only setting sc_imx_rpc.msg for the duration of the imx_scu_call_rpc call and warning in imx_scu_rx_callback if unset. Print the unexpected response data to help debugging. Signed-off-by: Leonard Crestez Acked-by: Anson Huang Signed-off-by: Shawn Guo --- drivers/firmware/imx/imx-scu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 04a24a863d6e..869be7a5172c 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -107,6 +107,12 @@ static void imx_scu_rx_callback(struct mbox_client *c, void *msg) struct imx_sc_rpc_msg *hdr; u32 *data = msg; + if (!sc_ipc->msg) { + dev_warn(sc_ipc->dev, "unexpected rx idx %d 0x%08x, ignore!\n", + sc_chan->idx, *data); + return; + } + if (sc_chan->idx == 0) { hdr = msg; sc_ipc->rx_size = hdr->size; @@ -165,7 +171,8 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) mutex_lock(&sc_ipc->lock); reinit_completion(&sc_ipc->done); - sc_ipc->msg = msg; + if (have_resp) + sc_ipc->msg = msg; sc_ipc->count = 0; ret = imx_scu_ipc_write(sc_ipc, msg); if (ret < 0) { @@ -187,6 +194,7 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) } out: + sc_ipc->msg = NULL; mutex_unlock(&sc_ipc->lock); dev_dbg(sc_ipc->dev, "RPC SVC done\n"); -- cgit v1.2.3 From 6b7fe77c334ae59fed9500140e08f4f896b36871 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 9 Aug 2019 14:22:40 +0100 Subject: arm/arm64: smccc/psci: add arm_smccc_1_1_get_conduit() SMCCC callers are currently amassing a collection of enums for the SMCCC conduit, and are having to dig into the PSCI driver's internals in order to figure out what to do. Let's clean this up, with common SMCCC_CONDUIT_* definitions, and an arm_smccc_1_1_get_conduit() helper that abstracts the PSCI driver's internal state. We can kill off the PSCI_CONDUIT_* definitions once we've migrated users over to the new interface. Signed-off-by: Mark Rutland Acked-by: Lorenzo Pieralisi Acked-by: Will Deacon Signed-off-by: Catalin Marinas --- drivers/firmware/psci/psci.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index 84f4ff351c62..eb797081d159 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -57,6 +57,21 @@ struct psci_operations psci_ops = { .smccc_version = SMCCC_VERSION_1_0, }; +enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void) +{ + if (psci_ops.smccc_version < SMCCC_VERSION_1_1) + return SMCCC_CONDUIT_NONE; + + switch (psci_ops.conduit) { + case PSCI_CONDUIT_SMC: + return SMCCC_CONDUIT_SMC; + case PSCI_CONDUIT_HVC: + return SMCCC_CONDUIT_HVC; + default: + return SMCCC_CONDUIT_NONE; + } +} + typedef unsigned long (psci_fn)(unsigned long, unsigned long, unsigned long, unsigned long); static psci_fn *invoke_psci_fn; -- cgit v1.2.3 From a5520eac4d2dafb7a48c1b0f1c486afcebd6fe0d Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 9 Aug 2019 14:22:43 +0100 Subject: firmware/psci: use common SMCCC_CONDUIT_* Now that we have common SMCCC_CONDUIT_* definitions, migrate the PSCI code over to them, and kill off the old PSCI_CONDUIT_* definitions. There should be no functional change as a result of this patch. Signed-off-by: Mark Rutland Acked-by: Lorenzo Pieralisi Acked-by: Will Deacon Signed-off-by: Catalin Marinas --- drivers/firmware/psci/psci.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index eb797081d159..b3b6c15e7b36 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -53,7 +53,7 @@ bool psci_tos_resident_on(int cpu) } struct psci_operations psci_ops = { - .conduit = PSCI_CONDUIT_NONE, + .conduit = SMCCC_CONDUIT_NONE, .smccc_version = SMCCC_VERSION_1_0, }; @@ -62,14 +62,7 @@ enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void) if (psci_ops.smccc_version < SMCCC_VERSION_1_1) return SMCCC_CONDUIT_NONE; - switch (psci_ops.conduit) { - case PSCI_CONDUIT_SMC: - return SMCCC_CONDUIT_SMC; - case PSCI_CONDUIT_HVC: - return SMCCC_CONDUIT_HVC; - default: - return SMCCC_CONDUIT_NONE; - } + return psci_ops.conduit; } typedef unsigned long (psci_fn)(unsigned long, unsigned long, @@ -227,13 +220,13 @@ static unsigned long psci_migrate_info_up_cpu(void) 0, 0, 0); } -static void set_conduit(enum psci_conduit conduit) +static void set_conduit(enum arm_smccc_conduit conduit) { switch (conduit) { - case PSCI_CONDUIT_HVC: + case SMCCC_CONDUIT_HVC: invoke_psci_fn = __invoke_psci_fn_hvc; break; - case PSCI_CONDUIT_SMC: + case SMCCC_CONDUIT_SMC: invoke_psci_fn = __invoke_psci_fn_smc; break; default: @@ -255,9 +248,9 @@ static int get_set_conduit_method(struct device_node *np) } if (!strcmp("hvc", method)) { - set_conduit(PSCI_CONDUIT_HVC); + set_conduit(SMCCC_CONDUIT_HVC); } else if (!strcmp("smc", method)) { - set_conduit(PSCI_CONDUIT_SMC); + set_conduit(SMCCC_CONDUIT_SMC); } else { pr_warn("invalid \"method\" property: %s\n", method); return -EINVAL; @@ -598,9 +591,9 @@ int __init psci_acpi_init(void) pr_info("probing for conduit method from ACPI.\n"); if (acpi_psci_use_hvc()) - set_conduit(PSCI_CONDUIT_HVC); + set_conduit(SMCCC_CONDUIT_HVC); else - set_conduit(PSCI_CONDUIT_SMC); + set_conduit(SMCCC_CONDUIT_SMC); return psci_probe(); } -- cgit v1.2.3 From e6ea46511b1ae8c4491904c79411fcd29139af14 Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Fri, 9 Aug 2019 14:22:44 +0100 Subject: firmware: arm_sdei: use common SMCCC_CONDUIT_* Now that we have common definitions for SMCCC conduits, move the SDEI code over to them, and remove the SDEI-specific definitions. There should be no functional change as a result of this patch. Signed-off-by: Mark Rutland Acked-by: Lorenzo Pieralisi Acked-by: James Morse Acked-by: Will Deacon Signed-off-by: Catalin Marinas --- drivers/firmware/arm_sdei.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 9cd70d1a5622..a479023fa036 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -967,29 +967,29 @@ static int sdei_get_conduit(struct platform_device *pdev) if (np) { if (of_property_read_string(np, "method", &method)) { pr_warn("missing \"method\" property\n"); - return CONDUIT_INVALID; + return SMCCC_CONDUIT_NONE; } if (!strcmp("hvc", method)) { sdei_firmware_call = &sdei_smccc_hvc; - return CONDUIT_HVC; + return SMCCC_CONDUIT_HVC; } else if (!strcmp("smc", method)) { sdei_firmware_call = &sdei_smccc_smc; - return CONDUIT_SMC; + return SMCCC_CONDUIT_SMC; } pr_warn("invalid \"method\" property: %s\n", method); } else if (IS_ENABLED(CONFIG_ACPI) && !acpi_disabled) { if (acpi_psci_use_hvc()) { sdei_firmware_call = &sdei_smccc_hvc; - return CONDUIT_HVC; + return SMCCC_CONDUIT_HVC; } else { sdei_firmware_call = &sdei_smccc_smc; - return CONDUIT_SMC; + return SMCCC_CONDUIT_SMC; } } - return CONDUIT_INVALID; + return SMCCC_CONDUIT_NONE; } static int sdei_probe(struct platform_device *pdev) -- cgit v1.2.3 From 51f5afabc07a13e3d030076c772a1c36e1687b99 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 7 Oct 2019 09:15:59 +0800 Subject: firmware: imx: Skip return value check for some special SCU firmware APIs The SCU firmware does NOT always have return value stored in message header's function element even the API has response data, those special APIs are defined as void function in SCU firmware, so they should be treated as return success always. Signed-off-by: Anson Huang Reviewed-by: Marco Felsch Signed-off-by: Shawn Guo --- drivers/firmware/imx/imx-scu.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 869be7a5172c..03b43b7a6d1d 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -162,6 +162,7 @@ static int imx_scu_ipc_write(struct imx_sc_ipc *sc_ipc, void *msg) */ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) { + uint8_t saved_svc, saved_func; struct imx_sc_rpc_msg *hdr; int ret; @@ -171,8 +172,11 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) mutex_lock(&sc_ipc->lock); reinit_completion(&sc_ipc->done); - if (have_resp) + if (have_resp) { sc_ipc->msg = msg; + saved_svc = ((struct imx_sc_rpc_msg *)msg)->svc; + saved_func = ((struct imx_sc_rpc_msg *)msg)->func; + } sc_ipc->count = 0; ret = imx_scu_ipc_write(sc_ipc, msg); if (ret < 0) { @@ -191,6 +195,16 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) /* response status is stored in hdr->func field */ hdr = msg; ret = hdr->func; + /* + * Some special SCU firmware APIs do NOT have return value + * in hdr->func, but they do have response data, those special + * APIs are defined as void function in SCU firmware, so they + * should be treated as return success always. + */ + if ((saved_svc == IMX_SC_RPC_SVC_MISC) && + (saved_func == IMX_SC_MISC_FUNC_UNIQUE_ID || + saved_func == IMX_SC_MISC_FUNC_GET_BUTTON_STATUS)) + ret = 0; } out: -- cgit v1.2.3 From 81dde26de9c08bb04c4962a15608778aaffb3cf9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 14 Oct 2019 21:41:24 +0200 Subject: firmware: dmi: Fix unlikely out-of-bounds read in save_mem_devices Before reading the Extended Size field, we should ensure it fits in the DMI record. There is already a record length check but it does not cover that field. It would take a seriously corrupted DMI table to hit that bug, so no need to worry, but we should still fix it. Signed-off-by: Jean Delvare Fixes: 6deae96b42eb ("firmware, DMI: Add function to look up a handle and return DIMM size") Cc: Tony Luck Cc: Borislav Petkov --- drivers/firmware/dmi_scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 35ed56b9c34f..1e21fc3e9851 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -408,7 +408,7 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) bytes = ~0ull; else if (size & 0x8000) bytes = (u64)(size & 0x7fff) << 10; - else if (size != 0x7fff) + else if (size != 0x7fff || dm->length < 0x20) bytes = (u64)size << 20; else bytes = (u64)get_unaligned((u32 *)&d[0x1C]) << 20; -- cgit v1.2.3 From af3f1afac38d34083faad852172d0ec82749c046 Mon Sep 17 00:00:00 2001 From: Jolly Shah Date: Mon, 7 Oct 2019 11:52:23 -0700 Subject: firmware: xilinx: Add support for versal soc Versal is xilinx's next generation soc. This patch adds driver support required to be compatible with versal device. Signed-off-by: Jolly Shah Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index fd3d83745208..75bdfaa08380 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -711,8 +711,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) int ret; np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); - if (!np) - return 0; + if (!np) { + np = of_find_compatible_node(NULL, NULL, "xlnx,versal"); + if (!np) + return 0; + } of_node_put(np); ret = get_set_conduit_method(dev->of_node); @@ -770,6 +773,7 @@ static int zynqmp_firmware_remove(struct platform_device *pdev) static const struct of_device_id zynqmp_firmware_of_match[] = { {.compatible = "xlnx,zynqmp-firmware"}, + {.compatible = "xlnx,versal-firmware"}, {}, }; MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match); -- cgit v1.2.3 From 0e4e8cc30a2940c57448af1376e40d3c0996fb29 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 14 Oct 2019 18:32:28 +0300 Subject: firmware: imx: Remove call to devm_of_platform_populate IMX DSP device is created by SOF layer. The current call to devm_of_platform_populate is not needed and it doesn't produce any effects. Fixes: ffbf23d50353915d ("firmware: imx: Add DSP IPC protocol interface) Signed-off-by: Daniel Baluta Signed-off-by: Shawn Guo --- drivers/firmware/imx/imx-dsp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/imx-dsp.c b/drivers/firmware/imx/imx-dsp.c index a43d2db5cbdb..4265e9dbed84 100644 --- a/drivers/firmware/imx/imx-dsp.c +++ b/drivers/firmware/imx/imx-dsp.c @@ -114,7 +114,7 @@ static int imx_dsp_probe(struct platform_device *pdev) dev_info(dev, "NXP i.MX DSP IPC initialized\n"); - return devm_of_platform_populate(dev); + return 0; out: kfree(chan_name); for (j = 0; j < i; j++) { -- cgit v1.2.3 From e4f9eefbb8a976bb86dbdc9d2dd1a2a113801464 Mon Sep 17 00:00:00 2001 From: "Ben Dooks (Codethink)" Date: Tue, 22 Oct 2019 16:40:09 +0100 Subject: firmware: imx: add missing include of Include for the declarations of the functions exported from this driver. This fixes the following sparse warnings: drivers/firmware/imx/imx-scu-irq.c:45:5: warning: symbol 'imx_scu_irq_register_notifier' was not declared. Should it be static? drivers/firmware/imx/imx-scu-irq.c:52:5: warning: symbol 'imx_scu_irq_unregister_notifier' was not declared. Should it be static? drivers/firmware/imx/imx-scu-irq.c:97:5: warning: symbol 'imx_scu_irq_group_enable' was not declared. Should it be static? drivers/firmware/imx/imx-scu-irq.c:130:5: warning: symbol 'imx_scu_enable_general_irq_channel' was not declared. Should it be static? Signed-off-by: Ben Dooks (Codethink) Signed-off-by: Shawn Guo --- drivers/firmware/imx/imx-scu-irq.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c index 687121f8c4d5..db655e87cdc8 100644 --- a/drivers/firmware/imx/imx-scu-irq.c +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -8,6 +8,7 @@ #include #include +#include #include #define IMX_SC_IRQ_FUNC_ENABLE 1 -- cgit v1.2.3 From 1770093c5bed404ac69b04dc8b9e62a2c4db944a Mon Sep 17 00:00:00 2001 From: Nikolaus Voss Date: Fri, 25 Oct 2019 14:36:53 -0700 Subject: ACPICA: make acpi_load_table() return table index ACPICA commit d1716a829d19be23277d9157c575a03b9abb7457 For unloading an ACPI table, it is necessary to provide the index of the table. The method intended for dynamically loading or hotplug addition of tables, acpi_load_table(), should provide this information via an optional pointer to the loaded table index. This patch fixes the table unload function of acpi_configfs. Reported-by: Andy Shevchenko Fixes: d06c47e3dd07f ("ACPI: configfs: Resolve objects on host-directed table loads") Link: https://github.com/acpica/acpica/commit/d1716a82 Signed-off-by: Nikolaus Voss Signed-off-by: Erik Schmauss Signed-off-by: Bob Moore Tested-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 69f00f7453a3..0d65cb21519d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -296,7 +296,7 @@ static __init int efivar_ssdt_load(void) goto free_data; } - ret = acpi_load_table(data); + ret = acpi_load_table(data, NULL); if (ret) { pr_err("failed to load table: %d\n", ret); goto free_data; -- cgit v1.2.3 From 0b6b30c65621fc11a799ca71241f52d8fd9e334c Mon Sep 17 00:00:00 2001 From: Narendra K Date: Tue, 29 Oct 2019 18:37:50 +0100 Subject: efi: Make CONFIG_EFI_RCI2_TABLE selectable on x86 only For the EFI_RCI2_TABLE Kconfig option, 'make oldconfig' asks the user for input on platforms where the option may not be applicable. This patch modifies the Kconfig option to ask the user for input only when CONFIG_X86 or CONFIG_COMPILE_TEST is set to y. Suggested-by: Geert Uytterhoeven Reported-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Signed-off-by: Narendra K Signed-off-by: Ard Biesheuvel Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191029173755.27149-2-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 178ee8106828..b248870a9806 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -182,6 +182,7 @@ config RESET_ATTACK_MITIGATION config EFI_RCI2_TABLE bool "EFI Runtime Configuration Interface Table Version 2 Support" + depends on X86 || COMPILE_TEST help Displays the content of the Runtime Configuration Interface Table version 2 on Dell EMC PowerEdge systems as a binary -- cgit v1.2.3 From 2bb6a81633cb47dcba4c9f75605cbe49e6b73d60 Mon Sep 17 00:00:00 2001 From: Jerry Snitselaar Date: Tue, 29 Oct 2019 18:37:51 +0100 Subject: efi/tpm: Return -EINVAL when determining tpm final events log size fails Currently nothing checks the return value of efi_tpm_eventlog_init(), but in case that changes in the future make sure an error is returned when it fails to determine the tpm final events log size. Suggested-by: Dan Carpenter Signed-off-by: Jerry Snitselaar Signed-off-by: Ard Biesheuvel Reviewed-by: Jarkko Sakkinen Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Fixes: e658c82be556 ("efi/tpm: Only set 'efi_tpm_final_log_size' after ...") Link: https://lkml.kernel.org/r/20191029173755.27149-3-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/tpm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/tpm.c b/drivers/firmware/efi/tpm.c index ebd7977653a8..31f9f0e369b9 100644 --- a/drivers/firmware/efi/tpm.c +++ b/drivers/firmware/efi/tpm.c @@ -88,6 +88,7 @@ int __init efi_tpm_eventlog_init(void) if (tbl_size < 0) { pr_err(FW_BUG "Failed to parse event in TPM Final Events Log\n"); + ret = -EINVAL; goto out_calc; } -- cgit v1.2.3 From 18b915ac6b0ac5ba7ded03156860f60a9f16df2b Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 29 Oct 2019 18:37:52 +0100 Subject: efi/random: Treat EFI_RNG_PROTOCOL output as bootloader randomness Commit 428826f5358c ("fdt: add support for rng-seed") introduced add_bootloader_randomness(), permitting randomness provided by the bootloader or firmware to be credited as entropy. However, the fact that the UEFI support code was already wired into the RNG subsystem via a call to add_device_randomness() was overlooked, and so it was not converted at the same time. Note that this UEFI (v2.4 or newer) feature is currently only implemented for EFI stub booting on ARM, and further note that CONFIG_RANDOM_TRUST_BOOTLOADER must be enabled, and this should be done only if there indeed is sufficient trust in the bootloader _and_ its source of randomness. [ ardb: update commit log ] Tested-by: Bhupesh Sharma Signed-off-by: Dominik Brodowski Signed-off-by: Ard Biesheuvel Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191029173755.27149-4-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 69f00f7453a3..e98bbf8e56d9 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -554,7 +554,7 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, sizeof(*seed) + size); if (seed != NULL) { pr_notice("seeding entropy pool\n"); - add_device_randomness(seed->bits, seed->size); + add_bootloader_randomness(seed->bits, seed->size); early_memunmap(seed, sizeof(*seed) + size); } else { pr_err("Could not map UEFI random seed!\n"); -- cgit v1.2.3 From 41cd96fa149b29684ebd38759fefb07f9c7d5276 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 29 Oct 2019 18:37:53 +0100 Subject: efi: libstub/arm: Account for firmware reserved memory at the base of RAM The EFI stubloader for ARM starts out by allocating a 32 MB window at the base of RAM, in order to ensure that the decompressor (which blindly copies the uncompressed kernel into that window) does not overwrite other allocations that are made while running in the context of the EFI firmware. In some cases, (e.g., U-Boot running on the Raspberry Pi 2), this is causing boot failures because this initial allocation conflicts with a page of reserved memory at the base of RAM that contains the SMP spin tables and other pieces of firmware data and which was put there by the bootloader under the assumption that the TEXT_OFFSET window right below the kernel is only used partially during early boot, and will be left alone once the memory reservations are processed and taken into account. So let's permit reserved memory regions to exist in the region starting at the base of RAM, and ending at TEXT_OFFSET - 5 * PAGE_SIZE, which is the window below the kernel that is not touched by the early boot code. Tested-by: Guillaume Gardet Signed-off-by: Ard Biesheuvel Acked-by: Chester Lin Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191029173755.27149-5-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/libstub/Makefile | 1 + drivers/firmware/efi/libstub/arm32-stub.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 0460c7581220..ee0661ddb25b 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -52,6 +52,7 @@ lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o random.o \ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o +CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) # diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index e8f7aefb6813..ffa242ad0a82 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -195,6 +195,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, unsigned long dram_base, efi_loaded_image_t *image) { + unsigned long kernel_base; efi_status_t status; /* @@ -204,9 +205,18 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, * loaded. These assumptions are made by the decompressor, * before any memory map is available. */ - dram_base = round_up(dram_base, SZ_128M); + kernel_base = round_up(dram_base, SZ_128M); - status = reserve_kernel_base(sys_table, dram_base, reserve_addr, + /* + * Note that some platforms (notably, the Raspberry Pi 2) put + * spin-tables and other pieces of firmware at the base of RAM, + * abusing the fact that the window of TEXT_OFFSET bytes at the + * base of the kernel image is only partially used at the moment. + * (Up to 5 pages are used for the swapper page tables) + */ + kernel_base += TEXT_OFFSET - 5 * PAGE_SIZE; + + status = reserve_kernel_base(sys_table, kernel_base, reserve_addr, reserve_size); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Unable to allocate memory for uncompressed kernel.\n"); @@ -220,7 +230,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, *image_size = image->image_size; status = efi_relocate_kernel(sys_table, image_addr, *image_size, *image_size, - dram_base + MAX_UNCOMP_KERNEL_SIZE, 0); + kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to relocate kernel.\n"); efi_free(sys_table, *reserve_size, *reserve_addr); -- cgit v1.2.3 From 220dd7699c46d5940115bd797b01b2ab047c87b8 Mon Sep 17 00:00:00 2001 From: Kairui Song Date: Tue, 29 Oct 2019 18:37:54 +0100 Subject: x86, efi: Never relocate kernel below lowest acceptable address Currently, kernel fails to boot on some HyperV VMs when using EFI. And it's a potential issue on all x86 platforms. It's caused by broken kernel relocation on EFI systems, when below three conditions are met: 1. Kernel image is not loaded to the default address (LOAD_PHYSICAL_ADDR) by the loader. 2. There isn't enough room to contain the kernel, starting from the default load address (eg. something else occupied part the region). 3. In the memmap provided by EFI firmware, there is a memory region starts below LOAD_PHYSICAL_ADDR, and suitable for containing the kernel. EFI stub will perform a kernel relocation when condition 1 is met. But due to condition 2, EFI stub can't relocate kernel to the preferred address, so it fallback to ask EFI firmware to alloc lowest usable memory region, got the low region mentioned in condition 3, and relocated kernel there. It's incorrect to relocate the kernel below LOAD_PHYSICAL_ADDR. This is the lowest acceptable kernel relocation address. The first thing goes wrong is in arch/x86/boot/compressed/head_64.S. Kernel decompression will force use LOAD_PHYSICAL_ADDR as the output address if kernel is located below it. Then the relocation before decompression, which move kernel to the end of the decompression buffer, will overwrite other memory region, as there is no enough memory there. To fix it, just don't let EFI stub relocate the kernel to any address lower than lowest acceptable address. [ ardb: introduce efi_low_alloc_above() to reduce the scope of the change ] Signed-off-by: Kairui Song Signed-off-by: Ard Biesheuvel Acked-by: Jarkko Sakkinen Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191029173755.27149-6-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/libstub/arm32-stub.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index ffa242ad0a82..41213bf5fcf5 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -230,7 +230,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, *image_size = image->image_size; status = efi_relocate_kernel(sys_table, image_addr, *image_size, *image_size, - kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0); + kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0); if (status != EFI_SUCCESS) { pr_efi_err(sys_table, "Failed to relocate kernel.\n"); efi_free(sys_table, *reserve_size, *reserve_addr); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 3caae7f2cf56..35dbc2791c97 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -260,11 +260,11 @@ fail: } /* - * Allocate at the lowest possible address. + * Allocate at the lowest possible address that is not below 'min'. */ -efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, - unsigned long size, unsigned long align, - unsigned long *addr) +efi_status_t efi_low_alloc_above(efi_system_table_t *sys_table_arg, + unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min) { unsigned long map_size, desc_size, buff_size; efi_memory_desc_t *map; @@ -311,13 +311,8 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg, start = desc->phys_addr; end = start + desc->num_pages * EFI_PAGE_SIZE; - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. Skip the first 8 - * bytes so we start at a nice even number. - */ - if (start == 0x0) - start += 8; + if (start < min) + start = min; start = round_up(start, align); if ((start + size) > end) @@ -698,7 +693,8 @@ efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, unsigned long image_size, unsigned long alloc_size, unsigned long preferred_addr, - unsigned long alignment) + unsigned long alignment, + unsigned long min_addr) { unsigned long cur_image_addr; unsigned long new_addr = 0; @@ -731,8 +727,8 @@ efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, * possible. */ if (status != EFI_SUCCESS) { - status = efi_low_alloc(sys_table_arg, alloc_size, alignment, - &new_addr); + status = efi_low_alloc_above(sys_table_arg, alloc_size, + alignment, &new_addr, min_addr); } if (status != EFI_SUCCESS) { pr_efi_err(sys_table_arg, "Failed to allocate usable memory for kernel.\n"); -- cgit v1.2.3 From 359efcc2c910117d2faf704ce154e91fc976d37f Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 29 Oct 2019 18:37:55 +0100 Subject: efi/efi_test: Lock down /dev/efi_test and require CAP_SYS_ADMIN The driver exposes EFI runtime services to user-space through an IOCTL interface, calling the EFI services function pointers directly without using the efivar API. Disallow access to the /dev/efi_test character device when the kernel is locked down to prevent arbitrary user-space to call EFI runtime services. Also require CAP_SYS_ADMIN to open the chardev to prevent unprivileged users to call the EFI runtime services, instead of just relying on the chardev file mode bits for this. The main user of this driver is the fwts [0] tool that already checks if the effective user ID is 0 and fails otherwise. So this change shouldn't cause any regression to this tool. [0]: https://wiki.ubuntu.com/FirmwareTestSuite/Reference/uefivarinfo Signed-off-by: Javier Martinez Canillas Signed-off-by: Ard Biesheuvel Acked-by: Laszlo Ersek Acked-by: Matthew Garrett Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191029173755.27149-7-ardb@kernel.org Signed-off-by: Ingo Molnar --- drivers/firmware/efi/test/efi_test.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c index 877745c3aaf2..7baf48c01e72 100644 --- a/drivers/firmware/efi/test/efi_test.c +++ b/drivers/firmware/efi/test/efi_test.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -717,6 +718,13 @@ static long efi_test_ioctl(struct file *file, unsigned int cmd, static int efi_test_open(struct inode *inode, struct file *file) { + int ret = security_locked_down(LOCKDOWN_EFI_TEST); + + if (ret) + return ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; /* * nothing special to do here * We do accept multiple open files at the same time as we -- cgit v1.2.3 From 246880958ac93989c97c73ae1e60b78b4c4c88c5 Mon Sep 17 00:00:00 2001 From: Vikas Gupta Date: Thu, 31 Oct 2019 15:38:50 +0530 Subject: firmware: broadcom: add OP-TEE based BNXT f/w manager This driver registers on TEE bus to interact with OP-TEE based BNXT firmware management modules Cc: Jakub Kicinski Reported-by: kbuild test robot Signed-off-by: Vikas Gupta Signed-off-by: Sheetal Tigadoli Signed-off-by: David S. Miller --- drivers/firmware/broadcom/Kconfig | 8 + drivers/firmware/broadcom/Makefile | 1 + drivers/firmware/broadcom/tee_bnxt_fw.c | 279 ++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 drivers/firmware/broadcom/tee_bnxt_fw.c (limited to 'drivers/firmware') diff --git a/drivers/firmware/broadcom/Kconfig b/drivers/firmware/broadcom/Kconfig index d03ed8e43ad7..8e3d355a637a 100644 --- a/drivers/firmware/broadcom/Kconfig +++ b/drivers/firmware/broadcom/Kconfig @@ -22,3 +22,11 @@ config BCM47XX_SPROM In case of SoC devices SPROM content is stored on a flash used by bootloader firmware CFE. This driver provides method to ssb and bcma drivers to read SPROM on SoC. + +config TEE_BNXT_FW + tristate "Broadcom BNXT firmware manager" + depends on (ARCH_BCM_IPROC && OPTEE) || (COMPILE_TEST && TEE) + default ARCH_BCM_IPROC + help + This module help to manage firmware on Broadcom BNXT device. The module + registers on tee bus and invoke calls to manage firmware on BNXT device. diff --git a/drivers/firmware/broadcom/Makefile b/drivers/firmware/broadcom/Makefile index 72c7fdc20c77..17c5061c47a7 100644 --- a/drivers/firmware/broadcom/Makefile +++ b/drivers/firmware/broadcom/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx_sprom.o +obj-$(CONFIG_TEE_BNXT_FW) += tee_bnxt_fw.o diff --git a/drivers/firmware/broadcom/tee_bnxt_fw.c b/drivers/firmware/broadcom/tee_bnxt_fw.c new file mode 100644 index 000000000000..5b7ef89eb701 --- /dev/null +++ b/drivers/firmware/broadcom/tee_bnxt_fw.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Broadcom. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define MAX_SHM_MEM_SZ SZ_4M + +#define MAX_TEE_PARAM_ARRY_MEMB 4 + +enum ta_cmd { + /* + * TA_CMD_BNXT_FASTBOOT - boot bnxt device by copying f/w into sram + * + * param[0] unused + * param[1] unused + * param[2] unused + * param[3] unused + * + * Result: + * TEE_SUCCESS - Invoke command success + * TEE_ERROR_ITEM_NOT_FOUND - Corrupt f/w image found on memory + */ + TA_CMD_BNXT_FASTBOOT = 0, + + /* + * TA_CMD_BNXT_COPY_COREDUMP - copy the core dump into shm + * + * param[0] (inout memref) - Coredump buffer memory reference + * param[1] (in value) - value.a: offset, data to be copied from + * value.b: size of data to be copied + * param[2] unused + * param[3] unused + * + * Result: + * TEE_SUCCESS - Invoke command success + * TEE_ERROR_BAD_PARAMETERS - Incorrect input param + * TEE_ERROR_ITEM_NOT_FOUND - Corrupt core dump + */ + TA_CMD_BNXT_COPY_COREDUMP = 3, +}; + +/** + * struct tee_bnxt_fw_private - OP-TEE bnxt private data + * @dev: OP-TEE based bnxt device. + * @ctx: OP-TEE context handler. + * @session_id: TA session identifier. + */ +struct tee_bnxt_fw_private { + struct device *dev; + struct tee_context *ctx; + u32 session_id; + struct tee_shm *fw_shm_pool; +}; + +static struct tee_bnxt_fw_private pvt_data; + +static void prepare_args(int cmd, + struct tee_ioctl_invoke_arg *arg, + struct tee_param *param) +{ + memset(arg, 0, sizeof(*arg)); + memset(param, 0, MAX_TEE_PARAM_ARRY_MEMB * sizeof(*param)); + + arg->func = cmd; + arg->session = pvt_data.session_id; + arg->num_params = MAX_TEE_PARAM_ARRY_MEMB; + + /* Fill invoke cmd params */ + switch (cmd) { + case TA_CMD_BNXT_COPY_COREDUMP: + param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; + param[0].u.memref.shm = pvt_data.fw_shm_pool; + param[0].u.memref.size = MAX_SHM_MEM_SZ; + param[0].u.memref.shm_offs = 0; + param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; + break; + case TA_CMD_BNXT_FASTBOOT: + default: + /* Nothing to do */ + break; + } +} + +/** + * tee_bnxt_fw_load() - Load the bnxt firmware + * Uses an OP-TEE call to start a secure + * boot process. + * Returns 0 on success, negative errno otherwise. + */ +int tee_bnxt_fw_load(void) +{ + int ret = 0; + struct tee_ioctl_invoke_arg arg; + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB]; + + if (!pvt_data.ctx) + return -ENODEV; + + prepare_args(TA_CMD_BNXT_FASTBOOT, &arg, param); + + ret = tee_client_invoke_func(pvt_data.ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(pvt_data.dev, + "TA_CMD_BNXT_FASTBOOT invoke failed TEE err: %x, ret:%x\n", + arg.ret, ret); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(tee_bnxt_fw_load); + +/** + * tee_bnxt_copy_coredump() - Copy coredump from the allocated memory + * Uses an OP-TEE call to copy coredump + * @buf: destination buffer where core dump is copied into + * @offset: offset from the base address of core dump area + * @size: size of the dump + * + * Returns 0 on success, negative errno otherwise. + */ +int tee_bnxt_copy_coredump(void *buf, u32 offset, u32 size) +{ + struct tee_ioctl_invoke_arg arg; + struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB]; + void *core_data; + u32 rbytes = size; + u32 nbytes = 0; + int ret = 0; + + if (!pvt_data.ctx) + return -ENODEV; + + prepare_args(TA_CMD_BNXT_COPY_COREDUMP, &arg, param); + + while (rbytes) { + nbytes = rbytes; + + nbytes = min_t(u32, rbytes, param[0].u.memref.size); + + /* Fill additional invoke cmd params */ + param[1].u.value.a = offset; + param[1].u.value.b = nbytes; + + ret = tee_client_invoke_func(pvt_data.ctx, &arg, param); + if (ret < 0 || arg.ret != 0) { + dev_err(pvt_data.dev, + "TA_CMD_BNXT_COPY_COREDUMP invoke failed TEE err: %x, ret:%x\n", + arg.ret, ret); + return -EINVAL; + } + + core_data = tee_shm_get_va(pvt_data.fw_shm_pool, 0); + if (IS_ERR(core_data)) { + dev_err(pvt_data.dev, "tee_shm_get_va failed\n"); + return PTR_ERR(core_data); + } + + memcpy(buf, core_data, nbytes); + + rbytes -= nbytes; + buf += nbytes; + offset += nbytes; + } + + return 0; +} +EXPORT_SYMBOL(tee_bnxt_copy_coredump); + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return (ver->impl_id == TEE_IMPL_ID_OPTEE); +} + +static int tee_bnxt_fw_probe(struct device *dev) +{ + struct tee_client_device *bnxt_device = to_tee_client_device(dev); + int ret, err = -ENODEV; + struct tee_ioctl_open_session_arg sess_arg; + struct tee_shm *fw_shm_pool; + + memset(&sess_arg, 0, sizeof(sess_arg)); + + /* Open context with TEE driver */ + pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, + NULL); + if (IS_ERR(pvt_data.ctx)) + return -ENODEV; + + /* Open session with Bnxt load Trusted App */ + memcpy(sess_arg.uuid, bnxt_device->id.uuid.b, TEE_IOCTL_UUID_LEN); + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; + sess_arg.num_params = 0; + + ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, err: %x\n", + sess_arg.ret); + err = -EINVAL; + goto out_ctx; + } + pvt_data.session_id = sess_arg.session; + + pvt_data.dev = dev; + + fw_shm_pool = tee_shm_alloc(pvt_data.ctx, MAX_SHM_MEM_SZ, + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF); + if (IS_ERR(fw_shm_pool)) { + tee_client_close_context(pvt_data.ctx); + dev_err(pvt_data.dev, "tee_shm_alloc failed\n"); + err = PTR_ERR(fw_shm_pool); + goto out_sess; + } + + pvt_data.fw_shm_pool = fw_shm_pool; + + return 0; + +out_sess: + tee_client_close_session(pvt_data.ctx, pvt_data.session_id); +out_ctx: + tee_client_close_context(pvt_data.ctx); + + return err; +} + +static int tee_bnxt_fw_remove(struct device *dev) +{ + tee_shm_free(pvt_data.fw_shm_pool); + tee_client_close_session(pvt_data.ctx, pvt_data.session_id); + tee_client_close_context(pvt_data.ctx); + pvt_data.ctx = NULL; + + return 0; +} + +static const struct tee_client_device_id tee_bnxt_fw_id_table[] = { + {UUID_INIT(0x6272636D, 0x2019, 0x0716, + 0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)}, + {} +}; + +MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table); + +static struct tee_client_driver tee_bnxt_fw_driver = { + .id_table = tee_bnxt_fw_id_table, + .driver = { + .name = KBUILD_MODNAME, + .bus = &tee_bus_type, + .probe = tee_bnxt_fw_probe, + .remove = tee_bnxt_fw_remove, + }, +}; + +static int __init tee_bnxt_fw_mod_init(void) +{ + return driver_register(&tee_bnxt_fw_driver.driver); +} + +static void __exit tee_bnxt_fw_mod_exit(void) +{ + driver_unregister(&tee_bnxt_fw_driver.driver); +} + +module_init(tee_bnxt_fw_mod_init); +module_exit(tee_bnxt_fw_mod_exit); + +MODULE_AUTHOR("Vikas Gupta "); +MODULE_DESCRIPTION("Broadcom bnxt firmware manager"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ff34f3cce278a0982a7b66b1afaed6295141b1fc Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 4 Nov 2019 15:58:15 +0000 Subject: firmware: qcom: scm: Ensure 'a0' status code is treated as signed The 'a0' member of 'struct arm_smccc_res' is declared as 'unsigned long', however the Qualcomm SCM firmware interface driver expects to receive negative error codes via this field, so ensure that it's cast to 'long' before comparing to see if it is less than 0. Cc: Reviewed-by: Bjorn Andersson Signed-off-by: Will Deacon --- drivers/firmware/qcom_scm-64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 91d5ad7cf58b..25e0f60c759a 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -150,7 +150,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, kfree(args_virt); } - if (res->a0 < 0) + if ((long)res->a0 < 0) return qcom_scm_remap_error(res->a0); return 0; -- cgit v1.2.3 From 1a5ea3b7a6ac6c133660b9aeda95b2087c8eec47 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 20 Sep 2019 13:34:27 +0530 Subject: firmware: qcom_scm-64: Add atomic version of qcom_scm_call There are scnenarios where drivers are required to make a scm call in atomic context, such as in one of the qcom's arm-smmu-500 errata [1]. [1] ("https://source.codeaurora.org/quic/la/kernel/msm-4.9/ tree/drivers/iommu/arm-smmu.c?h=msm-4.9#n4842") Signed-off-by: Vivek Gautam Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd Acked-by: Andy Gross Signed-off-by: Sai Prakash Ranjan Signed-off-by: Will Deacon --- drivers/firmware/qcom_scm-64.c | 138 ++++++++++++++++++++++++++++------------- 1 file changed, 94 insertions(+), 44 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 25e0f60c759a..872155c67b2d 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -62,32 +62,72 @@ static DEFINE_MUTEX(qcom_scm_lock); #define FIRST_EXT_ARG_IDX 3 #define N_REGISTER_ARGS (MAX_QCOM_SCM_ARGS - N_EXT_QCOM_SCM_ARGS + 1) -/** - * qcom_scm_call() - Invoke a syscall in the secure world - * @dev: device - * @svc_id: service identifier - * @cmd_id: command identifier - * @desc: Descriptor structure containing arguments and return values - * - * Sends a command to the SCM and waits for the command to finish processing. - * This should *only* be called in pre-emptible context. -*/ -static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, - const struct qcom_scm_desc *desc, - struct arm_smccc_res *res) +static void __qcom_scm_call_do(const struct qcom_scm_desc *desc, + struct arm_smccc_res *res, u32 fn_id, + u64 x5, u32 type) +{ + u64 cmd; + struct arm_smccc_quirk quirk = { .id = ARM_SMCCC_QUIRK_QCOM_A6 }; + + cmd = ARM_SMCCC_CALL_VAL(type, qcom_smccc_convention, + ARM_SMCCC_OWNER_SIP, fn_id); + + quirk.state.a6 = 0; + + do { + arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0], + desc->args[1], desc->args[2], x5, + quirk.state.a6, 0, res, &quirk); + + if (res->a0 == QCOM_SCM_INTERRUPTED) + cmd = res->a0; + + } while (res->a0 == QCOM_SCM_INTERRUPTED); +} + +static void qcom_scm_call_do(const struct qcom_scm_desc *desc, + struct arm_smccc_res *res, u32 fn_id, + u64 x5, bool atomic) +{ + int retry_count = 0; + + if (atomic) { + __qcom_scm_call_do(desc, res, fn_id, x5, ARM_SMCCC_FAST_CALL); + return; + } + + do { + mutex_lock(&qcom_scm_lock); + + __qcom_scm_call_do(desc, res, fn_id, x5, + ARM_SMCCC_STD_CALL); + + mutex_unlock(&qcom_scm_lock); + + if (res->a0 == QCOM_SCM_V2_EBUSY) { + if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) + break; + msleep(QCOM_SCM_EBUSY_WAIT_MS); + } + } while (res->a0 == QCOM_SCM_V2_EBUSY); +} + +static int ___qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, + const struct qcom_scm_desc *desc, + struct arm_smccc_res *res, bool atomic) { int arglen = desc->arginfo & 0xf; - int retry_count = 0, i; + int i; u32 fn_id = QCOM_SCM_FNID(svc_id, cmd_id); - u64 cmd, x5 = desc->args[FIRST_EXT_ARG_IDX]; + u64 x5 = desc->args[FIRST_EXT_ARG_IDX]; dma_addr_t args_phys = 0; void *args_virt = NULL; size_t alloc_len; - struct arm_smccc_quirk quirk = {.id = ARM_SMCCC_QUIRK_QCOM_A6}; + gfp_t flag = atomic ? GFP_ATOMIC : GFP_KERNEL; if (unlikely(arglen > N_REGISTER_ARGS)) { alloc_len = N_EXT_QCOM_SCM_ARGS * sizeof(u64); - args_virt = kzalloc(PAGE_ALIGN(alloc_len), GFP_KERNEL); + args_virt = kzalloc(PAGE_ALIGN(alloc_len), flag); if (!args_virt) return -ENOMEM; @@ -117,33 +157,7 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, x5 = args_phys; } - do { - mutex_lock(&qcom_scm_lock); - - cmd = ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, - qcom_smccc_convention, - ARM_SMCCC_OWNER_SIP, fn_id); - - quirk.state.a6 = 0; - - do { - arm_smccc_smc_quirk(cmd, desc->arginfo, desc->args[0], - desc->args[1], desc->args[2], x5, - quirk.state.a6, 0, res, &quirk); - - if (res->a0 == QCOM_SCM_INTERRUPTED) - cmd = res->a0; - - } while (res->a0 == QCOM_SCM_INTERRUPTED); - - mutex_unlock(&qcom_scm_lock); - - if (res->a0 == QCOM_SCM_V2_EBUSY) { - if (retry_count++ > QCOM_SCM_EBUSY_MAX_RETRY) - break; - msleep(QCOM_SCM_EBUSY_WAIT_MS); - } - } while (res->a0 == QCOM_SCM_V2_EBUSY); + qcom_scm_call_do(desc, res, fn_id, x5, atomic); if (args_virt) { dma_unmap_single(dev, args_phys, alloc_len, DMA_TO_DEVICE); @@ -156,6 +170,42 @@ static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, return 0; } +/** + * qcom_scm_call() - Invoke a syscall in the secure world + * @dev: device + * @svc_id: service identifier + * @cmd_id: command identifier + * @desc: Descriptor structure containing arguments and return values + * + * Sends a command to the SCM and waits for the command to finish processing. + * This should *only* be called in pre-emptible context. + */ +static int qcom_scm_call(struct device *dev, u32 svc_id, u32 cmd_id, + const struct qcom_scm_desc *desc, + struct arm_smccc_res *res) +{ + might_sleep(); + return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, false); +} + +/** + * qcom_scm_call_atomic() - atomic variation of qcom_scm_call() + * @dev: device + * @svc_id: service identifier + * @cmd_id: command identifier + * @desc: Descriptor structure containing arguments and return values + * @res: Structure containing results from SMC/HVC call + * + * Sends a command to the SCM and waits for the command to finish processing. + * This can be called in atomic context. + */ +static int qcom_scm_call_atomic(struct device *dev, u32 svc_id, u32 cmd_id, + const struct qcom_scm_desc *desc, + struct arm_smccc_res *res) +{ + return ___qcom_scm_call(dev, svc_id, cmd_id, desc, res, true); +} + /** * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus * @entry: Entry point function for the cpus -- cgit v1.2.3 From 5eb0e0e4f90addc6b79ebf1cb4b06b56b09f09de Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Fri, 20 Sep 2019 13:34:28 +0530 Subject: firmware/qcom_scm: Add scm call to handle smmu errata Qcom's smmu-500 needs to toggle wait-for-safe sequence to handle TLB invalidation sync's. Few firmwares allow doing that through SCM interface. Add API to toggle wait for safe from firmware through a SCM call. Signed-off-by: Vivek Gautam Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd Acked-by: Andy Gross Signed-off-by: Sai Prakash Ranjan Signed-off-by: Will Deacon --- drivers/firmware/qcom_scm-32.c | 5 +++++ drivers/firmware/qcom_scm-64.c | 13 +++++++++++++ drivers/firmware/qcom_scm.c | 6 ++++++ drivers/firmware/qcom_scm.h | 5 +++++ 4 files changed, 29 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 215061c581e1..bee8729525ec 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -614,3 +614,8 @@ int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) return qcom_scm_call_atomic2(QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE, addr, val); } + +int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool enable) +{ + return -ENODEV; +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 872155c67b2d..e1cd933ea9ae 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -552,3 +552,16 @@ int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) return qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE, &desc, &res); } + +int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, bool en) +{ + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = QCOM_SCM_CONFIG_ERRATA1_CLIENT_ALL; + desc.args[1] = en; + desc.arginfo = QCOM_SCM_ARGS(2); + + return qcom_scm_call_atomic(dev, QCOM_SCM_SVC_SMMU_PROGRAM, + QCOM_SCM_CONFIG_ERRATA1, &desc, &res); +} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 4802ab170fe5..a729e05c21b8 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -345,6 +345,12 @@ int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) } EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); +int qcom_scm_qsmmu500_wait_safe_toggle(bool en) +{ + return __qcom_scm_qsmmu500_wait_safe_toggle(__scm->dev, en); +} +EXPORT_SYMBOL(qcom_scm_qsmmu500_wait_safe_toggle); + int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) { return __qcom_scm_io_readl(__scm->dev, addr, val); diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 99506bd873c0..baee744dbcfe 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -91,10 +91,15 @@ extern int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare); #define QCOM_SCM_IOMMU_SECURE_PTBL_SIZE 3 #define QCOM_SCM_IOMMU_SECURE_PTBL_INIT 4 +#define QCOM_SCM_SVC_SMMU_PROGRAM 0x15 +#define QCOM_SCM_CONFIG_ERRATA1 0x3 +#define QCOM_SCM_CONFIG_ERRATA1_CLIENT_ALL 0x2 extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, size_t *size); extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, u32 spare); +extern int __qcom_scm_qsmmu500_wait_safe_toggle(struct device *dev, + bool enable); #define QCOM_MEM_PROT_ASSIGN_ID 0x16 extern int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, size_t mem_sz, -- cgit v1.2.3 From 4466bf82821be512da5ae973097a9afd38cf36f6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Oct 2019 13:02:21 -0700 Subject: efi/apple-properties: use PROPERTY_ENTRY_U8_ARRAY_LEN Let's switch to using PROPERTY_ENTRY_U8_ARRAY_LEN() to initialize property entries. Also, when dumping data, rely on local variables instead of poking into the property entry structure directly. Reviewed-by: Andy Shevchenko Signed-off-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/apple-properties.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index 0e206c9e0d7a..5ccf39986a14 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -53,7 +53,8 @@ static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, for (i = 0; i < dev_header->prop_count; i++) { int remaining = dev_header->len - (ptr - (void *)dev_header); - u32 key_len, val_len; + u32 key_len, val_len, entry_len; + const u8 *entry_data; char *key; if (sizeof(key_len) > remaining) @@ -85,17 +86,14 @@ static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, ucs2_as_utf8(key, ptr + sizeof(key_len), key_len - sizeof(key_len)); - entry[i].name = key; - entry[i].length = val_len - sizeof(val_len); - entry[i].is_array = !!entry[i].length; - entry[i].type = DEV_PROP_U8; - entry[i].pointer.u8_data = ptr + key_len + sizeof(val_len); - + entry_data = ptr + key_len + sizeof(val_len); + entry_len = val_len - sizeof(val_len); + entry[i] = PROPERTY_ENTRY_U8_ARRAY_LEN(key, entry_data, + entry_len); if (dump_properties) { - dev_info(dev, "property: %s\n", entry[i].name); + dev_info(dev, "property: %s\n", key); print_hex_dump(KERN_INFO, pr_fmt(), DUMP_PREFIX_OFFSET, - 16, 1, entry[i].pointer.u8_data, - entry[i].length, true); + 16, 1, entry_data, entry_len, true); } ptr += key_len + val_len; -- cgit v1.2.3 From 41e8a7c249bf50f2f719c2ff21ab92be70651f06 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Nov 2019 08:06:12 +0100 Subject: efi/random: use arch-independent efi_call_proto() To handle all arch-specific peculiarities when calling an EFI protocol function, a wrapper efi_call_proto() exists on all relevant architectures. On arm/arm64, this is merely a plain function call. On x86, a special EFI entry stub needs to be used, however, as the calling convention differs. To make the efi/random stub arch-independent, use efi_call_proto() instead of the existing non-portable calls to the EFI get_rng protocol function. This also requires the addition of some typedefs. Signed-off-by: Dominik Brodowski Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/random.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index b4b1d1dcb5fd..53f1466f7de6 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -9,6 +9,18 @@ #include "efistub.h" +typedef struct efi_rng_protocol efi_rng_protocol_t; + +typedef struct { + u32 get_info; + u32 get_rng; +} efi_rng_protocol_32_t; + +typedef struct { + u64 get_info; + u64 get_rng; +} efi_rng_protocol_64_t; + struct efi_rng_protocol { efi_status_t (*get_info)(struct efi_rng_protocol *, unsigned long *, efi_guid_t *); @@ -28,7 +40,7 @@ efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg, if (status != EFI_SUCCESS) return status; - return rng->get_rng(rng, NULL, size, out); + return efi_call_proto(efi_rng_protocol, get_rng, rng, NULL, size, out); } /* @@ -161,15 +173,16 @@ efi_status_t efi_random_get_seed(efi_system_table_t *sys_table_arg) if (status != EFI_SUCCESS) return status; - status = rng->get_rng(rng, &rng_algo_raw, EFI_RANDOM_SEED_SIZE, - seed->bits); + status = efi_call_proto(efi_rng_protocol, get_rng, rng, &rng_algo_raw, + EFI_RANDOM_SEED_SIZE, seed->bits); + if (status == EFI_UNSUPPORTED) /* * Use whatever algorithm we have available if the raw algorithm * is not implemented. */ - status = rng->get_rng(rng, NULL, EFI_RANDOM_SEED_SIZE, - seed->bits); + status = efi_call_proto(efi_rng_protocol, get_rng, rng, NULL, + EFI_RANDOM_SEED_SIZE, seed->bits); if (status != EFI_SUCCESS) goto err_freepool; -- cgit v1.2.3 From 0d95981438c3bdb53cc99b0fb656d24d7a80e1f3 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 6 Nov 2019 08:06:13 +0100 Subject: x86: efi/random: Invoke EFI_RNG_PROTOCOL to seed the UEFI RNG table Invoke the EFI_RNG_PROTOCOL protocol in the context of the x86 EFI stub, same as is done on arm/arm64 since commit 568bc4e87033 ("efi/arm*/libstub: Invoke EFI_RNG_PROTOCOL to seed the UEFI RNG table"). Within the stub, a Linux-specific RNG seed UEFI config table will be seeded. The EFI routines in the core kernel will pick that up later, yet still early during boot, to seed the kernel entropy pool. If CONFIG_RANDOM_TRUST_BOOTLOADER, entropy is credited for this seed. Signed-off-by: Dominik Brodowski Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/Makefile | 5 +++-- drivers/firmware/efi/libstub/efistub.h | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index ee0661ddb25b..c35f893897e1 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -38,7 +38,8 @@ OBJECT_FILES_NON_STANDARD := y # Prevents link failures: __sanitizer_cov_trace_pc() is not linked in. KCOV_INSTRUMENT := n -lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o +lib-y := efi-stub-helper.o gop.o secureboot.o tpm.o \ + random.o # include the stub's generic dependencies from lib/ when building for ARM/arm64 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c @@ -47,7 +48,7 @@ arm-deps-$(CONFIG_ARM64) += sort.c $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o random.o \ +lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \ $(patsubst %.c,lib-%.o,$(arm-deps-y)) lib-$(CONFIG_ARM) += arm32-stub.o diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 7f1556fd867d..05739ae013c8 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -63,8 +63,6 @@ efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg, efi_status_t check_platform_features(efi_system_table_t *sys_table_arg); -efi_status_t efi_random_get_seed(efi_system_table_t *sys_table_arg); - void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid); /* Helper macros for the usual case of using simple C variables: */ -- cgit v1.2.3 From d99c1ba6a73b9e93e2884b7893fe19e3c082ba03 Mon Sep 17 00:00:00 2001 From: Xinwei Kong Date: Thu, 7 Nov 2019 16:24:21 +0800 Subject: efi: libstub/tpm: enable tpm eventlog function for ARM platforms Wire up the existing code for ARM that loads the TPM event log into OS accessible buffers while running the EFI stub so that the kernel proper can access it at runtime. Tested-by: Zou Cao Signed-off-by: Xinwei Kong Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index c382a48c6678..817237ce2420 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -189,6 +189,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, goto fail_free_cmdline; } + efi_retrieve_tpm2_eventlog(sys_table); + /* Ask the firmware to clear memory on unclean shutdown */ efi_enable_reset_attack_mitigation(sys_table); -- cgit v1.2.3 From fe3e5e65c06edb1c56e64e567f053e243142001f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2019 17:43:00 -0800 Subject: efi: Enumerate EFI_MEMORY_SP UEFI 2.8 defines an EFI_MEMORY_SP attribute bit to augment the interpretation of the EFI Memory Types as "reserved for a specific purpose". The intent of this bit is to allow the OS to identify precious or scarce memory resources and optionally manage it separately from EfiConventionalMemory. As defined older OSes that do not know about this attribute are permitted to ignore it and the memory will be handled according to the OS default policy for the given memory type. In other words, this "specific purpose" hint is deliberately weaker than EfiReservedMemoryType in that the system continues to operate if the OS takes no action on the attribute. The risk of taking no action is potentially unwanted / unmovable kernel allocations from the designated resource that prevent the full realization of the "specific purpose". For example, consider a system with a high-bandwidth memory pool. Older kernels are permitted to boot and consume that memory as conventional "System-RAM" newer kernels may arrange for that memory to be set aside (soft reserved) by the system administrator for a dedicated high-bandwidth memory aware application to consume. Specifically, this mechanism allows for the elimination of scenarios where platform firmware tries to game OS policy by lying about ACPI SLIT values, i.e. claiming that a precious memory resource has a high distance to trigger the OS to avoid it by default. This reservation hint allows platform-firmware to instead tell the truth about performance characteristics by indicate to OS memory management to put immovable allocations elsewhere. Implement simple detection of the bit for EFI memory table dumps and save the kernel policy for a follow-on change. Reviewed-by: Ard Biesheuvel Reviewed-by: Dave Hansen Signed-off-by: Dan Williams Acked-by: Thomas Gleixner Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/efi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index e98bbf8e56d9..f8f8e273d809 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -842,15 +842,16 @@ char * __init efi_md_typeattr_format(char *buf, size_t size, if (attr & ~(EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_RO | EFI_MEMORY_WP | EFI_MEMORY_RP | EFI_MEMORY_XP | - EFI_MEMORY_NV | + EFI_MEMORY_NV | EFI_MEMORY_SP | EFI_MEMORY_RUNTIME | EFI_MEMORY_MORE_RELIABLE)) snprintf(pos, size, "|attr=0x%016llx]", (unsigned long long)attr); else snprintf(pos, size, - "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", + "|%3s|%2s|%2s|%2s|%2s|%2s|%2s|%2s|%3s|%2s|%2s|%2s|%2s]", attr & EFI_MEMORY_RUNTIME ? "RUN" : "", attr & EFI_MEMORY_MORE_RELIABLE ? "MR" : "", + attr & EFI_MEMORY_SP ? "SP" : "", attr & EFI_MEMORY_NV ? "NV" : "", attr & EFI_MEMORY_XP ? "XP" : "", attr & EFI_MEMORY_RP ? "RP" : "", -- cgit v1.2.3 From 6950e31b35fdf4588cbbdec1813091bb02cf8871 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2019 17:43:05 -0800 Subject: x86/efi: Push EFI_MEMMAP check into leaf routines In preparation for adding another EFI_MEMMAP dependent call that needs to occur before e820__memblock_setup() fixup the existing efi calls to check for EFI_MEMMAP internally. This ends up being cleaner than the alternative of checking EFI_MEMMAP multiple times in setup_arch(). Reviewed-by: Dave Hansen Reviewed-by: Ard Biesheuvel Signed-off-by: Dan Williams Acked-by: Thomas Gleixner Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/esrt.c | 3 +++ drivers/firmware/efi/fake_mem.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index d6dd5f503fa2..2762e0662bf4 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -246,6 +246,9 @@ void __init efi_esrt_init(void) int rc; phys_addr_t end; + if (!efi_enabled(EFI_MEMMAP)) + return; + pr_debug("esrt-init: loading.\n"); if (!esrt_table_exists()) return; diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c index 9501edc0fcfb..526b45331d96 100644 --- a/drivers/firmware/efi/fake_mem.c +++ b/drivers/firmware/efi/fake_mem.c @@ -44,7 +44,7 @@ void __init efi_fake_memmap(void) void *new_memmap; int i; - if (!nr_fake_mem) + if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) return; /* count up the number of EFI memory descriptor */ -- cgit v1.2.3 From b617c5266eedbef2ccbb90931bb9175faa4ae0bc Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2019 17:43:11 -0800 Subject: efi: Common enable/disable infrastructure for EFI soft reservation UEFI 2.8 defines an EFI_MEMORY_SP attribute bit to augment the interpretation of the EFI Memory Types as "reserved for a specific purpose". The proposed Linux behavior for specific purpose memory is that it is reserved for direct-access (device-dax) by default and not available for any kernel usage, not even as an OOM fallback. Later, through udev scripts or another init mechanism, these device-dax claimed ranges can be reconfigured and hot-added to the available System-RAM with a unique node identifier. This device-dax management scheme implements "soft" in the "soft reserved" designation by allowing some or all of the reservation to be recovered as typical memory. This policy can be disabled at compile-time with CONFIG_EFI_SOFT_RESERVE=n, or runtime with efi=nosoftreserve. As for this patch, define the common helpers to determine if the EFI_MEMORY_SP attribute should be honored. The determination needs to be made early to prevent the kernel from being loaded into soft-reserved memory, or otherwise allowing early allocations to land there. Follow-on changes are needed per architecture to leverage these helpers in their respective mem-init paths. Reviewed-by: Ard Biesheuvel Signed-off-by: Dan Williams Acked-by: Thomas Gleixner Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/Kconfig | 21 +++++++++++++++++++++ drivers/firmware/efi/efi.c | 8 ++++++++ drivers/firmware/efi/libstub/efi-stub-helper.c | 19 +++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index b248870a9806..bcc378c19ebe 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -75,6 +75,27 @@ config EFI_MAX_FAKE_MEM Ranges can be set up to this value using comma-separated list. The default value is 8. +config EFI_SOFT_RESERVE + bool "Reserve EFI Specific Purpose Memory" + depends on EFI && EFI_STUB && ACPI_HMAT + default ACPI_HMAT + help + On systems that have mixed performance classes of memory EFI + may indicate specific purpose memory with an attribute (See + EFI_MEMORY_SP in UEFI 2.8). A memory range tagged with this + attribute may have unique performance characteristics compared + to the system's general purpose "System RAM" pool. On the + expectation that such memory has application specific usage, + and its base EFI memory type is "conventional" answer Y to + arrange for the kernel to reserve it as a "Soft Reserved" + resource, and set aside for direct-access (device-dax) by + default. The memory range can later be optionally assigned to + the page allocator by system administrator policy via the + device-dax kmem facility. Say N to have the kernel treat this + memory as "System RAM" by default. + + If unsure, say Y. + config EFI_PARAMS_FROM_FDT bool help diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index f8f8e273d809..e1cb915b45c6 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -81,6 +81,11 @@ bool efi_runtime_disabled(void) return disable_runtime; } +bool __pure __efi_soft_reserve_enabled(void) +{ + return !efi_enabled(EFI_MEM_NO_SOFT_RESERVE); +} + static int __init parse_efi_cmdline(char *str) { if (!str) { @@ -94,6 +99,9 @@ static int __init parse_efi_cmdline(char *str) if (parse_option_str(str, "noruntime")) disable_runtime = true; + if (parse_option_str(str, "nosoftreserve")) + set_bit(EFI_MEM_NO_SOFT_RESERVE, &efi.flags); + return 0; } early_param("efi", parse_efi_cmdline); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 35dbc2791c97..e02579907f2e 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -32,6 +32,7 @@ static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; static int __section(.data) __nokaslr; static int __section(.data) __quiet; static int __section(.data) __novamap; +static bool __section(.data) efi_nosoftreserve; int __pure nokaslr(void) { @@ -45,6 +46,10 @@ int __pure novamap(void) { return __novamap; } +bool __pure __efi_soft_reserve_enabled(void) +{ + return !efi_nosoftreserve; +} #define EFI_MMAP_NR_SLACK_SLOTS 8 @@ -211,6 +216,10 @@ again: if (desc->type != EFI_CONVENTIONAL_MEMORY) continue; + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + if (desc->num_pages < nr_pages) continue; @@ -305,6 +314,10 @@ efi_status_t efi_low_alloc_above(efi_system_table_t *sys_table_arg, if (desc->type != EFI_CONVENTIONAL_MEMORY) continue; + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + if (desc->num_pages < nr_pages) continue; @@ -484,6 +497,12 @@ efi_status_t efi_parse_options(char const *cmdline) __novamap = 1; } + if (IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && + !strncmp(str, "nosoftreserve", 7)) { + str += strlen("nosoftreserve"); + efi_nosoftreserve = 1; + } + /* Group words together, delimited by "," */ while (*str && *str != ' ' && *str != ',') str++; -- cgit v1.2.3 From 16993c0f0a43213e23666ea40e9163887f593ac7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2019 17:43:21 -0800 Subject: arm/efi: EFI soft reservation to memblock UEFI 2.8 defines an EFI_MEMORY_SP attribute bit to augment the interpretation of the EFI Memory Types as "reserved for a specific purpose". The proposed Linux behavior for specific purpose memory is that it is reserved for direct-access (device-dax) by default and not available for any kernel usage, not even as an OOM fallback. Later, through udev scripts or another init mechanism, these device-dax claimed ranges can be reconfigured and hot-added to the available System-RAM with a unique node identifier. This device-dax management scheme implements "soft" in the "soft reserved" designation by allowing some or all of the reservation to be recovered as typical memory. This policy can be disabled at compile-time with CONFIG_EFI_SOFT_RESERVE=n, or runtime with efi=nosoftreserve. For this patch, update the ARM paths that consider EFI_CONVENTIONAL_MEMORY to optionally take the EFI_MEMORY_SP attribute into account as a reservation indicator. Publish the soft reservation as IORES_DESC_SOFT_RESERVED memory, similar to x86. (Based on an original patch by Ard) Reviewed-by: Ard Biesheuvel Signed-off-by: Dan Williams Acked-by: Thomas Gleixner Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/arm-init.c | 9 +++++++++ drivers/firmware/efi/arm-runtime.c | 24 ++++++++++++++++++++++++ drivers/firmware/efi/libstub/arm32-stub.c | 5 +++++ drivers/firmware/efi/libstub/random.c | 4 ++++ 4 files changed, 42 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 311cd349a862..904fa09e6a6b 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -163,6 +163,15 @@ static __init int is_usable_memory(efi_memory_desc_t *md) case EFI_BOOT_SERVICES_DATA: case EFI_CONVENTIONAL_MEMORY: case EFI_PERSISTENT_MEMORY: + /* + * Special purpose memory is 'soft reserved', which means it + * is set aside initially, but can be hotplugged back in or + * be assigned to the dax driver after boot. + */ + if (efi_soft_reserve_enabled() && + (md->attribute & EFI_MEMORY_SP)) + return false; + /* * According to the spec, these regions are no longer reserved * after calling ExitBootServices(). However, we can only use diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index e2ac5fa5531b..899b803842bb 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -121,6 +121,30 @@ static int __init arm_enable_runtime_services(void) return 0; } + if (efi_soft_reserve_enabled()) { + efi_memory_desc_t *md; + + for_each_efi_memory_desc(md) { + int md_size = md->num_pages << EFI_PAGE_SHIFT; + struct resource *res; + + if (!(md->attribute & EFI_MEMORY_SP)) + continue; + + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (WARN_ON(!res)) + break; + + res->start = md->phys_addr; + res->end = md->phys_addr + md_size - 1; + res->name = "Soft Reserved"; + res->flags = IORESOURCE_MEM; + res->desc = IORES_DESC_SOFT_RESERVED; + + insert_resource(&iomem_resource, res); + } + } + if (efi_runtime_disabled()) { pr_info("EFI runtime services will be disabled.\n"); return 0; diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index 41213bf5fcf5..4566640de650 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -146,6 +146,11 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg, continue; case EFI_CONVENTIONAL_MEMORY: + /* Skip soft reserved conventional memory */ + if (efi_soft_reserve_enabled() && + (desc->attribute & EFI_MEMORY_SP)) + continue; + /* * Reserve the intersection between this entry and the * region. diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index b4b1d1dcb5fd..6c188695e730 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -46,6 +46,10 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md, if (md->type != EFI_CONVENTIONAL_MEMORY) return 0; + if (efi_soft_reserve_enabled() && + (md->attribute & EFI_MEMORY_SP)) + return 0; + region_end = min((u64)ULONG_MAX, md->phys_addr + md->num_pages*EFI_PAGE_SIZE - 1); first_slot = round_up(md->phys_addr, align); -- cgit v1.2.3 From 199c8471761273b7e287914cee968ddf21dfbfe0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2019 17:43:26 -0800 Subject: x86/efi: Add efi_fake_mem support for EFI_MEMORY_SP Given that EFI_MEMORY_SP is platform BIOS policy decision for marking memory ranges as "reserved for a specific purpose" there will inevitably be scenarios where the BIOS omits the attribute in situations where it is desired. Unlike other attributes if the OS wants to reserve this memory from the kernel the reservation needs to happen early in init. So early, in fact, that it needs to happen before e820__memblock_setup() which is a pre-requisite for efi_fake_memmap() that wants to allocate memory for the updated table. Introduce an x86 specific efi_fake_memmap_early() that can search for attempts to set EFI_MEMORY_SP via efi_fake_mem and update the e820 table accordingly. The KASLR code that scans the command line looking for user-directed memory reservations also needs to be updated to consider "efi_fake_mem=nn@ss:0x40000" requests. Acked-by: Ard Biesheuvel Reviewed-by: Dave Hansen Signed-off-by: Dan Williams Acked-by: Thomas Gleixner Signed-off-by: Rafael J. Wysocki --- drivers/firmware/efi/Makefile | 5 ++- drivers/firmware/efi/fake_mem.c | 24 ++++++------- drivers/firmware/efi/fake_mem.h | 10 ++++++ drivers/firmware/efi/x86_fake_mem.c | 69 +++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 drivers/firmware/efi/fake_mem.h create mode 100644 drivers/firmware/efi/x86_fake_mem.c (limited to 'drivers/firmware') diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 4ac2de4dfa72..554d795270d9 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -20,13 +20,16 @@ obj-$(CONFIG_UEFI_CPER) += cper.o obj-$(CONFIG_EFI_RUNTIME_MAP) += runtime-map.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o obj-$(CONFIG_EFI_STUB) += libstub/ -obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o +obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_map.o obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o +fake_map-y += fake_mem.o +fake_map-$(CONFIG_X86) += x86_fake_mem.o + arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y) diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c index 526b45331d96..bb9fc70d0cfa 100644 --- a/drivers/firmware/efi/fake_mem.c +++ b/drivers/firmware/efi/fake_mem.c @@ -17,12 +17,10 @@ #include #include #include -#include +#include "fake_mem.h" -#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM - -static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM]; -static int nr_fake_mem; +struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; +int nr_fake_mem; static int __init cmp_fake_mem(const void *x1, const void *x2) { @@ -50,7 +48,7 @@ void __init efi_fake_memmap(void) /* count up the number of EFI memory descriptor */ for (i = 0; i < nr_fake_mem; i++) { for_each_efi_memory_desc(md) { - struct range *r = &fake_mems[i].range; + struct range *r = &efi_fake_mems[i].range; new_nr_map += efi_memmap_split_count(md, r); } @@ -70,7 +68,7 @@ void __init efi_fake_memmap(void) } for (i = 0; i < nr_fake_mem; i++) - efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]); + efi_memmap_insert(&efi.memmap, new_memmap, &efi_fake_mems[i]); /* swap into new EFI memmap */ early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map); @@ -104,22 +102,22 @@ static int __init setup_fake_mem(char *p) if (nr_fake_mem >= EFI_MAX_FAKEMEM) break; - fake_mems[nr_fake_mem].range.start = start; - fake_mems[nr_fake_mem].range.end = start + mem_size - 1; - fake_mems[nr_fake_mem].attribute = attribute; + efi_fake_mems[nr_fake_mem].range.start = start; + efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1; + efi_fake_mems[nr_fake_mem].attribute = attribute; nr_fake_mem++; if (*p == ',') p++; } - sort(fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), + sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), cmp_fake_mem, NULL); for (i = 0; i < nr_fake_mem; i++) pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", - fake_mems[i].attribute, fake_mems[i].range.start, - fake_mems[i].range.end); + efi_fake_mems[i].attribute, efi_fake_mems[i].range.start, + efi_fake_mems[i].range.end); return *p == '\0' ? 0 : -EINVAL; } diff --git a/drivers/firmware/efi/fake_mem.h b/drivers/firmware/efi/fake_mem.h new file mode 100644 index 000000000000..d52791af4b18 --- /dev/null +++ b/drivers/firmware/efi/fake_mem.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __EFI_FAKE_MEM_H__ +#define __EFI_FAKE_MEM_H__ +#include + +#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM + +extern struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM]; +extern int nr_fake_mem; +#endif /* __EFI_FAKE_MEM_H__ */ diff --git a/drivers/firmware/efi/x86_fake_mem.c b/drivers/firmware/efi/x86_fake_mem.c new file mode 100644 index 000000000000..e5d6d5a1b240 --- /dev/null +++ b/drivers/firmware/efi/x86_fake_mem.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright(c) 2019 Intel Corporation. All rights reserved. */ +#include +#include +#include "fake_mem.h" + +void __init efi_fake_memmap_early(void) +{ + int i; + + /* + * The late efi_fake_mem() call can handle all requests if + * EFI_MEMORY_SP support is disabled. + */ + if (!efi_soft_reserve_enabled()) + return; + + if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem) + return; + + /* + * Given that efi_fake_memmap() needs to perform memblock + * allocations it needs to run after e820__memblock_setup(). + * However, if efi_fake_mem specifies EFI_MEMORY_SP for a given + * address range that potentially needs to mark the memory as + * reserved prior to e820__memblock_setup(). Update e820 + * directly if EFI_MEMORY_SP is specified for an + * EFI_CONVENTIONAL_MEMORY descriptor. + */ + for (i = 0; i < nr_fake_mem; i++) { + struct efi_mem_range *mem = &efi_fake_mems[i]; + efi_memory_desc_t *md; + u64 m_start, m_end; + + if ((mem->attribute & EFI_MEMORY_SP) == 0) + continue; + + m_start = mem->range.start; + m_end = mem->range.end; + for_each_efi_memory_desc(md) { + u64 start, end; + + if (md->type != EFI_CONVENTIONAL_MEMORY) + continue; + + start = md->phys_addr; + end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1; + + if (m_start <= end && m_end >= start) + /* fake range overlaps descriptor */; + else + continue; + + /* + * Trim the boundary of the e820 update to the + * descriptor in case the fake range overlaps + * !EFI_CONVENTIONAL_MEMORY + */ + start = max(start, m_start); + end = min(end, m_end); + + if (end <= start) + continue; + e820__range_update(start, end - start + 1, E820_TYPE_RAM, + E820_TYPE_SOFT_RESERVED); + e820__update_table(e820_table); + } + } +} -- cgit v1.2.3 From e9cb0497b1c801a66f9abc907b2c55241099e6ac Mon Sep 17 00:00:00 2001 From: Richard Gong Date: Mon, 4 Nov 2019 10:24:36 -0600 Subject: firmware: Fix incompatible function behavior for RSU driver The older versions of remote system update (RSU) firmware don't support retry and notify features then the kernel module dies when it queries the RSU retry counter or performs notify operation. Update the Intel service layer and RSU drivers to be compatible with all versions of RSU firmware. Reported-by: Radu Barcau Reported-by: kbuild test robot Signed-off-by: Richard Gong Link: https://lore.kernel.org/r/1572884676-1385-1-git-send-email-richard.gong@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/stratix10-rsu.c | 42 +++++++++++++++++----------------------- drivers/firmware/stratix10-svc.c | 18 ++++++++++++++++- 2 files changed, 35 insertions(+), 25 deletions(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/stratix10-rsu.c b/drivers/firmware/stratix10-rsu.c index bb008c019920..f8533338b018 100644 --- a/drivers/firmware/stratix10-rsu.c +++ b/drivers/firmware/stratix10-rsu.c @@ -20,7 +20,6 @@ #define RSU_VERSION_MASK GENMASK_ULL(63, 32) #define RSU_ERROR_LOCATION_MASK GENMASK_ULL(31, 0) #define RSU_ERROR_DETAIL_MASK GENMASK_ULL(63, 32) -#define RSU_FW_VERSION_MASK GENMASK_ULL(15, 0) #define RSU_TIMEOUT (msecs_to_jiffies(SVC_RSU_REQUEST_TIMEOUT_MS)) @@ -109,9 +108,12 @@ static void rsu_command_callback(struct stratix10_svc_client *client, { struct stratix10_rsu_priv *priv = client->priv; - if (data->status != BIT(SVC_STATUS_RSU_OK)) - dev_err(client->dev, "RSU returned status is %i\n", - data->status); + if (data->status == BIT(SVC_STATUS_RSU_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support notify\n"); + else if (data->status == BIT(SVC_STATUS_RSU_ERROR)) + dev_err(client->dev, "Failure, returned status is %lu\n", + BIT(data->status)); + complete(&priv->completion); } @@ -133,9 +135,11 @@ static void rsu_retry_callback(struct stratix10_svc_client *client, if (data->status == BIT(SVC_STATUS_RSU_OK)) priv->retry_counter = *counter; + else if (data->status == BIT(SVC_STATUS_RSU_NO_SUPPORT)) + dev_warn(client->dev, "Secure FW doesn't support retry\n"); else - dev_err(client->dev, "Failed to get retry counter %i\n", - data->status); + dev_err(client->dev, "Failed to get retry counter %lu\n", + BIT(data->status)); complete(&priv->completion); } @@ -333,15 +337,10 @@ static ssize_t notify_store(struct device *dev, return ret; } - /* only 19.3 or late version FW supports retry counter feature */ - if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) { - ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, - 0, rsu_retry_callback); - if (ret) { - dev_err(dev, - "Error, getting RSU retry %i\n", ret); - return ret; - } + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + return ret; } return count; @@ -413,15 +412,10 @@ static int stratix10_rsu_probe(struct platform_device *pdev) stratix10_svc_free_channel(priv->chan); } - /* only 19.3 or late version FW supports retry counter feature */ - if (FIELD_GET(RSU_FW_VERSION_MASK, priv->status.version)) { - ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, - rsu_retry_callback); - if (ret) { - dev_err(dev, - "Error, getting RSU retry %i\n", ret); - stratix10_svc_free_channel(priv->chan); - } + ret = rsu_send_msg(priv, COMMAND_RSU_RETRY, 0, rsu_retry_callback); + if (ret) { + dev_err(dev, "Error, getting RSU retry %i\n", ret); + stratix10_svc_free_channel(priv->chan); } return ret; diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index b485321189e1..c6c31402848d 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -493,8 +493,24 @@ static int svc_normal_to_secure_thread(void *data) pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata); break; default: - pr_warn("it shouldn't happen\n"); + pr_warn("Secure firmware doesn't support...\n"); + + /* + * be compatible with older version firmware which + * doesn't support RSU notify or retry + */ + if ((pdata->command == COMMAND_RSU_RETRY) || + (pdata->command == COMMAND_RSU_NOTIFY)) { + cbdata->status = + BIT(SVC_STATUS_RSU_NO_SUPPORT); + cbdata->kaddr1 = NULL; + cbdata->kaddr2 = NULL; + cbdata->kaddr3 = NULL; + pdata->chan->scl->receive_cb( + pdata->chan->scl, cbdata); + } break; + } }; -- cgit v1.2.3 From 9e0afe3910ff7e5493c5d8ebe3b499994b5e0272 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 3 Dec 2019 11:20:37 +0100 Subject: firmware: dmi: Remember the memory type Store the memory type while walking the memory slots, and provide a way to retrieve it later. Signed-off-by: Jean Delvare --- drivers/firmware/dmi_scan.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/firmware') diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 1e21fc3e9851..6f28e515fdc0 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -35,6 +35,7 @@ static struct dmi_memdev_info { const char *bank; u64 size; /* bytes */ u16 handle; + u8 type; /* DDR2, DDR3, DDR4 etc */ } *dmi_memdev; static int dmi_memdev_nr; @@ -391,7 +392,7 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) u64 bytes; u16 size; - if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x12) + if (dm->type != DMI_ENTRY_MEM_DEVICE || dm->length < 0x13) return; if (nr >= dmi_memdev_nr) { pr_warn(FW_BUG "Too many DIMM entries in SMBIOS table\n"); @@ -400,6 +401,7 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) dmi_memdev[nr].handle = get_unaligned(&dm->handle); dmi_memdev[nr].device = dmi_string(dm, d[0x10]); dmi_memdev[nr].bank = dmi_string(dm, d[0x11]); + dmi_memdev[nr].type = d[0x12]; size = get_unaligned((u16 *)&d[0xC]); if (size == 0) @@ -1128,3 +1130,24 @@ u64 dmi_memdev_size(u16 handle) return ~0ull; } EXPORT_SYMBOL_GPL(dmi_memdev_size); + +/** + * dmi_memdev_type - get the memory type + * @handle: DMI structure handle + * + * Return the DMI memory type of the module in the slot associated with the + * given DMI handle, or 0x0 if no such DMI handle exists. + */ +u8 dmi_memdev_type(u16 handle) +{ + int n; + + if (dmi_memdev) { + for (n = 0; n < dmi_memdev_nr; n++) { + if (handle == dmi_memdev[n].handle) + return dmi_memdev[n].type; + } + } + return 0x0; /* Not a valid value */ +} +EXPORT_SYMBOL_GPL(dmi_memdev_type); -- cgit v1.2.3 From 7c2378800cf7ac87e2663afa7f39d102871f0c28 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 3 Dec 2019 11:20:37 +0100 Subject: firmware: dmi: Add dmi_memdev_handle Add a utility function dmi_memdev_handle() which returns the DMI handle associated with a given memory slot. This will allow kernel drivers to iterate over the memory slots. Signed-off-by: Jean Delvare --- drivers/firmware/dmi_scan.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/firmware') diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 6f28e515fdc0..2045566d622f 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -1151,3 +1151,19 @@ u8 dmi_memdev_type(u16 handle) return 0x0; /* Not a valid value */ } EXPORT_SYMBOL_GPL(dmi_memdev_type); + +/** + * dmi_memdev_handle - get the DMI handle of a memory slot + * @slot: slot number + * + * Return the DMI handle associated with a given memory slot, or %0xFFFF + * if there is no such slot. + */ +u16 dmi_memdev_handle(int slot) +{ + if (dmi_memdev && slot >= 0 && slot < dmi_memdev_nr) + return dmi_memdev[slot].handle; + + return 0xffff; /* Not a valid value */ +} +EXPORT_SYMBOL_GPL(dmi_memdev_handle); -- cgit v1.2.3