linux/drivers/remoteproc/remoteproc_elf_loader.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Remote Processor Framework Elf loader
   4 *
   5 * Copyright (C) 2011 Texas Instruments, Inc.
   6 * Copyright (C) 2011 Google, Inc.
   7 *
   8 * Ohad Ben-Cohen <ohad@wizery.com>
   9 * Brian Swetland <swetland@google.com>
  10 * Mark Grosen <mgrosen@ti.com>
  11 * Fernando Guzman Lugo <fernando.lugo@ti.com>
  12 * Suman Anna <s-anna@ti.com>
  13 * Robert Tivy <rtivy@ti.com>
  14 * Armando Uribe De Leon <x0095078@ti.com>
  15 * Sjur Brændeland <sjur.brandeland@stericsson.com>
  16 */
  17
  18#define pr_fmt(fmt)    "%s: " fmt, __func__
  19
  20#include <linux/module.h>
  21#include <linux/firmware.h>
  22#include <linux/remoteproc.h>
  23#include <linux/elf.h>
  24
  25#include "remoteproc_internal.h"
  26
  27/**
  28 * rproc_elf_sanity_check() - Sanity Check ELF firmware image
  29 * @rproc: the remote processor handle
  30 * @fw: the ELF firmware image
  31 *
  32 * Make sure this fw image is sane.
  33 */
  34int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw)
  35{
  36        const char *name = rproc->firmware;
  37        struct device *dev = &rproc->dev;
  38        struct elf32_hdr *ehdr;
  39        char class;
  40
  41        if (!fw) {
  42                dev_err(dev, "failed to load %s\n", name);
  43                return -EINVAL;
  44        }
  45
  46        if (fw->size < sizeof(struct elf32_hdr)) {
  47                dev_err(dev, "Image is too small\n");
  48                return -EINVAL;
  49        }
  50
  51        ehdr = (struct elf32_hdr *)fw->data;
  52
  53        /* We only support ELF32 at this point */
  54        class = ehdr->e_ident[EI_CLASS];
  55        if (class != ELFCLASS32) {
  56                dev_err(dev, "Unsupported class: %d\n", class);
  57                return -EINVAL;
  58        }
  59
  60        /* We assume the firmware has the same endianness as the host */
  61# ifdef __LITTLE_ENDIAN
  62        if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
  63# else /* BIG ENDIAN */
  64        if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
  65# endif
  66                dev_err(dev, "Unsupported firmware endianness\n");
  67                return -EINVAL;
  68        }
  69
  70        if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
  71                dev_err(dev, "Image is too small\n");
  72                return -EINVAL;
  73        }
  74
  75        if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
  76                dev_err(dev, "Image is corrupted (bad magic)\n");
  77                return -EINVAL;
  78        }
  79
  80        if (ehdr->e_phnum == 0) {
  81                dev_err(dev, "No loadable segments\n");
  82                return -EINVAL;
  83        }
  84
  85        if (ehdr->e_phoff > fw->size) {
  86                dev_err(dev, "Firmware size is too small\n");
  87                return -EINVAL;
  88        }
  89
  90        return 0;
  91}
  92EXPORT_SYMBOL(rproc_elf_sanity_check);
  93
  94/**
  95 * rproc_elf_get_boot_addr() - Get rproc's boot address.
  96 * @rproc: the remote processor handle
  97 * @fw: the ELF firmware image
  98 *
  99 * This function returns the entry point address of the ELF
 100 * image.
 101 *
 102 * Note that the boot address is not a configurable property of all remote
 103 * processors. Some will always boot at a specific hard-coded address.
 104 */
 105u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
 106{
 107        struct elf32_hdr *ehdr  = (struct elf32_hdr *)fw->data;
 108
 109        return ehdr->e_entry;
 110}
 111EXPORT_SYMBOL(rproc_elf_get_boot_addr);
 112
 113/**
 114 * rproc_elf_load_segments() - load firmware segments to memory
 115 * @rproc: remote processor which will be booted using these fw segments
 116 * @fw: the ELF firmware image
 117 *
 118 * This function loads the firmware segments to memory, where the remote
 119 * processor expects them.
 120 *
 121 * Some remote processors will expect their code and data to be placed
 122 * in specific device addresses, and can't have them dynamically assigned.
 123 *
 124 * We currently support only those kind of remote processors, and expect
 125 * the program header's paddr member to contain those addresses. We then go
 126 * through the physically contiguous "carveout" memory regions which we
 127 * allocated (and mapped) earlier on behalf of the remote processor,
 128 * and "translate" device address to kernel addresses, so we can copy the
 129 * segments where they are expected.
 130 *
 131 * Currently we only support remote processors that required carveout
 132 * allocations and got them mapped onto their iommus. Some processors
 133 * might be different: they might not have iommus, and would prefer to
 134 * directly allocate memory for every segment/resource. This is not yet
 135 * supported, though.
 136 */
 137int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
 138{
 139        struct device *dev = &rproc->dev;
 140        struct elf32_hdr *ehdr;
 141        struct elf32_phdr *phdr;
 142        int i, ret = 0;
 143        const u8 *elf_data = fw->data;
 144
 145        ehdr = (struct elf32_hdr *)elf_data;
 146        phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
 147
 148        /* go through the available ELF segments */
 149        for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
 150                u32 da = phdr->p_paddr;
 151                u32 memsz = phdr->p_memsz;
 152                u32 filesz = phdr->p_filesz;
 153                u32 offset = phdr->p_offset;
 154                void *ptr;
 155
 156                if (phdr->p_type != PT_LOAD)
 157                        continue;
 158
 159                dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
 160                        phdr->p_type, da, memsz, filesz);
 161
 162                if (filesz > memsz) {
 163                        dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
 164                                filesz, memsz);
 165                        ret = -EINVAL;
 166                        break;
 167                }
 168
 169                if (offset + filesz > fw->size) {
 170                        dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
 171                                offset + filesz, fw->size);
 172                        ret = -EINVAL;
 173                        break;
 174                }
 175
 176                /* grab the kernel address for this device address */
 177                ptr = rproc_da_to_va(rproc, da, memsz);
 178                if (!ptr) {
 179                        dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
 180                        ret = -EINVAL;
 181                        break;
 182                }
 183
 184                /* put the segment where the remote processor expects it */
 185                if (phdr->p_filesz)
 186                        memcpy(ptr, elf_data + phdr->p_offset, filesz);
 187
 188                /*
 189                 * Zero out remaining memory for this segment.
 190                 *
 191                 * This isn't strictly required since dma_alloc_coherent already
 192                 * did this for us. albeit harmless, we may consider removing
 193                 * this.
 194                 */
 195                if (memsz > filesz)
 196                        memset(ptr + filesz, 0, memsz - filesz);
 197        }
 198
 199        return ret;
 200}
 201EXPORT_SYMBOL(rproc_elf_load_segments);
 202
 203static struct elf32_shdr *
 204find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
 205{
 206        struct elf32_shdr *shdr;
 207        int i;
 208        const char *name_table;
 209        struct resource_table *table = NULL;
 210        const u8 *elf_data = (void *)ehdr;
 211
 212        /* look for the resource table and handle it */
 213        shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
 214        name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
 215
 216        for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
 217                u32 size = shdr->sh_size;
 218                u32 offset = shdr->sh_offset;
 219
 220                if (strcmp(name_table + shdr->sh_name, ".resource_table"))
 221                        continue;
 222
 223                table = (struct resource_table *)(elf_data + offset);
 224
 225                /* make sure we have the entire table */
 226                if (offset + size > fw_size || offset + size < size) {
 227                        dev_err(dev, "resource table truncated\n");
 228                        return NULL;
 229                }
 230
 231                /* make sure table has at least the header */
 232                if (sizeof(struct resource_table) > size) {
 233                        dev_err(dev, "header-less resource table\n");
 234                        return NULL;
 235                }
 236
 237                /* we don't support any version beyond the first */
 238                if (table->ver != 1) {
 239                        dev_err(dev, "unsupported fw ver: %d\n", table->ver);
 240                        return NULL;
 241                }
 242
 243                /* make sure reserved bytes are zeroes */
 244                if (table->reserved[0] || table->reserved[1]) {
 245                        dev_err(dev, "non zero reserved bytes\n");
 246                        return NULL;
 247                }
 248
 249                /* make sure the offsets array isn't truncated */
 250                if (struct_size(table, offset, table->num) > size) {
 251                        dev_err(dev, "resource table incomplete\n");
 252                        return NULL;
 253                }
 254
 255                return shdr;
 256        }
 257
 258        return NULL;
 259}
 260
 261/**
 262 * rproc_elf_load_rsc_table() - load the resource table
 263 * @rproc: the rproc handle
 264 * @fw: the ELF firmware image
 265 *
 266 * This function finds the resource table inside the remote processor's
 267 * firmware, load it into the @cached_table and update @table_ptr.
 268 *
 269 * Return: 0 on success, negative errno on failure.
 270 */
 271int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw)
 272{
 273        struct elf32_hdr *ehdr;
 274        struct elf32_shdr *shdr;
 275        struct device *dev = &rproc->dev;
 276        struct resource_table *table = NULL;
 277        const u8 *elf_data = fw->data;
 278        size_t tablesz;
 279
 280        ehdr = (struct elf32_hdr *)elf_data;
 281
 282        shdr = find_table(dev, ehdr, fw->size);
 283        if (!shdr)
 284                return -EINVAL;
 285
 286        table = (struct resource_table *)(elf_data + shdr->sh_offset);
 287        tablesz = shdr->sh_size;
 288
 289        /*
 290         * Create a copy of the resource table. When a virtio device starts
 291         * and calls vring_new_virtqueue() the address of the allocated vring
 292         * will be stored in the cached_table. Before the device is started,
 293         * cached_table will be copied into device memory.
 294         */
 295        rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL);
 296        if (!rproc->cached_table)
 297                return -ENOMEM;
 298
 299        rproc->table_ptr = rproc->cached_table;
 300        rproc->table_sz = tablesz;
 301
 302        return 0;
 303}
 304EXPORT_SYMBOL(rproc_elf_load_rsc_table);
 305
 306/**
 307 * rproc_elf_find_loaded_rsc_table() - find the loaded resource table
 308 * @rproc: the rproc handle
 309 * @fw: the ELF firmware image
 310 *
 311 * This function finds the location of the loaded resource table. Don't
 312 * call this function if the table wasn't loaded yet - it's a bug if you do.
 313 *
 314 * Returns the pointer to the resource table if it is found or NULL otherwise.
 315 * If the table wasn't loaded yet the result is unspecified.
 316 */
 317struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc,
 318                                                       const struct firmware *fw)
 319{
 320        struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
 321        struct elf32_shdr *shdr;
 322
 323        shdr = find_table(&rproc->dev, ehdr, fw->size);
 324        if (!shdr)
 325                return NULL;
 326
 327        return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
 328}
 329EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table);
 330