diff options
| -rw-r--r-- | drivers/remoteproc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 249 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_elf_loader.c | 287 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_internal.h | 11 |
4 files changed, 300 insertions, 248 deletions
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 5445d9b23294..934ce6e2c66b 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile | |||
| @@ -6,4 +6,5 @@ obj-$(CONFIG_REMOTEPROC) += remoteproc.o | |||
| 6 | remoteproc-y := remoteproc_core.o | 6 | remoteproc-y := remoteproc_core.o |
| 7 | remoteproc-y += remoteproc_debugfs.o | 7 | remoteproc-y += remoteproc_debugfs.o |
| 8 | remoteproc-y += remoteproc_virtio.o | 8 | remoteproc-y += remoteproc_virtio.o |
| 9 | remoteproc-y += remoteproc_elf_loader.o | ||
| 9 | obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o | 10 | obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o |
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 3173e213940b..c68b3bb567f4 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c | |||
| @@ -149,7 +149,7 @@ static void rproc_disable_iommu(struct rproc *rproc) | |||
| 149 | * but only on kernel direct mapped RAM memory. Instead, we're just using | 149 | * but only on kernel direct mapped RAM memory. Instead, we're just using |
| 150 | * here the output of the DMA API, which should be more correct. | 150 | * here the output of the DMA API, which should be more correct. |
| 151 | */ | 151 | */ |
| 152 | static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) | 152 | void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) |
| 153 | { | 153 | { |
| 154 | struct rproc_mem_entry *carveout; | 154 | struct rproc_mem_entry *carveout; |
| 155 | void *ptr = NULL; | 155 | void *ptr = NULL; |
| @@ -173,96 +173,6 @@ static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) | |||
| 173 | return ptr; | 173 | return ptr; |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | /** | ||
| 177 | * rproc_load_segments() - load firmware segments to memory | ||
| 178 | * @rproc: remote processor which will be booted using these fw segments | ||
| 179 | * @fw: the ELF firmware image | ||
| 180 | * | ||
| 181 | * This function loads the firmware segments to memory, where the remote | ||
| 182 | * processor expects them. | ||
| 183 | * | ||
| 184 | * Some remote processors will expect their code and data to be placed | ||
| 185 | * in specific device addresses, and can't have them dynamically assigned. | ||
| 186 | * | ||
| 187 | * We currently support only those kind of remote processors, and expect | ||
| 188 | * the program header's paddr member to contain those addresses. We then go | ||
| 189 | * through the physically contiguous "carveout" memory regions which we | ||
| 190 | * allocated (and mapped) earlier on behalf of the remote processor, | ||
| 191 | * and "translate" device address to kernel addresses, so we can copy the | ||
| 192 | * segments where they are expected. | ||
| 193 | * | ||
| 194 | * Currently we only support remote processors that required carveout | ||
| 195 | * allocations and got them mapped onto their iommus. Some processors | ||
| 196 | * might be different: they might not have iommus, and would prefer to | ||
| 197 | * directly allocate memory for every segment/resource. This is not yet | ||
| 198 | * supported, though. | ||
| 199 | */ | ||
| 200 | static int | ||
| 201 | rproc_load_segments(struct rproc *rproc, const struct firmware *fw) | ||
| 202 | { | ||
| 203 | struct device *dev = &rproc->dev; | ||
| 204 | struct elf32_hdr *ehdr; | ||
| 205 | struct elf32_phdr *phdr; | ||
| 206 | int i, ret = 0; | ||
| 207 | const u8 *elf_data = fw->data; | ||
| 208 | |||
| 209 | ehdr = (struct elf32_hdr *)elf_data; | ||
| 210 | phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); | ||
| 211 | |||
| 212 | /* go through the available ELF segments */ | ||
| 213 | for (i = 0; i < ehdr->e_phnum; i++, phdr++) { | ||
| 214 | u32 da = phdr->p_paddr; | ||
| 215 | u32 memsz = phdr->p_memsz; | ||
| 216 | u32 filesz = phdr->p_filesz; | ||
| 217 | u32 offset = phdr->p_offset; | ||
| 218 | void *ptr; | ||
| 219 | |||
| 220 | if (phdr->p_type != PT_LOAD) | ||
| 221 | continue; | ||
| 222 | |||
| 223 | dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", | ||
| 224 | phdr->p_type, da, memsz, filesz); | ||
| 225 | |||
| 226 | if (filesz > memsz) { | ||
| 227 | dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", | ||
| 228 | filesz, memsz); | ||
| 229 | ret = -EINVAL; | ||
| 230 | break; | ||
| 231 | } | ||
| 232 | |||
| 233 | if (offset + filesz > fw->size) { | ||
| 234 | dev_err(dev, "truncated fw: need 0x%x avail 0x%x\n", | ||
| 235 | offset + filesz, fw->size); | ||
| 236 | ret = -EINVAL; | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | |||
| 240 | /* grab the kernel address for this device address */ | ||
| 241 | ptr = rproc_da_to_va(rproc, da, memsz); | ||
| 242 | if (!ptr) { | ||
| 243 | dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); | ||
| 244 | ret = -EINVAL; | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | |||
| 248 | /* put the segment where the remote processor expects it */ | ||
| 249 | if (phdr->p_filesz) | ||
| 250 | memcpy(ptr, elf_data + phdr->p_offset, filesz); | ||
| 251 | |||
| 252 | /* | ||
| 253 | * Zero out remaining memory for this segment. | ||
| 254 | * | ||
| 255 | * This isn't strictly required since dma_alloc_coherent already | ||
| 256 | * did this for us. albeit harmless, we may consider removing | ||
| 257 | * this. | ||
| 258 | */ | ||
| 259 | if (memsz > filesz) | ||
| 260 | memset(ptr + filesz, 0, memsz - filesz); | ||
| 261 | } | ||
| 262 | |||
| 263 | return ret; | ||
| 264 | } | ||
| 265 | |||
| 266 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) | 176 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) |
| 267 | { | 177 | { |
| 268 | struct rproc *rproc = rvdev->rproc; | 178 | struct rproc *rproc = rvdev->rproc; |
| @@ -817,85 +727,6 @@ rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int l | |||
| 817 | } | 727 | } |
| 818 | 728 | ||
| 819 | /** | 729 | /** |
| 820 | * rproc_find_rsc_table() - find the resource table | ||
| 821 | * @rproc: the rproc handle | ||
| 822 | * @fw: the ELF firmware image | ||
| 823 | * @tablesz: place holder for providing back the table size | ||
| 824 | * | ||
| 825 | * This function finds the resource table inside the remote processor's | ||
| 826 | * firmware. It is used both upon the registration of @rproc (in order | ||
| 827 | * to look for and register the supported virito devices), and when the | ||
| 828 | * @rproc is booted. | ||
| 829 | * | ||
| 830 | * Returns the pointer to the resource table if it is found, and write its | ||
| 831 | * size into @tablesz. If a valid table isn't found, NULL is returned | ||
| 832 | * (and @tablesz isn't set). | ||
| 833 | */ | ||
| 834 | static struct resource_table * | ||
| 835 | rproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw, | ||
| 836 | int *tablesz) | ||
| 837 | { | ||
| 838 | struct elf32_hdr *ehdr; | ||
| 839 | struct elf32_shdr *shdr; | ||
| 840 | const char *name_table; | ||
| 841 | struct device *dev = &rproc->dev; | ||
| 842 | struct resource_table *table = NULL; | ||
| 843 | int i; | ||
| 844 | const u8 *elf_data = fw->data; | ||
| 845 | |||
| 846 | ehdr = (struct elf32_hdr *)elf_data; | ||
| 847 | shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); | ||
| 848 | name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; | ||
| 849 | |||
| 850 | /* look for the resource table and handle it */ | ||
| 851 | for (i = 0; i < ehdr->e_shnum; i++, shdr++) { | ||
| 852 | int size = shdr->sh_size; | ||
| 853 | int offset = shdr->sh_offset; | ||
| 854 | |||
| 855 | if (strcmp(name_table + shdr->sh_name, ".resource_table")) | ||
| 856 | continue; | ||
| 857 | |||
| 858 | table = (struct resource_table *)(elf_data + offset); | ||
| 859 | |||
| 860 | /* make sure we have the entire table */ | ||
| 861 | if (offset + size > fw->size) { | ||
| 862 | dev_err(dev, "resource table truncated\n"); | ||
| 863 | return NULL; | ||
| 864 | } | ||
| 865 | |||
| 866 | /* make sure table has at least the header */ | ||
| 867 | if (sizeof(struct resource_table) > size) { | ||
| 868 | dev_err(dev, "header-less resource table\n"); | ||
| 869 | return NULL; | ||
| 870 | } | ||
| 871 | |||
| 872 | /* we don't support any version beyond the first */ | ||
| 873 | if (table->ver != 1) { | ||
| 874 | dev_err(dev, "unsupported fw ver: %d\n", table->ver); | ||
| 875 | return NULL; | ||
| 876 | } | ||
| 877 | |||
| 878 | /* make sure reserved bytes are zeroes */ | ||
| 879 | if (table->reserved[0] || table->reserved[1]) { | ||
| 880 | dev_err(dev, "non zero reserved bytes\n"); | ||
| 881 | return NULL; | ||
| 882 | } | ||
| 883 | |||
| 884 | /* make sure the offsets array isn't truncated */ | ||
| 885 | if (table->num * sizeof(table->offset[0]) + | ||
| 886 | sizeof(struct resource_table) > size) { | ||
| 887 | dev_err(dev, "resource table incomplete\n"); | ||
| 888 | return NULL; | ||
| 889 | } | ||
| 890 | |||
| 891 | *tablesz = shdr->sh_size; | ||
| 892 | break; | ||
| 893 | } | ||
| 894 | |||
| 895 | return table; | ||
| 896 | } | ||
| 897 | |||
| 898 | /** | ||
| 899 | * rproc_resource_cleanup() - clean up and free all acquired resources | 730 | * rproc_resource_cleanup() - clean up and free all acquired resources |
| 900 | * @rproc: rproc handle | 731 | * @rproc: rproc handle |
| 901 | * | 732 | * |
| @@ -938,84 +769,6 @@ static void rproc_resource_cleanup(struct rproc *rproc) | |||
| 938 | } | 769 | } |
| 939 | } | 770 | } |
| 940 | 771 | ||
| 941 | /* make sure this fw image is sane */ | ||
| 942 | static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) | ||
| 943 | { | ||
| 944 | const char *name = rproc->firmware; | ||
| 945 | struct device *dev = &rproc->dev; | ||
| 946 | struct elf32_hdr *ehdr; | ||
| 947 | char class; | ||
| 948 | |||
| 949 | if (!fw) { | ||
| 950 | dev_err(dev, "failed to load %s\n", name); | ||
| 951 | return -EINVAL; | ||
| 952 | } | ||
| 953 | |||
| 954 | if (fw->size < sizeof(struct elf32_hdr)) { | ||
| 955 | dev_err(dev, "Image is too small\n"); | ||
| 956 | return -EINVAL; | ||
| 957 | } | ||
| 958 | |||
| 959 | ehdr = (struct elf32_hdr *)fw->data; | ||
| 960 | |||
| 961 | /* We only support ELF32 at this point */ | ||
| 962 | class = ehdr->e_ident[EI_CLASS]; | ||
| 963 | if (class != ELFCLASS32) { | ||
| 964 | dev_err(dev, "Unsupported class: %d\n", class); | ||
| 965 | return -EINVAL; | ||
| 966 | } | ||
| 967 | |||
| 968 | /* We assume the firmware has the same endianess as the host */ | ||
| 969 | # ifdef __LITTLE_ENDIAN | ||
| 970 | if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { | ||
| 971 | # else /* BIG ENDIAN */ | ||
| 972 | if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { | ||
| 973 | # endif | ||
| 974 | dev_err(dev, "Unsupported firmware endianess\n"); | ||
| 975 | return -EINVAL; | ||
| 976 | } | ||
| 977 | |||
| 978 | if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { | ||
| 979 | dev_err(dev, "Image is too small\n"); | ||
| 980 | return -EINVAL; | ||
| 981 | } | ||
| 982 | |||
| 983 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { | ||
| 984 | dev_err(dev, "Image is corrupted (bad magic)\n"); | ||
| 985 | return -EINVAL; | ||
| 986 | } | ||
| 987 | |||
| 988 | if (ehdr->e_phnum == 0) { | ||
| 989 | dev_err(dev, "No loadable segments\n"); | ||
| 990 | return -EINVAL; | ||
| 991 | } | ||
| 992 | |||
| 993 | if (ehdr->e_phoff > fw->size) { | ||
| 994 | dev_err(dev, "Firmware size is too small\n"); | ||
| 995 | return -EINVAL; | ||
| 996 | } | ||
| 997 | |||
| 998 | return 0; | ||
| 999 | } | ||
| 1000 | |||
| 1001 | /** | ||
| 1002 | * rproc_get_boot_addr() - Get rproc's boot address. | ||
| 1003 | * @rproc: the remote processor handle | ||
| 1004 | * @fw: the ELF firmware image | ||
| 1005 | * | ||
| 1006 | * This function returns the entry point address of the ELF | ||
| 1007 | * image. | ||
| 1008 | * | ||
| 1009 | * Note that the boot address is not a configurable property of all remote | ||
| 1010 | * processors. Some will always boot at a specific hard-coded address. | ||
| 1011 | */ | ||
| 1012 | u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw) | ||
| 1013 | { | ||
| 1014 | struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; | ||
| 1015 | |||
| 1016 | return ehdr->e_entry; | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | /* | 772 | /* |
| 1020 | * take a firmware and boot a remote processor with it. | 773 | * take a firmware and boot a remote processor with it. |
| 1021 | */ | 774 | */ |
diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c new file mode 100644 index 000000000000..2c6fe6ad2d95 --- /dev/null +++ b/drivers/remoteproc/remoteproc_elf_loader.c | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | /* | ||
| 2 | * Remote Processor Framework Elf loader | ||
| 3 | * | ||
| 4 | * Copyright (C) 2011 Texas Instruments, Inc. | ||
| 5 | * Copyright (C) 2011 Google, Inc. | ||
| 6 | * | ||
| 7 | * Ohad Ben-Cohen <ohad@wizery.com> | ||
| 8 | * Brian Swetland <swetland@google.com> | ||
| 9 | * Mark Grosen <mgrosen@ti.com> | ||
| 10 | * Fernando Guzman Lugo <fernando.lugo@ti.com> | ||
| 11 | * Suman Anna <s-anna@ti.com> | ||
| 12 | * Robert Tivy <rtivy@ti.com> | ||
| 13 | * Armando Uribe De Leon <x0095078@ti.com> | ||
| 14 | * Sjur Brændeland <sjur.brandeland@stericsson.com> | ||
| 15 | * | ||
| 16 | * This program is free software; you can redistribute it and/or | ||
| 17 | * modify it under the terms of the GNU General Public License | ||
| 18 | * version 2 as published by the Free Software Foundation. | ||
| 19 | * | ||
| 20 | * This program is distributed in the hope that it will be useful, | ||
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 23 | * GNU General Public License for more details. | ||
| 24 | */ | ||
| 25 | |||
| 26 | #define pr_fmt(fmt) "%s: " fmt, __func__ | ||
| 27 | |||
| 28 | #include <linux/module.h> | ||
| 29 | #include <linux/firmware.h> | ||
| 30 | #include <linux/remoteproc.h> | ||
| 31 | #include <linux/elf.h> | ||
| 32 | |||
| 33 | #include "remoteproc_internal.h" | ||
| 34 | |||
| 35 | /** | ||
| 36 | * rproc_fw_sanity_check() - Sanity Check ELF firmware image | ||
| 37 | * @rproc: the remote processor handle | ||
| 38 | * @fw: the ELF firmware image | ||
| 39 | * | ||
| 40 | * Make sure this fw image is sane. | ||
| 41 | */ | ||
| 42 | int | ||
| 43 | rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) | ||
| 44 | { | ||
| 45 | const char *name = rproc->firmware; | ||
| 46 | struct device *dev = &rproc->dev; | ||
| 47 | struct elf32_hdr *ehdr; | ||
| 48 | char class; | ||
| 49 | |||
| 50 | if (!fw) { | ||
| 51 | dev_err(dev, "failed to load %s\n", name); | ||
| 52 | return -EINVAL; | ||
| 53 | } | ||
| 54 | |||
| 55 | if (fw->size < sizeof(struct elf32_hdr)) { | ||
| 56 | dev_err(dev, "Image is too small\n"); | ||
| 57 | return -EINVAL; | ||
| 58 | } | ||
| 59 | |||
| 60 | ehdr = (struct elf32_hdr *)fw->data; | ||
| 61 | |||
| 62 | /* We only support ELF32 at this point */ | ||
| 63 | class = ehdr->e_ident[EI_CLASS]; | ||
| 64 | if (class != ELFCLASS32) { | ||
| 65 | dev_err(dev, "Unsupported class: %d\n", class); | ||
| 66 | return -EINVAL; | ||
| 67 | } | ||
| 68 | |||
| 69 | /* We assume the firmware has the same endianess as the host */ | ||
| 70 | # ifdef __LITTLE_ENDIAN | ||
| 71 | if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { | ||
| 72 | # else /* BIG ENDIAN */ | ||
| 73 | if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { | ||
| 74 | # endif | ||
| 75 | dev_err(dev, "Unsupported firmware endianess\n"); | ||
| 76 | return -EINVAL; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { | ||
| 80 | dev_err(dev, "Image is too small\n"); | ||
| 81 | return -EINVAL; | ||
| 82 | } | ||
| 83 | |||
| 84 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { | ||
| 85 | dev_err(dev, "Image is corrupted (bad magic)\n"); | ||
| 86 | return -EINVAL; | ||
| 87 | } | ||
| 88 | |||
| 89 | if (ehdr->e_phnum == 0) { | ||
| 90 | dev_err(dev, "No loadable segments\n"); | ||
| 91 | return -EINVAL; | ||
| 92 | } | ||
| 93 | |||
| 94 | if (ehdr->e_phoff > fw->size) { | ||
| 95 | dev_err(dev, "Firmware size is too small\n"); | ||
| 96 | return -EINVAL; | ||
| 97 | } | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | /** | ||
| 103 | * rproc_get_boot_addr() - Get rproc's boot address. | ||
| 104 | * @rproc: the remote processor handle | ||
| 105 | * @fw: the ELF firmware image | ||
| 106 | * | ||
| 107 | * This function returns the entry point address of the ELF | ||
| 108 | * image. | ||
| 109 | * | ||
| 110 | * Note that the boot address is not a configurable property of all remote | ||
| 111 | * processors. Some will always boot at a specific hard-coded address. | ||
| 112 | */ | ||
| 113 | u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw) | ||
| 114 | { | ||
| 115 | struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; | ||
| 116 | |||
| 117 | return ehdr->e_entry; | ||
| 118 | } | ||
| 119 | |||
| 120 | /** | ||
| 121 | * rproc_load_segments() - load firmware segments to memory | ||
| 122 | * @rproc: remote processor which will be booted using these fw segments | ||
| 123 | * @fw: the ELF firmware image | ||
| 124 | * | ||
| 125 | * This function loads the firmware segments to memory, where the remote | ||
| 126 | * processor expects them. | ||
| 127 | * | ||
| 128 | * Some remote processors will expect their code and data to be placed | ||
| 129 | * in specific device addresses, and can't have them dynamically assigned. | ||
| 130 | * | ||
| 131 | * We currently support only those kind of remote processors, and expect | ||
| 132 | * the program header's paddr member to contain those addresses. We then go | ||
| 133 | * through the physically contiguous "carveout" memory regions which we | ||
| 134 | * allocated (and mapped) earlier on behalf of the remote processor, | ||
| 135 | * and "translate" device address to kernel addresses, so we can copy the | ||
| 136 | * segments where they are expected. | ||
| 137 | * | ||
| 138 | * Currently we only support remote processors that required carveout | ||
| 139 | * allocations and got them mapped onto their iommus. Some processors | ||
| 140 | * might be different: they might not have iommus, and would prefer to | ||
| 141 | * directly allocate memory for every segment/resource. This is not yet | ||
| 142 | * supported, though. | ||
| 143 | */ | ||
| 144 | int | ||
| 145 | rproc_load_segments(struct rproc *rproc, const struct firmware *fw) | ||
| 146 | { | ||
| 147 | struct device *dev = &rproc->dev; | ||
| 148 | struct elf32_hdr *ehdr; | ||
| 149 | struct elf32_phdr *phdr; | ||
| 150 | int i, ret = 0; | ||
| 151 | const u8 *elf_data = fw->data; | ||
| 152 | |||
| 153 | ehdr = (struct elf32_hdr *)elf_data; | ||
| 154 | phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); | ||
| 155 | |||
| 156 | /* go through the available ELF segments */ | ||
| 157 | for (i = 0; i < ehdr->e_phnum; i++, phdr++) { | ||
| 158 | u32 da = phdr->p_paddr; | ||
| 159 | u32 memsz = phdr->p_memsz; | ||
| 160 | u32 filesz = phdr->p_filesz; | ||
| 161 | u32 offset = phdr->p_offset; | ||
| 162 | void *ptr; | ||
| 163 | |||
| 164 | if (phdr->p_type != PT_LOAD) | ||
| 165 | continue; | ||
| 166 | |||
| 167 | dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", | ||
| 168 | phdr->p_type, da, memsz, filesz); | ||
| 169 | |||
| 170 | if (filesz > memsz) { | ||
| 171 | dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", | ||
| 172 | filesz, memsz); | ||
| 173 | ret = -EINVAL; | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | |||
| 177 | if (offset + filesz > fw->size) { | ||
| 178 | dev_err(dev, "truncated fw: need 0x%x avail 0x%x\n", | ||
| 179 | offset + filesz, fw->size); | ||
| 180 | ret = -EINVAL; | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* grab the kernel address for this device address */ | ||
| 185 | ptr = rproc_da_to_va(rproc, da, memsz); | ||
| 186 | if (!ptr) { | ||
| 187 | dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); | ||
| 188 | ret = -EINVAL; | ||
| 189 | break; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* put the segment where the remote processor expects it */ | ||
| 193 | if (phdr->p_filesz) | ||
| 194 | memcpy(ptr, elf_data + phdr->p_offset, filesz); | ||
| 195 | |||
| 196 | /* | ||
| 197 | * Zero out remaining memory for this segment. | ||
| 198 | * | ||
| 199 | * This isn't strictly required since dma_alloc_coherent already | ||
| 200 | * did this for us. albeit harmless, we may consider removing | ||
| 201 | * this. | ||
| 202 | */ | ||
| 203 | if (memsz > filesz) | ||
| 204 | memset(ptr + filesz, 0, memsz - filesz); | ||
| 205 | } | ||
| 206 | |||
| 207 | return ret; | ||
| 208 | } | ||
| 209 | |||
| 210 | /** | ||
| 211 | * rproc_find_rsc_table() - find the resource table | ||
| 212 | * @rproc: the rproc handle | ||
| 213 | * @fw: the ELF firmware image | ||
| 214 | * @tablesz: place holder for providing back the table size | ||
| 215 | * | ||
| 216 | * This function finds the resource table inside the remote processor's | ||
| 217 | * firmware. It is used both upon the registration of @rproc (in order | ||
| 218 | * to look for and register the supported virito devices), and when the | ||
| 219 | * @rproc is booted. | ||
| 220 | * | ||
| 221 | * Returns the pointer to the resource table if it is found, and write its | ||
| 222 | * size into @tablesz. If a valid table isn't found, NULL is returned | ||
| 223 | * (and @tablesz isn't set). | ||
| 224 | */ | ||
| 225 | struct resource_table * | ||
| 226 | rproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw, | ||
| 227 | int *tablesz) | ||
| 228 | { | ||
| 229 | struct elf32_hdr *ehdr; | ||
| 230 | struct elf32_shdr *shdr; | ||
| 231 | const char *name_table; | ||
| 232 | struct device *dev = &rproc->dev; | ||
| 233 | struct resource_table *table = NULL; | ||
| 234 | int i; | ||
| 235 | const u8 *elf_data = fw->data; | ||
| 236 | |||
| 237 | ehdr = (struct elf32_hdr *)elf_data; | ||
| 238 | shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); | ||
| 239 | name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; | ||
| 240 | |||
| 241 | /* look for the resource table and handle it */ | ||
| 242 | for (i = 0; i < ehdr->e_shnum; i++, shdr++) { | ||
| 243 | int size = shdr->sh_size; | ||
| 244 | int offset = shdr->sh_offset; | ||
| 245 | |||
| 246 | if (strcmp(name_table + shdr->sh_name, ".resource_table")) | ||
| 247 | continue; | ||
| 248 | |||
| 249 | table = (struct resource_table *)(elf_data + offset); | ||
| 250 | |||
| 251 | /* make sure we have the entire table */ | ||
| 252 | if (offset + size > fw->size) { | ||
| 253 | dev_err(dev, "resource table truncated\n"); | ||
| 254 | return NULL; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* make sure table has at least the header */ | ||
| 258 | if (sizeof(struct resource_table) > size) { | ||
| 259 | dev_err(dev, "header-less resource table\n"); | ||
| 260 | return NULL; | ||
| 261 | } | ||
| 262 | |||
| 263 | /* we don't support any version beyond the first */ | ||
| 264 | if (table->ver != 1) { | ||
| 265 | dev_err(dev, "unsupported fw ver: %d\n", table->ver); | ||
| 266 | return NULL; | ||
| 267 | } | ||
| 268 | |||
| 269 | /* make sure reserved bytes are zeroes */ | ||
| 270 | if (table->reserved[0] || table->reserved[1]) { | ||
| 271 | dev_err(dev, "non zero reserved bytes\n"); | ||
| 272 | return NULL; | ||
| 273 | } | ||
| 274 | |||
| 275 | /* make sure the offsets array isn't truncated */ | ||
| 276 | if (table->num * sizeof(table->offset[0]) + | ||
| 277 | sizeof(struct resource_table) > size) { | ||
| 278 | dev_err(dev, "resource table incomplete\n"); | ||
| 279 | return NULL; | ||
| 280 | } | ||
| 281 | |||
| 282 | *tablesz = shdr->sh_size; | ||
| 283 | break; | ||
| 284 | } | ||
| 285 | |||
| 286 | return table; | ||
| 287 | } | ||
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index f4957cfa0883..a44e1926e4c3 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #define REMOTEPROC_INTERNAL_H | 21 | #define REMOTEPROC_INTERNAL_H |
| 22 | 22 | ||
| 23 | #include <linux/irqreturn.h> | 23 | #include <linux/irqreturn.h> |
| 24 | #include <linux/firmware.h> | ||
| 24 | 25 | ||
| 25 | struct rproc; | 26 | struct rproc; |
| 26 | 27 | ||
| @@ -43,4 +44,14 @@ void rproc_exit_debugfs(void); | |||
| 43 | 44 | ||
| 44 | void rproc_free_vring(struct rproc_vring *rvring); | 45 | void rproc_free_vring(struct rproc_vring *rvring); |
| 45 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); | 46 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); |
| 47 | |||
| 48 | void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); | ||
| 49 | |||
| 50 | struct resource_table *rproc_find_rsc_table(struct rproc *rproc, | ||
| 51 | const struct firmware *fw, | ||
| 52 | int *tablesz); | ||
| 53 | int rproc_load_segments(struct rproc *rproc, const struct firmware *fw); | ||
| 54 | int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw); | ||
| 55 | u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw); | ||
| 56 | |||
| 46 | #endif /* REMOTEPROC_INTERNAL_H */ | 57 | #endif /* REMOTEPROC_INTERNAL_H */ |
