remoteproc: qcom_q6v5_mss: Use a carveout to authenticate modem headers
authorSibi Sankar <quic_sibis@quicinc.com>
Tue, 17 Jan 2023 08:58:35 +0000 (14:28 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:33:40 +0000 (09:33 +0100)
[ Upstream commit 57f72170a2b2a362c35bb9407fc844eac5afdec1 ]

Any access to the dynamically allocated metadata region by the application
processor after assigning it to the remote Q6 will result in a XPU
violation. Fix this by replacing the dynamically allocated memory region
with a no-map carveout and unmap the modem metadata memory region before
passing control to the remote Q6.

Reported-and-tested-by: Amit Pundir <amit.pundir@linaro.org>
Fixes: 6c5a9dc2481b ("remoteproc: qcom: Make secure world call for mem ownership switch")
Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
Link: https://lore.kernel.org/r/20230117085840.32356-7-quic_sibis@quicinc.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/remoteproc/qcom_q6v5_mss.c

index a8b141d..7dbab5f 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/of_address.h>
 #include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
 #include <linux/pm_domain.h>
 #include <linux/pm_runtime.h>
@@ -210,6 +211,9 @@ struct q6v5 {
        size_t mba_size;
        size_t dp_size;
 
+       phys_addr_t mdata_phys;
+       size_t mdata_size;
+
        phys_addr_t mpss_phys;
        phys_addr_t mpss_reloc;
        size_t mpss_size;
@@ -945,15 +949,35 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw,
        if (IS_ERR(metadata))
                return PTR_ERR(metadata);
 
-       ptr = dma_alloc_attrs(qproc->dev, size, &phys, GFP_KERNEL, dma_attrs);
-       if (!ptr) {
-               kfree(metadata);
-               dev_err(qproc->dev, "failed to allocate mdt buffer\n");
-               return -ENOMEM;
+       if (qproc->mdata_phys) {
+               if (size > qproc->mdata_size) {
+                       ret = -EINVAL;
+                       dev_err(qproc->dev, "metadata size outside memory range\n");
+                       goto free_metadata;
+               }
+
+               phys = qproc->mdata_phys;
+               ptr = memremap(qproc->mdata_phys, size, MEMREMAP_WC);
+               if (!ptr) {
+                       ret = -EBUSY;
+                       dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n",
+                               &qproc->mdata_phys, size);
+                       goto free_metadata;
+               }
+       } else {
+               ptr = dma_alloc_attrs(qproc->dev, size, &phys, GFP_KERNEL, dma_attrs);
+               if (!ptr) {
+                       ret = -ENOMEM;
+                       dev_err(qproc->dev, "failed to allocate mdt buffer\n");
+                       goto free_metadata;
+               }
        }
 
        memcpy(ptr, metadata, size);
 
+       if (qproc->mdata_phys)
+               memunmap(ptr);
+
        /* Hypervisor mapping to access metadata by modem */
        mdata_perm = BIT(QCOM_SCM_VMID_HLOS);
        ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, false, true,
@@ -982,7 +1006,9 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw,
                         "mdt buffer not reclaimed system may become unstable\n");
 
 free_dma_attrs:
-       dma_free_attrs(qproc->dev, size, ptr, phys, dma_attrs);
+       if (!qproc->mdata_phys)
+               dma_free_attrs(qproc->dev, size, ptr, phys, dma_attrs);
+free_metadata:
        kfree(metadata);
 
        return ret < 0 ? ret : 0;
@@ -1810,6 +1836,7 @@ static int q6v5_init_reset(struct q6v5 *qproc)
 static int q6v5_alloc_memory_region(struct q6v5 *qproc)
 {
        struct device_node *child;
+       struct reserved_mem *rmem;
        struct device_node *node;
        struct resource r;
        int ret;
@@ -1856,6 +1883,26 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
        qproc->mpss_phys = qproc->mpss_reloc = r.start;
        qproc->mpss_size = resource_size(&r);
 
+       if (!child) {
+               node = of_parse_phandle(qproc->dev->of_node, "memory-region", 2);
+       } else {
+               child = of_get_child_by_name(qproc->dev->of_node, "metadata");
+               node = of_parse_phandle(child, "memory-region", 0);
+               of_node_put(child);
+       }
+
+       if (!node)
+               return 0;
+
+       rmem = of_reserved_mem_lookup(node);
+       if (!rmem) {
+               dev_err(qproc->dev, "unable to resolve metadata region\n");
+               return -EINVAL;
+       }
+
+       qproc->mdata_phys = rmem->base;
+       qproc->mdata_size = rmem->size;
+
        return 0;
 }