linux/drivers/remoteproc/remoteproc_elf_loader.c
<<
>>
Prefs
   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_elf_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 */
  42static int
  43rproc_elf_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 endianness 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 endianness\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_elf_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 */
 113static
 114u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw)
 115{
 116        struct elf32_hdr *ehdr  = (struct elf32_hdr *)fw->data;
 117
 118        return ehdr->e_entry;
 119}
 120
 121/**
 122 * rproc_elf_load_segments() - load firmware segments to memory
 123 * @rproc: remote processor which will be booted using these fw segments
 124 * @fw: the ELF firmware image
 125 *
 126 * This function loads the firmware segments to memory, where the remote
 127 * processor expects them.
 128 *
 129 * Some remote processors will expect their code and data to be placed
 130 * in specific device addresses, and can't have them dynamically assigned.
 131 *
 132 * We currently support only those kind of remote processors, and expect
 133 * the program header's paddr member to contain those addresses. We then go
 134 * through the physically contiguous "carveout" memory regions which we
 135 * allocated (and mapped) earlier on behalf of the remote processor,
 136 * and "translate" device address to kernel addresses, so we can copy the
 137 * segments where they are expected.
 138 *
 139 * Currently we only support remote processors that required carveout
 140 * allocations and got them mapped onto their iommus. Some processors
 141 * might be different: they might not have iommus, and would prefer to
 142 * directly allocate memory for every segment/resource. This is not yet
 143 * supported, though.
 144 */
 145static int
 146rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
 147{
 148        struct device *dev = &rproc->dev;
 149        struct elf32_hdr *ehdr;
 150        struct elf32_phdr *phdr;
 151        int i, ret = 0;
 152        const u8 *elf_data = fw->data;
 153
 154        ehdr = (struct elf32_hdr *)elf_data;
 155        phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
 156
 157        /* go through the available ELF segments */
 158        for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
 159                u32 da = phdr->p_paddr;
 160                u32 memsz = phdr->p_memsz;
 161                u32 filesz = phdr->p_filesz;
 162                u32 offset = phdr->p_offset;
 163                void *ptr;
 164
 165                if (phdr->p_type != PT_LOAD)
 166                        continue;
 167
 168                dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
 169                                        phdr->p_type, da, memsz, filesz);
 170
 171                if (filesz > memsz) {
 172                        dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
 173                                                        filesz, memsz);
 174                        ret = -EINVAL;
 175                        break;
 176                }
 177
 178                if (offset + filesz > fw->size) {
 179                        dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n",
 180                                        offset + filesz, fw->size);
 181                        ret = -EINVAL;
 182                        break;
 183                }
 184
 185                /* grab the kernel address for this device address */
 186                ptr = rproc_da_to_va(rproc, da, memsz);
 187                if (!ptr) {
 188                        dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
 189                        ret = -EINVAL;
 190                        break;
 191                }
 192
 193                /* put the segment where the remote processor expects it */
 194                if (phdr->p_filesz)
 195                        memcpy(ptr, elf_data + phdr->p_offset, filesz);
 196
 197                /*
 198                 * Zero out remaining memory for this segment.
 199                 *
 200                 * This isn't strictly required since dma_alloc_coherent already
 201                 * did this for us. albeit harmless, we may consider removing
 202                 * this.
 203                 */
 204                if (memsz > filesz)
 205                        memset(ptr + filesz, 0, memsz - filesz);
 206        }
 207
 208        return ret;
 209}
 210
 211static struct elf32_shdr *
 212find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
 213{
 214        struct elf32_shdr *shdr;
 215        int i;
 216        const char *name_table;
 217        struct resource_table *table = NULL;
 218        const u8 *elf_data = (void *)ehdr;
 219
 220        /* look for the resource table and handle it */
 221        shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
 222        name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
 223
 224        for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
 225                u32 size = shdr->sh_size;
 226                u32 offset = shdr->sh_offset;
 227
 228                if (strcmp(name_table + shdr->sh_name, ".resource_table"))
 229                        continue;
 230
 231                table = (struct resource_table *)(elf_data + offset);
 232
 233                /* make sure we have the entire table */
 234                if (offset + size > fw_size || offset + size < size) {
 235                        dev_err(dev, "resource table truncated\n");
 236                        return NULL;
 237                }
 238
 239                /* make sure table has at least the header */
 240                if (sizeof(struct resource_table) > size) {
 241                        dev_err(dev, "header-less resource table\n");
 242                        return NULL;
 243                }
 244
 245                /* we don't support any version beyond the first */
 246                if (table->ver != 1) {
 247                        dev_err(dev, "unsupported fw ver: %d\n", table->ver);
 248                        return NULL;
 249                }
 250
 251                /* make sure reserved bytes are zeroes */
 252                if (table->reserved[0] || table->reserved[1]) {
 253                        dev_err(dev, "non zero reserved bytes\n");
 254                        return NULL;
 255                }
 256
 257                /* make sure the offsets array isn't truncated */
 258                if (table->num * sizeof(table->offset[0]) +
 259                                sizeof(struct resource_table) > size) {
 260                        dev_err(dev, "resource table incomplete\n");
 261                        return NULL;
 262                }
 263
 264                return shdr;
 265        }
 266
 267        return NULL;
 268}
 269
 270/**
 271 * rproc_elf_find_rsc_table() - find the resource table
 272 * @rproc: the rproc handle
 273 * @fw: the ELF firmware image
 274 * @tablesz: place holder for providing back the table size
 275 *
 276 * This function finds the resource table inside the remote processor's
 277 * firmware. It is used both upon the registration of @rproc (in order
 278 * to look for and register the supported virito devices), and when the
 279 * @rproc is booted.
 280 *
 281 * Returns the pointer to the resource table if it is found, and write its
 282 * size into @tablesz. If a valid table isn't found, NULL is returned
 283 * (and @tablesz isn't set).
 284 */
 285static struct resource_table *
 286rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
 287                         int *tablesz)
 288{
 289        struct elf32_hdr *ehdr;
 290        struct elf32_shdr *shdr;
 291        struct device *dev = &rproc->dev;
 292        struct resource_table *table = NULL;
 293        const u8 *elf_data = fw->data;
 294
 295        ehdr = (struct elf32_hdr *)elf_data;
 296
 297        shdr = find_table(dev, ehdr, fw->size);
 298        if (!shdr)
 299                return NULL;
 300
 301        table = (struct resource_table *)(elf_data + shdr->sh_offset);
 302        *tablesz = shdr->sh_size;
 303
 304        return table;
 305}
 306
 307/**
 308 * rproc_elf_find_loaded_rsc_table() - find the loaded resource table
 309 * @rproc: the rproc handle
 310 * @fw: the ELF firmware image
 311 *
 312 * This function finds the location of the loaded resource table. Don't
 313 * call this function if the table wasn't loaded yet - it's a bug if you do.
 314 *
 315 * Returns the pointer to the resource table if it is found or NULL otherwise.
 316 * If the table wasn't loaded yet the result is unspecified.
 317 */
 318static struct resource_table *
 319rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
 320{
 321        struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
 322        struct elf32_shdr *shdr;
 323
 324        shdr = find_table(&rproc->dev, ehdr, fw->size);
 325        if (!shdr)
 326                return NULL;
 327
 328        return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
 329}
 330
 331const struct rproc_fw_ops rproc_elf_fw_ops = {
 332        .load = rproc_elf_load_segments,
 333        .find_rsc_table = rproc_elf_find_rsc_table,
 334        .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
 335        .sanity_check = rproc_elf_sanity_check,
 336        .get_boot_addr = rproc_elf_get_boot_addr
 337};
 338
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.