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
 211/**
 212 * rproc_elf_find_rsc_table() - find the resource table
 213 * @rproc: the rproc handle
 214 * @fw: the ELF firmware image
 215 * @tablesz: place holder for providing back the table size
 216 *
 217 * This function finds the resource table inside the remote processor's
 218 * firmware. It is used both upon the registration of @rproc (in order
 219 * to look for and register the supported virito devices), and when the
 220 * @rproc is booted.
 221 *
 222 * Returns the pointer to the resource table if it is found, and write its
 223 * size into @tablesz. If a valid table isn't found, NULL is returned
 224 * (and @tablesz isn't set).
 225 */
 226static struct resource_table *
 227rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
 228                                                        int *tablesz)
 229{
 230        struct elf32_hdr *ehdr;
 231        struct elf32_shdr *shdr;
 232        const char *name_table;
 233        struct device *dev = &rproc->dev;
 234        struct resource_table *table = NULL;
 235        int i;
 236        const u8 *elf_data = fw->data;
 237
 238        ehdr = (struct elf32_hdr *)elf_data;
 239        shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
 240        name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
 241
 242        /* look for the resource table and handle it */
 243        for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
 244                int size = shdr->sh_size;
 245                int offset = shdr->sh_offset;
 246
 247                if (strcmp(name_table + shdr->sh_name, ".resource_table"))
 248                        continue;
 249
 250                table = (struct resource_table *)(elf_data + offset);
 251
 252                /* make sure we have the entire table */
 253                if (offset + size > fw->size) {
 254                        dev_err(dev, "resource table truncated\n");
 255                        return NULL;
 256                }
 257
 258                /* make sure table has at least the header */
 259                if (sizeof(struct resource_table) > size) {
 260                        dev_err(dev, "header-less resource table\n");
 261                        return NULL;
 262                }
 263
 264                /* we don't support any version beyond the first */
 265                if (table->ver != 1) {
 266                        dev_err(dev, "unsupported fw ver: %d\n", table->ver);
 267                        return NULL;
 268                }
 269
 270                /* make sure reserved bytes are zeroes */
 271                if (table->reserved[0] || table->reserved[1]) {
 272                        dev_err(dev, "non zero reserved bytes\n");
 273                        return NULL;
 274                }
 275
 276                /* make sure the offsets array isn't truncated */
 277                if (table->num * sizeof(table->offset[0]) +
 278                                sizeof(struct resource_table) > size) {
 279                        dev_err(dev, "resource table incomplete\n");
 280                        return NULL;
 281                }
 282
 283                *tablesz = shdr->sh_size;
 284                break;
 285        }
 286
 287        return table;
 288}
 289
 290const struct rproc_fw_ops rproc_elf_fw_ops = {
 291        .load = rproc_elf_load_segments,
 292        .find_rsc_table = rproc_elf_find_rsc_table,
 293        .sanity_check = rproc_elf_sanity_check,
 294        .get_boot_addr = rproc_elf_get_boot_addr
 295};
 296