diff options
-rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 146 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_elf_loader.c | 96 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_internal.h | 13 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 38 | ||||
-rw-r--r-- | drivers/remoteproc/ste_modem_rproc.c | 50 | ||||
-rw-r--r-- | include/linux/remoteproc.h | 7 |
6 files changed, 240 insertions, 110 deletions
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index dd3bfaf1ad40..13dc7b49f760 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -207,7 +207,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) /* * Allocate non-cacheable memory for the vring. In the future * this call will also configure the IOMMU for us - * TODO: let the rproc know the da of this vring */ va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); if (!va) { @@ -218,7 +217,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) /* * Assign an rproc-wide unique index for this vring * TODO: assign a notifyid for rvdev updates as well - * TODO: let the rproc know the notifyid of this vring * TODO: support predefined notifyids (via resource table) */ ret = idr_get_new(&rproc->notifyids, rvring, ¬ifyid); @@ -228,9 +226,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) return ret; } - /* Store largest notifyid */ - rproc->max_notifyid = max(rproc->max_notifyid, notifyid); - dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va, (unsigned long long)dma, size, notifyid); @@ -238,6 +233,14 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) rvring->dma = dma; rvring->notifyid = notifyid; + /* + * Let the rproc know the notifyid and da of this vring. + * Not all platforms use dma_alloc_coherent to automatically + * set up the iommu. In this case the device address (da) will + * hold the physical address and not the device address. + */ + rvdev->rsc->vring[i].da = dma; + rvdev->rsc->vring[i].notifyid = notifyid; return 0; } @@ -272,25 +275,13 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) return 0; } -static int rproc_max_notifyid(int id, void *p, void *data) -{ - int *maxid = data; - *maxid = max(*maxid, id); - return 0; -} - void rproc_free_vring(struct rproc_vring *rvring) { int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); struct rproc *rproc = rvring->rvdev->rproc; - int maxid = 0; dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); idr_remove(&rproc->notifyids, rvring->notifyid); - - /* Find the largest remaining notifyid */ - idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid); - rproc->max_notifyid = maxid; } /** @@ -362,8 +353,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, goto free_rvdev; } - /* remember the device features */ - rvdev->dfeatures = rsc->dfeatures; + /* remember the resource entry */ + rvdev->rsc = rsc; list_add_tail(&rvdev->node, &rproc->rvdevs); @@ -673,28 +664,46 @@ free_carv: return ret; } +static int rproc_handle_notifyid(struct rproc *rproc, struct fw_rsc_vdev *rsc, + int avail) +{ + /* Summerize the number of notification IDs */ + rproc->max_notifyid += rsc->num_of_vrings; + + return 0; +} + /* * A lookup table for resource handlers. The indices are defined in * enum fw_resource_type. */ -static rproc_handle_resource_t rproc_handle_rsc[] = { +static rproc_handle_resource_t rproc_handle_rsc[RSC_LAST] = { [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout, [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem, [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace, [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */ }; +static rproc_handle_resource_t rproc_handle_vdev_rsc[RSC_LAST] = { + [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev, +}; + +static rproc_handle_resource_t rproc_handle_notifyid_rsc[RSC_LAST] = { + [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_notifyid, +}; + /* handle firmware resource entries before booting the remote processor */ static int -rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len) +rproc_handle_resource(struct rproc *rproc, int len, + rproc_handle_resource_t handlers[RSC_LAST]) { struct device *dev = &rproc->dev; rproc_handle_resource_t handler; int ret = 0, i; - for (i = 0; i < table->num; i++) { - int offset = table->offset[i]; - struct fw_rsc_hdr *hdr = (void *)table + offset; + for (i = 0; i < rproc->rsc->num; i++) { + int offset = rproc->rsc->offset[i]; + struct fw_rsc_hdr *hdr = (void *)rproc->rsc + offset; int avail = len - offset - sizeof(*hdr); void *rsc = (void *)hdr + sizeof(*hdr); @@ -711,7 +720,7 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len continue; } - handler = rproc_handle_rsc[hdr->type]; + handler = handlers[hdr->type]; if (!handler) continue; @@ -723,40 +732,6 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len return ret; } -/* handle firmware resource entries while registering the remote processor */ -static int -rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len) -{ - struct device *dev = &rproc->dev; - int ret = 0, i; - - for (i = 0; i < table->num; i++) { - int offset = table->offset[i]; - struct fw_rsc_hdr *hdr = (void *)table + offset; - int avail = len - offset - sizeof(*hdr); - struct fw_rsc_vdev *vrsc; - - /* make sure table isn't truncated */ - if (avail < 0) { - dev_err(dev, "rsc table is truncated\n"); - return -EINVAL; - } - - dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type); - - if (hdr->type != RSC_VDEV) - continue; - - vrsc = (struct fw_rsc_vdev *)hdr->data; - - ret = rproc_handle_vdev(rproc, vrsc, avail); - if (ret) - break; - } - - return ret; -} - /** * rproc_resource_cleanup() - clean up and free all acquired resources * @rproc: rproc handle @@ -807,9 +782,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) { struct device *dev = &rproc->dev; const char *name = rproc->firmware; - struct resource_table *table; + struct rproc_vdev *rvdev; + struct resource_table *table, *devmem_rsc, *tmp; int ret, tablesz; + if (!rproc->rsc) + return -ENOMEM; + ret = rproc_fw_sanity_check(rproc, fw); if (ret) return ret; @@ -835,8 +814,17 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) goto clean_up; } + /* Verify that resource table in loaded fw is unchanged */ + if (rproc->rsc_csum != ip_compute_csum(table, tablesz)) { + dev_err(dev, "resource checksum failed, fw changed?\n"); + ret = -EINVAL; + goto clean_up; + } + + /* handle fw resources which are required to boot rproc */ - ret = rproc_handle_boot_rsc(rproc, table, tablesz); + ret = rproc_handle_resource(rproc, tablesz, + rproc_handle_rsc); if (ret) { dev_err(dev, "Failed to process resources: %d\n", ret); goto clean_up; @@ -849,6 +837,26 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) goto clean_up; } + /* Get the resource table address in device memory */ + devmem_rsc = rproc_get_rsctab_addr(rproc, fw); + + /* Copy the updated resource table to device memory */ + memcpy(devmem_rsc, rproc->rsc, tablesz); + + /* Free the copy of the resource table */ + tmp = rproc->rsc; + rproc->rsc = devmem_rsc; + kfree(tmp); + + /* Update the vdev rsc address */ + list_for_each_entry(rvdev, &rproc->rvdevs, node) { + int offset = (void *)rvdev->rsc - (void *)tmp; + rvdev->rsc = (void *)devmem_rsc + offset; + } + + /* Other virtio drivers will see the rsc table in device memory */ + rproc->rsc = devmem_rsc; + /* power up the remote processor */ ret = rproc->ops->start(rproc); if (ret) { @@ -890,8 +898,22 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context) if (!table) goto out; + rproc->rsc_csum = ip_compute_csum(table, tablesz); + + /* count the numbe of notify-ids */ + rproc->max_notifyid = 0; + rproc->rsc = table; + ret = rproc_handle_resource(rproc, tablesz, + rproc_handle_notifyid_rsc); + + /* Copy resource table containing vdev config info */ + rproc->rsc = kmalloc(tablesz, GFP_KERNEL); + if (rproc->rsc) + memcpy(rproc->rsc, table, tablesz); + /* look for virtio devices and register them */ - ret = rproc_handle_virtio_rsc(rproc, table, tablesz); + ret = rproc_handle_resource(rproc, tablesz, + rproc_handle_vdev_rsc); if (ret) goto out; diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 0d36f94ab51d..3137fba3dc99 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -208,38 +208,19 @@ rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) return ret; } -/** - * rproc_elf_find_rsc_table() - find the resource table - * @rproc: the rproc handle - * @fw: the ELF firmware image - * @tablesz: place holder for providing back the table size - * - * This function finds the resource table inside the remote processor's - * firmware. It is used both upon the registration of @rproc (in order - * to look for and register the supported virito devices), and when the - * @rproc is booted. - * - * Returns the pointer to the resource table if it is found, and write its - * size into @tablesz. If a valid table isn't found, NULL is returned - * (and @tablesz isn't set). - */ -static struct resource_table * -rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, - int *tablesz) +static struct elf32_shdr * +find_rsc_shdr(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size) { - struct elf32_hdr *ehdr; struct elf32_shdr *shdr; + int i; const char *name_table; - struct device *dev = &rproc->dev; struct resource_table *table = NULL; - int i; - const u8 *elf_data = fw->data; + const u8 *elf_data = (void *)ehdr; - ehdr = (struct elf32_hdr *)elf_data; + /* look for the resource table and handle it */ shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; - /* look for the resource table and handle it */ for (i = 0; i < ehdr->e_shnum; i++, shdr++) { int size = shdr->sh_size; int offset = shdr->sh_offset; @@ -250,7 +231,7 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, table = (struct resource_table *)(elf_data + offset); /* make sure we have the entire table */ - if (offset + size > fw->size) { + if (offset + size > fw_size) { dev_err(dev, "resource table truncated\n"); return NULL; } @@ -280,16 +261,75 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, return NULL; } - *tablesz = shdr->sh_size; - break; + return shdr; } + return NULL; +} + +/** + * rproc_elf_find_rsc_table() - find the resource table + * @rproc: the rproc handle + * @fw: the ELF firmware image + * @tablesz: place holder for providing back the table size + * + * This function finds the resource table inside the remote processor's + * firmware. It is used both upon the registration of @rproc (in order + * to look for and register the supported virito devices), and when the + * @rproc is booted. + * + * Returns the pointer to the resource table if it is found, and write its + * size into @tablesz. If a valid table isn't found, NULL is returned + * (and @tablesz isn't set). + */ +static struct resource_table * +rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, + int *tablesz) +{ + struct elf32_hdr *ehdr; + struct elf32_shdr *shdr; + + struct device *dev = &rproc->dev; + struct resource_table *table = NULL; + + const u8 *elf_data = fw->data; + + ehdr = (struct elf32_hdr *)elf_data; + + shdr = find_rsc_shdr(dev, ehdr, fw->size); + if (!shdr) + return NULL; + + /* make sure we have the entire table */ + if (shdr->sh_offset + shdr->sh_size > fw->size) { + dev_err(dev, "resource table truncated\n"); + return NULL; + } + + table = (struct resource_table *)(elf_data + shdr->sh_offset); + *tablesz = shdr->sh_size; + return table; } +struct resource_table *rproc_elf_get_rsctab_addr(struct rproc *rproc, + const struct firmware *fw) +{ + struct elf32_shdr *shdr; + + shdr = find_rsc_shdr(&rproc->dev, (struct elf32_hdr *)fw->data, + fw->size); + if (!shdr) + return NULL; + + /* Find resource table in loaded segments */ + return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size); +} + const struct rproc_fw_ops rproc_elf_fw_ops = { .load = rproc_elf_load_segments, .find_rsc_table = rproc_elf_find_rsc_table, .sanity_check = rproc_elf_sanity_check, - .get_boot_addr = rproc_elf_get_boot_addr + .get_boot_addr = rproc_elf_get_boot_addr, + .get_rsctab_addr = rproc_elf_get_rsctab_addr }; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 7bb66482d061..3a5cb7d64a0f 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -32,6 +32,7 @@ struct rproc; * expects to find it * @sanity_check: sanity check the fw image * @get_boot_addr: get boot address to entry point specified in firmware + * @get_rsctab_addr: get resouce table address as specified in firmware */ struct rproc_fw_ops { struct resource_table *(*find_rsc_table) (struct rproc *rproc, @@ -40,6 +41,8 @@ struct rproc_fw_ops { int (*load)(struct rproc *rproc, const struct firmware *fw); int (*sanity_check)(struct rproc *rproc, const struct firmware *fw); u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw); + struct resource_table *(*get_rsctab_addr)(struct rproc *rproc, + const struct firmware *fw); }; /* from remoteproc_core.c */ @@ -102,6 +105,16 @@ struct resource_table *rproc_find_rsc_table(struct rproc *rproc, return NULL; } +static inline +struct resource_table *rproc_get_rsctab_addr(struct rproc *rproc, + const struct firmware *fw) +{ + if (rproc->fw_ops->get_rsctab_addr) + return rproc->fw_ops->get_rsctab_addr(rproc, fw); + + return NULL; +} + extern const struct rproc_fw_ops rproc_elf_fw_ops; #endif /* REMOTEPROC_INTERNAL_H */ diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index afed9b7731c4..4e22bcc91cec 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -182,16 +182,21 @@ error: */ static u8 rproc_virtio_get_status(struct virtio_device *vdev) { - return 0; + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + return rvdev->rsc->status; } static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) { + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + rvdev->rsc->status = status; dev_dbg(&vdev->dev, "status: %d\n", status); } static void rproc_virtio_reset(struct virtio_device *vdev) { + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + rvdev->rsc->status = 0; dev_dbg(&vdev->dev, "reset !\n"); } @@ -200,7 +205,7 @@ static u32 rproc_virtio_get_features(struct virtio_device *vdev) { struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); - return rvdev->dfeatures; + return rvdev->rsc->dfeatures; } static void rproc_virtio_finalize_features(struct virtio_device *vdev) @@ -219,7 +224,31 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev) * fixed as part of a small resource table overhaul and then an * extension of the virtio resource entries. */ - rvdev->gfeatures = vdev->features[0]; + rvdev->rsc->gfeatures = vdev->features[0]; +} + +void rproc_virtio_get(struct virtio_device *vdev, unsigned offset, + void *buf, unsigned len) +{ + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + void *cfg = &rvdev->rsc->vring[rvdev->rsc->num_of_vrings]; + if (offset + len > rvdev->rsc->config_len) + dev_err(&vdev->dev, + "rproc_virtio_get: access out of bounds\n"); + else + memcpy(buf, cfg + offset, len); +} + +void rproc_virtio_set(struct virtio_device *vdev, unsigned offset, + const void *buf, unsigned len) +{ + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + void *cfg = &rvdev->rsc->vring[rvdev->rsc->num_of_vrings]; + if (offset + len > rvdev->rsc->config_len) + dev_err(&vdev->dev, + "rproc_virtio_set: access out of bounds\n"); + else + memcpy(cfg + offset, buf, len); } static const struct virtio_config_ops rproc_virtio_config_ops = { @@ -230,6 +259,9 @@ static const struct virtio_config_ops rproc_virtio_config_ops = { .reset = rproc_virtio_reset, .set_status = rproc_virtio_set_status, .get_status = rproc_virtio_get_status, + .get = rproc_virtio_get, + .set = rproc_virtio_set, + }; /* diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c index a7743c069339..932e97d78751 100644 --- a/drivers/remoteproc/ste_modem_rproc.c +++ b/drivers/remoteproc/ste_modem_rproc.c @@ -64,26 +64,18 @@ static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw) } /* Find the entry for resource table in the Table of Content */ -static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw) +static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data) { int i; - struct ste_toc *toc; - - if (!fw) - return NULL; - - toc = (void *)fw->data; + const struct ste_toc *toc; + toc = data; /* Search the table for the resource table */ for (i = 0; i < SPROC_MAX_TOC_ENTRIES && toc->table[i].start != 0xffffffff; i++) { - if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME, - sizeof(toc->table[i].name))) { - if (toc->table[i].start > fw->size) - return NULL; + sizeof(toc->table[i].name))) return &toc->table[i]; - } } return NULL; @@ -96,9 +88,12 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw, { struct sproc *sproc = rproc->priv; struct resource_table *table; - struct ste_toc_entry *entry; + const struct ste_toc_entry *entry; - entry = sproc_find_rsc_entry(fw); + if (!fw) + return NULL; + + entry = sproc_find_rsc_entry(fw->data); if (!entry) { sproc_err(sproc, "resource table not found in fw\n"); return NULL; @@ -149,10 +144,30 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw, return table; } +/* Find the resource table inside the remote processor's firmware. */ +static struct resource_table * +sproc_get_rsctab_addr(struct rproc *rproc, const struct firmware *fw) +{ + struct sproc *sproc = rproc->priv; + const struct ste_toc_entry *entry; + + if (!fw || !sproc->fw_addr) + return NULL; + + entry = sproc_find_rsc_entry(sproc->fw_addr); + if (!entry) { + sproc_err(sproc, "resource table not found in fw\n"); + return NULL; + } + + return sproc->fw_addr + entry->start; +} + /* STE modem firmware handler operations */ const struct rproc_fw_ops sproc_fw_ops = { .load = sproc_load_segments, .find_rsc_table = sproc_find_rsc_table, + .get_rsctab_addr = sproc_get_rsctab_addr, }; /* Kick the modem with specified notification id */ @@ -240,6 +255,8 @@ static int sproc_drv_remove(struct platform_device *pdev) /* Unregister as remoteproc device */ rproc_del(sproc->rproc); + dma_free_coherent(sproc->rproc->dev.parent, SPROC_FW_SIZE, + sproc->fw_addr, sproc->fw_dma_addr); rproc_put(sproc->rproc); mdev->drv_data = NULL; @@ -297,10 +314,13 @@ static int sproc_probe(struct platform_device *pdev) /* Register as a remoteproc device */ err = rproc_add(rproc); if (err) - goto free_rproc; + goto free_mem; return 0; +free_mem: + dma_free_coherent(rproc->dev.parent, SPROC_FW_SIZE, + sproc->fw_addr, sproc->fw_dma_addr); free_rproc: /* Reset device data upon error */ mdev->drv_data = NULL; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index faf33324c78f..07deff4982d0 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -41,6 +41,7 @@ #include <linux/virtio.h> #include <linux/completion.h> #include <linux/idr.h> +#include <asm/checksum.h> /** * struct resource_table - firmware resource table header @@ -429,6 +430,8 @@ struct rproc { struct completion crash_comp; bool recovery_disabled; int max_notifyid; + struct resource_table *rsc; + __sum16 rsc_csum; }; /* we currently support only two vrings per rvdev */ @@ -464,14 +467,14 @@ struct rproc_vring { * @vring: the vrings for this vdev * @dfeatures: virtio device features * @gfeatures: virtio guest features + * @rsc: vdev resource entry */ struct rproc_vdev { struct list_head node; struct rproc *rproc; struct virtio_device vdev; struct rproc_vring vring[RVDEV_NUM_VRINGS]; - unsigned long dfeatures; - unsigned long gfeatures; + struct fw_rsc_vdev *rsc; }; struct rproc *rproc_alloc(struct device *dev, const char *name, |