diff mbox series

[v2,06/14] remoteproc: add page lookup for TI PRU to ELF loader

Message ID 1549290167-876-7-git-send-email-rogerq@ti.com
State New
Headers show
Series [v2,01/14] dt-bindings: remoteproc: Add TI PRUSS bindings | expand

Commit Message

Roger Quadros Feb. 4, 2019, 2:22 p.m. UTC
From: David Lechner <david@lechnology.com>


This adds a special handler to the default remoteproc ELF firmware
loader that looks up the memory map on TI PRU firmware files.

These processors have multiple memory maps that share the same address
space, so we need to know the page in addition to the physical address
in order to translate the address to a local CPU address.

Signed-off-by: David Lechner <david@lechnology.com>

Signed-off-by: Roger Quadros <rogerq@ti.com>

---
 drivers/remoteproc/remoteproc_elf_loader.c | 117 +++++++++++++++++++++++++++--
 include/uapi/linux/elf-em.h                |   1 +
 2 files changed, 112 insertions(+), 6 deletions(-)

-- 
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

Comments

Andrew Davis Feb. 4, 2019, 3:19 p.m. UTC | #1
On 2/4/19 8:22 AM, Roger Quadros wrote:
> From: David Lechner <david@lechnology.com>

> 

> This adds a special handler to the default remoteproc ELF firmware

> loader that looks up the memory map on TI PRU firmware files.

> 

> These processors have multiple memory maps that share the same address

> space, so we need to know the page in addition to the physical address

> in order to translate the address to a local CPU address.

> 

> Signed-off-by: David Lechner <david@lechnology.com>

> Signed-off-by: Roger Quadros <rogerq@ti.com>

> ---

>  drivers/remoteproc/remoteproc_elf_loader.c | 117 +++++++++++++++++++++++++++--

>  include/uapi/linux/elf-em.h                |   1 +

>  2 files changed, 112 insertions(+), 6 deletions(-)

> 

> diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c

> index 8888d39..79c9d39 100644

> --- a/drivers/remoteproc/remoteproc_elf_loader.c

> +++ b/drivers/remoteproc/remoteproc_elf_loader.c

> @@ -32,6 +32,103 @@

>  

>  #include "remoteproc_internal.h"

>  

> +#define SHT_TI_PHATTRS 0x7F000004

> +#define SHT_TI_SH_PAGE 0x7F000007

> +

> +struct elf32_ti_phattrs {

> +	Elf32_Half pha_seg_id; /* Segment id */

> +	Elf32_Half pha_tag_id; /* Attribute kind id */

> +	union {

> +		Elf32_Off pha_offset; /* byte offset within the section */

> +		Elf32_Word pha_value; /* Constant tag value */

> +	} pha_un;

> +};

> +

> +/* this struct is reverse engineered, so not sure what most of the values are */


Do we really not know? Someone should go check this, if they are not
used then for now label them "unused", not "unknown".

Andrew

> +struct ti_section_page {

> +	u32 unk0;

> +	u32 unk1;

> +	u32 unk2;

> +	u32 unk3;

> +	u32 unk4;

> +	u16 size;

> +	u16 unk5;

> +	u16 unk6;

> +	u8 data[0]; /* array of size */

> +};

> +

> +/**

> + * rproc_elf_segment_to_map() - Gets memory map for segment

> + * @id: segment id

> + * @elf_data: pointer to ELF file data

> + *

> + * Returns the memory map for the segment.

> + */

> +static int rproc_elf_segment_to_map(u32 id, const u8 *elf_data)

> +{

> +	struct elf32_hdr *ehdr;

> +	struct elf32_shdr *shdr;

> +	struct elf32_ti_phattrs *ti_attrs = NULL;

> +	int i;

> +

> +	ehdr = (struct elf32_hdr *)elf_data;

> +	shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);

> +

> +	if (ehdr->e_machine != EM_TI_PRU)

> +		return 0;

> +

> +	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {

> +		if (shdr->sh_type == SHT_TI_PHATTRS) {

> +			ti_attrs = (struct elf32_ti_phattrs *)(elf_data + shdr->sh_offset);

> +			break;

> +		}

> +	}

> +

> +	if (!ti_attrs)

> +		return 0;

> +

> +	/* list is terminated by tag id == 0 (PHA_NULL) */

> +	for (; ti_attrs->pha_tag_id; ti_attrs++) {

> +		if (ti_attrs->pha_tag_id == 3 && ti_attrs->pha_seg_id == id)

> +			return ti_attrs->pha_un.pha_value;

> +	}

> +

> +	return 0;

> +}

> +

> +/**

> + * rproc_elf_section_to_map() - Gets memory map for section

> + * @id: segment id

> + * @elf_data: pointer to ELF file data

> + *

> + * Returns the memory map for the section.

> + */

> +static int rproc_elf_section_to_map(u32 id, const u8 *elf_data)

> +{

> +	struct elf32_hdr *ehdr;

> +	struct elf32_shdr *shdr;

> +	struct ti_section_page *map = NULL;

> +	int i;

> +

> +	ehdr = (struct elf32_hdr *)elf_data;

> +	shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);

> +

> +	if (ehdr->e_machine != EM_TI_PRU)

> +		return 0;

> +

> +	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {

> +		if (shdr->sh_type == SHT_TI_SH_PAGE) {

> +			map = (struct ti_section_page *)(elf_data + shdr->sh_offset);

> +			break;

> +		}

> +	}

> +

> +	if (!map || id >= map->size)

> +		return 0;

> +

> +	return map->data[id];

> +}

> +

>  /**

>   * rproc_elf_sanity_check() - Sanity Check ELF firmware image

>   * @rproc: the remote processor handle

> @@ -147,7 +244,7 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)

>  	struct device *dev = &rproc->dev;

>  	struct elf32_hdr *ehdr;

>  	struct elf32_phdr *phdr;

> -	int i, ret = 0;

> +	int i, map, ret = 0;

>  	const u8 *elf_data = fw->data;

>  

>  	ehdr = (struct elf32_hdr *)elf_data;

> @@ -181,8 +278,10 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)

>  			break;

>  		}

>  

> +		map = rproc_elf_segment_to_map(i, elf_data);

> +

>  		/* grab the kernel address for this device address */

> -		ptr = rproc_da_to_va(rproc, da, memsz, 0);

> +		ptr = rproc_da_to_va(rproc, da, memsz, map);

>  		if (!ptr) {

>  			dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);

>  			ret = -EINVAL;

> @@ -209,7 +308,7 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)

>  EXPORT_SYMBOL(rproc_elf_load_segments);

>  

>  static struct elf32_shdr *

> -find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)

> +find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size, int *id)

>  {

>  	struct elf32_shdr *shdr;

>  	int i;

> @@ -261,6 +360,9 @@ find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)

>  			return NULL;

>  		}

>  

> +		if (id)

> +			*id = i;

> +

>  		return shdr;

>  	}

>  

> @@ -288,7 +390,7 @@ int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw)

>  

>  	ehdr = (struct elf32_hdr *)elf_data;

>  

> -	shdr = find_table(dev, ehdr, fw->size);

> +	shdr = find_table(dev, ehdr, fw->size, NULL);

>  	if (!shdr)

>  		return -EINVAL;

>  

> @@ -328,11 +430,14 @@ struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,

>  {

>  	struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;

>  	struct elf32_shdr *shdr;

> +	int id, map;

>  

> -	shdr = find_table(&rproc->dev, ehdr, fw->size);

> +	shdr = find_table(&rproc->dev, ehdr, fw->size, &id);

>  	if (!shdr)

>  		return NULL;

>  

> -	return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size, 0);

> +	map = rproc_elf_section_to_map(id, fw->data);

> +

> +	return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size, map);

>  }

>  EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);

> diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h

> index 0c3000fa..70b487a 100644

> --- a/include/uapi/linux/elf-em.h

> +++ b/include/uapi/linux/elf-em.h

> @@ -38,6 +38,7 @@

>  #define EM_BLACKFIN     106     /* ADI Blackfin Processor */

>  #define EM_ALTERA_NIOS2	113	/* Altera Nios II soft-core processor */

>  #define EM_TI_C6000	140	/* TI C6X DSPs */

> +#define EM_TI_PRU	144	/* TI Programmable Realtime Unit */

>  #define EM_AARCH64	183	/* ARM 64 bit */

>  #define EM_TILEPRO	188	/* Tilera TILEPro */

>  #define EM_MICROBLAZE	189	/* Xilinx MicroBlaze */

>
diff mbox series

Patch

diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c
index 8888d39..79c9d39 100644
--- a/drivers/remoteproc/remoteproc_elf_loader.c
+++ b/drivers/remoteproc/remoteproc_elf_loader.c
@@ -32,6 +32,103 @@ 
 
 #include "remoteproc_internal.h"
 
+#define SHT_TI_PHATTRS 0x7F000004
+#define SHT_TI_SH_PAGE 0x7F000007
+
+struct elf32_ti_phattrs {
+	Elf32_Half pha_seg_id; /* Segment id */
+	Elf32_Half pha_tag_id; /* Attribute kind id */
+	union {
+		Elf32_Off pha_offset; /* byte offset within the section */
+		Elf32_Word pha_value; /* Constant tag value */
+	} pha_un;
+};
+
+/* this struct is reverse engineered, so not sure what most of the values are */
+struct ti_section_page {
+	u32 unk0;
+	u32 unk1;
+	u32 unk2;
+	u32 unk3;
+	u32 unk4;
+	u16 size;
+	u16 unk5;
+	u16 unk6;
+	u8 data[0]; /* array of size */
+};
+
+/**
+ * rproc_elf_segment_to_map() - Gets memory map for segment
+ * @id: segment id
+ * @elf_data: pointer to ELF file data
+ *
+ * Returns the memory map for the segment.
+ */
+static int rproc_elf_segment_to_map(u32 id, const u8 *elf_data)
+{
+	struct elf32_hdr *ehdr;
+	struct elf32_shdr *shdr;
+	struct elf32_ti_phattrs *ti_attrs = NULL;
+	int i;
+
+	ehdr = (struct elf32_hdr *)elf_data;
+	shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
+
+	if (ehdr->e_machine != EM_TI_PRU)
+		return 0;
+
+	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
+		if (shdr->sh_type == SHT_TI_PHATTRS) {
+			ti_attrs = (struct elf32_ti_phattrs *)(elf_data + shdr->sh_offset);
+			break;
+		}
+	}
+
+	if (!ti_attrs)
+		return 0;
+
+	/* list is terminated by tag id == 0 (PHA_NULL) */
+	for (; ti_attrs->pha_tag_id; ti_attrs++) {
+		if (ti_attrs->pha_tag_id == 3 && ti_attrs->pha_seg_id == id)
+			return ti_attrs->pha_un.pha_value;
+	}
+
+	return 0;
+}
+
+/**
+ * rproc_elf_section_to_map() - Gets memory map for section
+ * @id: segment id
+ * @elf_data: pointer to ELF file data
+ *
+ * Returns the memory map for the section.
+ */
+static int rproc_elf_section_to_map(u32 id, const u8 *elf_data)
+{
+	struct elf32_hdr *ehdr;
+	struct elf32_shdr *shdr;
+	struct ti_section_page *map = NULL;
+	int i;
+
+	ehdr = (struct elf32_hdr *)elf_data;
+	shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
+
+	if (ehdr->e_machine != EM_TI_PRU)
+		return 0;
+
+	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
+		if (shdr->sh_type == SHT_TI_SH_PAGE) {
+			map = (struct ti_section_page *)(elf_data + shdr->sh_offset);
+			break;
+		}
+	}
+
+	if (!map || id >= map->size)
+		return 0;
+
+	return map->data[id];
+}
+
 /**
  * rproc_elf_sanity_check() - Sanity Check ELF firmware image
  * @rproc: the remote processor handle
@@ -147,7 +244,7 @@  int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
 	struct device *dev = &rproc->dev;
 	struct elf32_hdr *ehdr;
 	struct elf32_phdr *phdr;
-	int i, ret = 0;
+	int i, map, ret = 0;
 	const u8 *elf_data = fw->data;
 
 	ehdr = (struct elf32_hdr *)elf_data;
@@ -181,8 +278,10 @@  int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
 			break;
 		}
 
+		map = rproc_elf_segment_to_map(i, elf_data);
+
 		/* grab the kernel address for this device address */
-		ptr = rproc_da_to_va(rproc, da, memsz, 0);
+		ptr = rproc_da_to_va(rproc, da, memsz, map);
 		if (!ptr) {
 			dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
 			ret = -EINVAL;
@@ -209,7 +308,7 @@  int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
 EXPORT_SYMBOL(rproc_elf_load_segments);
 
 static struct elf32_shdr *
-find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
+find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size, int *id)
 {
 	struct elf32_shdr *shdr;
 	int i;
@@ -261,6 +360,9 @@  find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
 			return NULL;
 		}
 
+		if (id)
+			*id = i;
+
 		return shdr;
 	}
 
@@ -288,7 +390,7 @@  int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw)
 
 	ehdr = (struct elf32_hdr *)elf_data;
 
-	shdr = find_table(dev, ehdr, fw->size);
+	shdr = find_table(dev, ehdr, fw->size, NULL);
 	if (!shdr)
 		return -EINVAL;
 
@@ -328,11 +430,14 @@  struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
 {
 	struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
 	struct elf32_shdr *shdr;
+	int id, map;
 
-	shdr = find_table(&rproc->dev, ehdr, fw->size);
+	shdr = find_table(&rproc->dev, ehdr, fw->size, &id);
 	if (!shdr)
 		return NULL;
 
-	return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size, 0);
+	map = rproc_elf_section_to_map(id, fw->data);
+
+	return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size, map);
 }
 EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);
diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h
index 0c3000fa..70b487a 100644
--- a/include/uapi/linux/elf-em.h
+++ b/include/uapi/linux/elf-em.h
@@ -38,6 +38,7 @@ 
 #define EM_BLACKFIN     106     /* ADI Blackfin Processor */
 #define EM_ALTERA_NIOS2	113	/* Altera Nios II soft-core processor */
 #define EM_TI_C6000	140	/* TI C6X DSPs */
+#define EM_TI_PRU	144	/* TI Programmable Realtime Unit */
 #define EM_AARCH64	183	/* ARM 64 bit */
 #define EM_TILEPRO	188	/* Tilera TILEPro */
 #define EM_MICROBLAZE	189	/* Xilinx MicroBlaze */