Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f0dd07a authored by Bjorn Andersson's avatar Bjorn Andersson
Browse files

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: default avatarBjorn Andersson <bjorn.andersson@linaro.org>
parent 3e8b571a
Loading
Loading
Loading
Loading
+2 −28
Original line number Original line Diff line number Diff line
@@ -75,35 +75,9 @@ struct qcom_adsp {
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
static int adsp_load(struct rproc *rproc, const struct firmware *fw)
{
{
	struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
	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 = {
static const struct rproc_fw_ops adsp_fw_ops = {
+90 −47
Original line number Original line Diff line number Diff line
@@ -15,35 +15,44 @@
 * GNU General Public License for more details.
 * GNU General Public License for more details.
 */
 */


#include <linux/device.h>
#include <linux/elf.h>
#include <linux/elf.h>
#include <linux/firmware.h>
#include <linux/firmware.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/remoteproc.h>
#include <linux/qcom_scm.h>
#include <linux/sizes.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/slab.h>


#include "remoteproc_internal.h"
#include "qcom_mdt_loader.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
 * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt
 * @fw:		firmware handle
 * @fw:		firmware object for the mdt file
 * @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
 *
 *
 * 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,
ssize_t qcom_mdt_get_size(const struct firmware *fw)
		   size_t *fw_size, bool *fw_relocate)
{
{
	const struct elf32_phdr *phdrs;
	const struct elf32_phdr *phdrs;
	const struct elf32_phdr *phdr;
	const struct elf32_phdr *phdr;
	const struct elf32_hdr *ehdr;
	const struct elf32_hdr *ehdr;
	phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
	phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX;
	phys_addr_t max_addr = 0;
	phys_addr_t max_addr = 0;
	bool relocate = false;
	int i;
	int i;


	ehdr = (struct elf32_hdr *)fw->data;
	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++) {
	for (i = 0; i < ehdr->e_phnum; i++) {
		phdr = &phdrs[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;
			continue;


		if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
			relocate = true;

		if (phdr->p_paddr < min_addr)
		if (phdr->p_paddr < min_addr)
			min_addr = phdr->p_paddr;
			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);
			max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
	}
	}


	if (fw_addr)
	return min_addr < max_addr ? max_addr - min_addr : -EINVAL;
		*fw_addr = min_addr;
	if (fw_size)
		*fw_size = max_addr - min_addr;
	if (fw_relocate)
		*fw_relocate = relocate;

	return 0;
}
}
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
 * qcom_mdt_load() - load the firmware which header is loaded as fw
 * @rproc:	rproc handle
 * @dev:	device handle to associate resources with
 * @fw:		frimware object for the header
 * @fw:		firmware object for the mdt file
 * @firmware:	filename of the firmware, for building .bXX names
 * @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.
 * Returns 0 on success, negative errno otherwise.
 */
 */
int qcom_mdt_load(struct rproc *rproc,
int qcom_mdt_load(struct device *dev, const struct firmware *fw,
		  const struct firmware *fw,
		  const char *firmware, int pas_id, void *mem_region,
		  const char *firmware)
		  phys_addr_t mem_phys, size_t mem_size)
{
{
	const struct elf32_phdr *phdrs;
	const struct elf32_phdr *phdrs;
	const struct elf32_phdr *phdr;
	const struct elf32_phdr *phdr;
	const struct elf32_hdr *ehdr;
	const struct elf32_hdr *ehdr;
	const struct firmware *seg_fw;
	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 fw_name_len;
	size_t offset;
	char *fw_name;
	char *fw_name;
	bool relocate = false;
	void *ptr;
	void *ptr;
	int ret;
	int ret;
	int i;
	int i;


	if (!fw || !mem_region || !mem_phys || !mem_size)
		return -EINVAL;

	ehdr = (struct elf32_hdr *)fw->data;
	ehdr = (struct elf32_hdr *)fw->data;
	phdrs = (struct elf32_phdr *)(ehdr + 1);
	phdrs = (struct elf32_phdr *)(ehdr + 1);


@@ -115,31 +120,68 @@ int qcom_mdt_load(struct rproc *rproc,
	if (!fw_name)
	if (!fw_name)
		return -ENOMEM;
		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++) {
	for (i = 0; i < ehdr->e_phnum; i++) {
		phdr = &phdrs[i];
		phdr = &phdrs[i];


		if (phdr->p_type != PT_LOAD)
		if (!mdt_phdr_valid(phdr))
			continue;
			continue;


		if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
		if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
			continue;
			relocate = true;


		if (!phdr->p_memsz)
		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;
	}

	for (i = 0; i < ehdr->e_phnum; i++) {
		phdr = &phdrs[i];

		if (!mdt_phdr_valid(phdr))
			continue;
			continue;


		ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz);
		offset = phdr->p_paddr - mem_reloc;
		if (!ptr) {
		if (offset < 0 || offset + phdr->p_memsz > mem_size) {
			dev_err(&rproc->dev, "segment outside memory range\n");
			dev_err(dev, "segment outside memory range\n");
			ret = -EINVAL;
			ret = -EINVAL;
			break;
			break;
		}
		}


		ptr = mem_region + offset;

		if (phdr->p_filesz) {
		if (phdr->p_filesz) {
			sprintf(fw_name + fw_name_len - 3, "b%02d", i);
			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) {
			if (ret) {
				dev_err(&rproc->dev, "failed to load %s\n",
				dev_err(dev, "failed to load %s\n", fw_name);
					fw_name);
				break;
				break;
			}
			}


@@ -152,6 +194,7 @@ int qcom_mdt_load(struct rproc *rproc,
			memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
			memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
	}
	}


out:
	kfree(fw_name);
	kfree(fw_name);


	return ret;
	return ret;
+4 −3
Original line number Original line Diff line number Diff line
@@ -5,8 +5,9 @@
#define QCOM_MDT_TYPE_HASH	(2 << 24)
#define QCOM_MDT_TYPE_HASH	(2 << 24)
#define QCOM_MDT_RELOCATABLE	BIT(27)
#define QCOM_MDT_RELOCATABLE	BIT(27)


int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name);
ssize_t qcom_mdt_get_size(const struct firmware *fw);

int qcom_mdt_load(struct device *dev, const struct firmware *fw,
int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate);
		  const char *fw_name, int pas_id, void *mem_region,
		  phys_addr_t mem_phys, size_t mem_size);


#endif
#endif
+2 −27
Original line number Original line Diff line number Diff line
@@ -153,34 +153,9 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
{
{
	struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
	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 = {
static const struct rproc_fw_ops wcnss_fw_ops = {