diff options
author | Bjorn Andersson <bjorn.andersson@linaro.org> | 2017-01-27 02:17:23 -0800 |
---|---|---|
committer | Bjorn Andersson <bjorn.andersson@linaro.org> | 2017-02-06 08:57:24 -0800 |
commit | 7f0dd07a9b29cfadffcd2fa08902a844f5c94e86 (patch) | |
tree | 022b7fe0d968f61d2e2035a255e79f43fdb3da9e | |
parent | 3e8b571a9a0881ba3381ca0915995696da145ab8 (diff) |
remoteproc: qcom: mdt_loader: Refactor MDT loader
Pushing the SCM calls into the MDT loader reduces duplication in the
callers and allows for non-remoteproc clients to use the helper for
parsing and loading MDT files.
Cc: Andy Gross <andy.gross@linaro.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-rw-r--r-- | drivers/remoteproc/qcom_adsp_pil.c | 30 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_mdt_loader.c | 137 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_mdt_loader.h | 7 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_wcnss.c | 29 |
4 files changed, 98 insertions, 105 deletions
diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 4fb4c4b47875..c1ee5c818b42 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c @@ -75,35 +75,9 @@ struct qcom_adsp { static int adsp_load(struct rproc *rproc, const struct firmware *fw) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; - phys_addr_t fw_addr; - size_t fw_size; - bool relocate; - int ret; - - ret = qcom_scm_pas_init_image(adsp->pas_id, fw->data, fw->size); - if (ret) { - dev_err(&rproc->dev, "invalid firmware metadata\n"); - return ret; - } - - ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); - if (ret) { - dev_err(&rproc->dev, "failed to parse mdt header\n"); - return ret; - } - - if (relocate) { - adsp->mem_reloc = fw_addr; - - ret = qcom_scm_pas_mem_setup(adsp->pas_id, - adsp->mem_phys, fw_size); - if (ret) { - dev_err(&rproc->dev, "unable to setup memory for image\n"); - return ret; - } - } - return qcom_mdt_load(rproc, fw, rproc->firmware); + return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, + adsp->mem_region, adsp->mem_phys, adsp->mem_size); } static const struct rproc_fw_ops adsp_fw_ops = { diff --git a/drivers/remoteproc/qcom_mdt_loader.c b/drivers/remoteproc/qcom_mdt_loader.c index fe7d5207ad17..790ab31f31ff 100644 --- a/drivers/remoteproc/qcom_mdt_loader.c +++ b/drivers/remoteproc/qcom_mdt_loader.c @@ -15,35 +15,44 @@ * GNU General Public License for more details. */ +#include <linux/device.h> #include <linux/elf.h> #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/remoteproc.h> +#include <linux/qcom_scm.h> #include <linux/sizes.h> #include <linux/slab.h> -#include "remoteproc_internal.h" #include "qcom_mdt_loader.h" +static bool mdt_phdr_valid(const struct elf32_phdr *phdr) +{ + if (phdr->p_type != PT_LOAD) + return false; + + if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) + return false; + + if (!phdr->p_memsz) + return false; + + return true; +} + /** - * qcom_mdt_parse() - extract useful parameters from the mdt header - * @fw: firmware handle - * @fw_addr: optional reference for base of the firmware's memory region - * @fw_size: optional reference for size of the firmware's memory region - * @fw_relocate: optional reference for flagging if the firmware is relocatable + * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt + * @fw: firmware object for the mdt file * - * Returns 0 on success, negative errno otherwise. + * Returns size of the loaded firmware blob, or -EINVAL on failure. */ -int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, - size_t *fw_size, bool *fw_relocate) +ssize_t qcom_mdt_get_size(const struct firmware *fw) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; const struct elf32_hdr *ehdr; phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; phys_addr_t max_addr = 0; - bool relocate = false; int i; ehdr = (struct elf32_hdr *)fw->data; @@ -52,18 +61,9 @@ int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (phdr->p_type != PT_LOAD) + if (!mdt_phdr_valid(phdr)) continue; - if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) - continue; - - if (!phdr->p_memsz) - continue; - - if (phdr->p_flags & QCOM_MDT_RELOCATABLE) - relocate = true; - if (phdr->p_paddr < min_addr) min_addr = phdr->p_paddr; @@ -71,39 +71,44 @@ int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); } - if (fw_addr) - *fw_addr = min_addr; - if (fw_size) - *fw_size = max_addr - min_addr; - if (fw_relocate) - *fw_relocate = relocate; - - return 0; + return min_addr < max_addr ? max_addr - min_addr : -EINVAL; } -EXPORT_SYMBOL_GPL(qcom_mdt_parse); +EXPORT_SYMBOL_GPL(qcom_mdt_get_size); /** - * qcom_mdt_load() - load the firmware which header is defined in fw - * @rproc: rproc handle - * @fw: frimware object for the header - * @firmware: filename of the firmware, for building .bXX names + * qcom_mdt_load() - load the firmware which header is loaded as fw + * @dev: device handle to associate resources with + * @fw: firmware object for the mdt file + * @firmware: name of the firmware, for construction of segment file names + * @pas_id: PAS identifier + * @mem_region: allocated memory region to load firmware into + * @mem_phys: physical address of allocated memory region + * @mem_size: size of the allocated memory region * * Returns 0 on success, negative errno otherwise. */ -int qcom_mdt_load(struct rproc *rproc, - const struct firmware *fw, - const char *firmware) +int qcom_mdt_load(struct device *dev, const struct firmware *fw, + const char *firmware, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; const struct elf32_hdr *ehdr; const struct firmware *seg_fw; + phys_addr_t mem_reloc; + phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; + phys_addr_t max_addr = 0; size_t fw_name_len; + size_t offset; char *fw_name; + bool relocate = false; void *ptr; int ret; int i; + if (!fw || !mem_region || !mem_phys || !mem_size) + return -EINVAL; + ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); @@ -115,31 +120,68 @@ int qcom_mdt_load(struct rproc *rproc, if (!fw_name) return -ENOMEM; + ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); + if (ret) { + dev_err(dev, "invalid firmware metadata\n"); + goto out; + } + for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; - if (phdr->p_type != PT_LOAD) + if (!mdt_phdr_valid(phdr)) continue; - if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) - continue; + if (phdr->p_flags & QCOM_MDT_RELOCATABLE) + relocate = true; + + if (phdr->p_paddr < min_addr) + min_addr = phdr->p_paddr; + + if (phdr->p_paddr + phdr->p_memsz > max_addr) + max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); + } + + if (relocate) { + ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); + if (ret) { + dev_err(dev, "unable to setup relocation\n"); + goto out; + } + + /* + * The image is relocatable, so offset each segment based on + * the lowest segment address. + */ + mem_reloc = min_addr; + } else { + /* + * Image is not relocatable, so offset each segment based on + * the allocated physical chunk of memory. + */ + mem_reloc = mem_phys; + } - if (!phdr->p_memsz) + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + + if (!mdt_phdr_valid(phdr)) continue; - ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz); - if (!ptr) { - dev_err(&rproc->dev, "segment outside memory range\n"); + offset = phdr->p_paddr - mem_reloc; + if (offset < 0 || offset + phdr->p_memsz > mem_size) { + dev_err(dev, "segment outside memory range\n"); ret = -EINVAL; break; } + ptr = mem_region + offset; + if (phdr->p_filesz) { sprintf(fw_name + fw_name_len - 3, "b%02d", i); - ret = request_firmware(&seg_fw, fw_name, &rproc->dev); + ret = request_firmware(&seg_fw, fw_name, dev); if (ret) { - dev_err(&rproc->dev, "failed to load %s\n", - fw_name); + dev_err(dev, "failed to load %s\n", fw_name); break; } @@ -152,6 +194,7 @@ int qcom_mdt_load(struct rproc *rproc, memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } +out: kfree(fw_name); return ret; diff --git a/drivers/remoteproc/qcom_mdt_loader.h b/drivers/remoteproc/qcom_mdt_loader.h index e513548e52ec..ff6e45b77326 100644 --- a/drivers/remoteproc/qcom_mdt_loader.h +++ b/drivers/remoteproc/qcom_mdt_loader.h @@ -5,8 +5,9 @@ #define QCOM_MDT_TYPE_HASH (2 << 24) #define QCOM_MDT_RELOCATABLE BIT(27) -int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name); - -int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate); +ssize_t qcom_mdt_get_size(const struct firmware *fw); +int qcom_mdt_load(struct device *dev, const struct firmware *fw, + const char *fw_name, int pas_id, void *mem_region, + phys_addr_t mem_phys, size_t mem_size); #endif diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index f158f8243b04..fbb25ea4ae8a 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -153,34 +153,9 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, static int wcnss_load(struct rproc *rproc, const struct firmware *fw) { struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; - phys_addr_t fw_addr; - size_t fw_size; - bool relocate; - int ret; - - ret = qcom_scm_pas_init_image(WCNSS_PAS_ID, fw->data, fw->size); - if (ret) { - dev_err(&rproc->dev, "invalid firmware metadata\n"); - return ret; - } - - ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); - if (ret) { - dev_err(&rproc->dev, "failed to parse mdt header\n"); - return ret; - } - - if (relocate) { - wcnss->mem_reloc = fw_addr; - - ret = qcom_scm_pas_mem_setup(WCNSS_PAS_ID, wcnss->mem_phys, fw_size); - if (ret) { - dev_err(&rproc->dev, "unable to setup memory for image\n"); - return ret; - } - } - return qcom_mdt_load(rproc, fw, rproc->firmware); + return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, + wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size); } static const struct rproc_fw_ops wcnss_fw_ops = { |