uboot/drivers/remoteproc/rproc-elf-loader.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
   4 */
   5#include <common.h>
   6#include <cpu_func.h>
   7#include <dm.h>
   8#include <elf.h>
   9#include <log.h>
  10#include <remoteproc.h>
  11#include <asm/cache.h>
  12#include <dm/device_compat.h>
  13#include <linux/compat.h>
  14
  15/**
  16 * struct resource_table - firmware resource table header
  17 * @ver: version number
  18 * @num: number of resource entries
  19 * @reserved: reserved (must be zero)
  20 * @offset: array of offsets pointing at the various resource entries
  21 *
  22 * A resource table is essentially a list of system resources required
  23 * by the remote processor. It may also include configuration entries.
  24 * If needed, the remote processor firmware should contain this table
  25 * as a dedicated ".resource_table" ELF section.
  26 *
  27 * Some resources entries are mere announcements, where the host is informed
  28 * of specific remoteproc configuration. Other entries require the host to
  29 * do something (e.g. allocate a system resource). Sometimes a negotiation
  30 * is expected, where the firmware requests a resource, and once allocated,
  31 * the host should provide back its details (e.g. address of an allocated
  32 * memory region).
  33 *
  34 * The header of the resource table, as expressed by this structure,
  35 * contains a version number (should we need to change this format in the
  36 * future), the number of available resource entries, and their offsets
  37 * in the table.
  38 *
  39 * Immediately following this header are the resource entries themselves.
  40 */
  41struct resource_table {
  42        u32 ver;
  43        u32 num;
  44        u32 reserved[2];
  45        u32 offset[0];
  46} __packed;
  47
  48/* Basic function to verify ELF32 image format */
  49int rproc_elf32_sanity_check(ulong addr, ulong size)
  50{
  51        Elf32_Ehdr *ehdr;
  52        char class;
  53
  54        if (!addr) {
  55                pr_debug("Invalid fw address?\n");
  56                return -EFAULT;
  57        }
  58
  59        if (size < sizeof(Elf32_Ehdr)) {
  60                pr_debug("Image is too small\n");
  61                return -ENOSPC;
  62        }
  63
  64        ehdr = (Elf32_Ehdr *)addr;
  65        class = ehdr->e_ident[EI_CLASS];
  66
  67        if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS32) {
  68                pr_debug("Not an executable ELF32 image\n");
  69                return -EPROTONOSUPPORT;
  70        }
  71
  72        /* We assume the firmware has the same endianness as the host */
  73# ifdef __LITTLE_ENDIAN
  74        if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
  75# else /* BIG ENDIAN */
  76        if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
  77# endif
  78                pr_debug("Unsupported firmware endianness\n");
  79                return -EILSEQ;
  80        }
  81
  82        if (size < ehdr->e_shoff + sizeof(Elf32_Shdr)) {
  83                pr_debug("Image is too small\n");
  84                return -ENOSPC;
  85        }
  86
  87        if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
  88                pr_debug("Image is corrupted (bad magic)\n");
  89                return -EBADF;
  90        }
  91
  92        if (ehdr->e_phnum == 0) {
  93                pr_debug("No loadable segments\n");
  94                return -ENOEXEC;
  95        }
  96
  97        if (ehdr->e_phoff > size) {
  98                pr_debug("Firmware size is too small\n");
  99                return -ENOSPC;
 100        }
 101
 102        return 0;
 103}
 104
 105/* Basic function to verify ELF64 image format */
 106int rproc_elf64_sanity_check(ulong addr, ulong size)
 107{
 108        Elf64_Ehdr *ehdr = (Elf64_Ehdr *)addr;
 109        char class;
 110
 111        if (!addr) {
 112                pr_debug("Invalid fw address?\n");
 113                return -EFAULT;
 114        }
 115
 116        if (size < sizeof(Elf64_Ehdr)) {
 117                pr_debug("Image is too small\n");
 118                return -ENOSPC;
 119        }
 120
 121        class = ehdr->e_ident[EI_CLASS];
 122
 123        if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS64) {
 124                pr_debug("Not an executable ELF64 image\n");
 125                return -EPROTONOSUPPORT;
 126        }
 127
 128        /* We assume the firmware has the same endianness as the host */
 129# ifdef __LITTLE_ENDIAN
 130        if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
 131# else /* BIG ENDIAN */
 132        if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
 133# endif
 134                pr_debug("Unsupported firmware endianness\n");
 135                return -EILSEQ;
 136        }
 137
 138        if (size < ehdr->e_shoff + sizeof(Elf64_Shdr)) {
 139                pr_debug("Image is too small\n");
 140                return -ENOSPC;
 141        }
 142
 143        if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
 144                pr_debug("Image is corrupted (bad magic)\n");
 145                return -EBADF;
 146        }
 147
 148        if (ehdr->e_phnum == 0) {
 149                pr_debug("No loadable segments\n");
 150                return -ENOEXEC;
 151        }
 152
 153        if (ehdr->e_phoff > size) {
 154                pr_debug("Firmware size is too small\n");
 155                return -ENOSPC;
 156        }
 157
 158        return 0;
 159}
 160
 161int rproc_elf32_load_image(struct udevice *dev, unsigned long addr, ulong size)
 162{
 163        Elf32_Ehdr *ehdr; /* Elf header structure pointer */
 164        Elf32_Phdr *phdr; /* Program header structure pointer */
 165        const struct dm_rproc_ops *ops;
 166        unsigned int i, ret;
 167
 168        ret =  rproc_elf32_sanity_check(addr, size);
 169        if (ret) {
 170                dev_err(dev, "Invalid ELF32 Image %d\n", ret);
 171                return ret;
 172        }
 173
 174        ehdr = (Elf32_Ehdr *)addr;
 175        phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
 176
 177        ops = rproc_get_ops(dev);
 178
 179        /* Load each program header */
 180        for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
 181                void *dst = (void *)(uintptr_t)phdr->p_paddr;
 182                void *src = (void *)addr + phdr->p_offset;
 183
 184                if (phdr->p_type != PT_LOAD)
 185                        continue;
 186
 187                if (ops->device_to_virt)
 188                        dst = ops->device_to_virt(dev, (ulong)dst,
 189                                                  phdr->p_memsz);
 190
 191                dev_dbg(dev, "Loading phdr %i to 0x%p (%i bytes)\n",
 192                        i, dst, phdr->p_filesz);
 193                if (phdr->p_filesz)
 194                        memcpy(dst, src, phdr->p_filesz);
 195                if (phdr->p_filesz != phdr->p_memsz)
 196                        memset(dst + phdr->p_filesz, 0x00,
 197                               phdr->p_memsz - phdr->p_filesz);
 198                flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
 199                            roundup((unsigned long)dst + phdr->p_filesz,
 200                                    ARCH_DMA_MINALIGN) -
 201                            rounddown((unsigned long)dst, ARCH_DMA_MINALIGN));
 202        }
 203
 204        return 0;
 205}
 206
 207int rproc_elf64_load_image(struct udevice *dev, ulong addr, ulong size)
 208{
 209        const struct dm_rproc_ops *ops = rproc_get_ops(dev);
 210        u64 da, memsz, filesz, offset;
 211        Elf64_Ehdr *ehdr;
 212        Elf64_Phdr *phdr;
 213        int i, ret = 0;
 214        void *ptr;
 215
 216        dev_dbg(dev, "%s: addr = 0x%lx size = 0x%lx\n", __func__, addr, size);
 217
 218        if (rproc_elf64_sanity_check(addr, size))
 219                return -EINVAL;
 220
 221        ehdr = (Elf64_Ehdr *)addr;
 222        phdr = (Elf64_Phdr *)(addr + (ulong)ehdr->e_phoff);
 223
 224        /* go through the available ELF segments */
 225        for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
 226                da = phdr->p_paddr;
 227                memsz = phdr->p_memsz;
 228                filesz = phdr->p_filesz;
 229                offset = phdr->p_offset;
 230
 231                if (phdr->p_type != PT_LOAD)
 232                        continue;
 233
 234                dev_dbg(dev, "%s:phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n",
 235                        __func__, phdr->p_type, da, memsz, filesz);
 236
 237                ptr = (void *)(uintptr_t)da;
 238                if (ops->device_to_virt) {
 239                        ptr = ops->device_to_virt(dev, da, phdr->p_memsz);
 240                        if (!ptr) {
 241                                dev_err(dev, "bad da 0x%llx mem 0x%llx\n", da,
 242                                        memsz);
 243                                ret = -EINVAL;
 244                                break;
 245                        }
 246                }
 247
 248                if (filesz)
 249                        memcpy(ptr, (void *)addr + offset, filesz);
 250                if (filesz != memsz)
 251                        memset(ptr + filesz, 0x00, memsz - filesz);
 252
 253                flush_cache(rounddown((ulong)ptr, ARCH_DMA_MINALIGN),
 254                            roundup((ulong)ptr + filesz, ARCH_DMA_MINALIGN) -
 255                            rounddown((ulong)ptr, ARCH_DMA_MINALIGN));
 256        }
 257
 258        return ret;
 259}
 260
 261int rproc_elf_load_image(struct udevice *dev, ulong addr, ulong size)
 262{
 263        Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr;
 264
 265        if (!addr) {
 266                dev_err(dev, "Invalid firmware address\n");
 267                return -EFAULT;
 268        }
 269
 270        if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
 271                return rproc_elf64_load_image(dev, addr, size);
 272        else
 273                return rproc_elf32_load_image(dev, addr, size);
 274}
 275
 276static ulong rproc_elf32_get_boot_addr(ulong addr)
 277{
 278        Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr;
 279
 280        return ehdr->e_entry;
 281}
 282
 283static ulong rproc_elf64_get_boot_addr(ulong addr)
 284{
 285        Elf64_Ehdr *ehdr = (Elf64_Ehdr *)addr;
 286
 287        return ehdr->e_entry;
 288}
 289
 290ulong rproc_elf_get_boot_addr(struct udevice *dev, ulong addr)
 291{
 292        Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr;
 293
 294        if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
 295                return rproc_elf64_get_boot_addr(addr);
 296        else
 297                return rproc_elf32_get_boot_addr(addr);
 298}
 299
 300/*
 301 * Search for the resource table in an ELF32 image.
 302 * Returns the address of the resource table section if found, NULL if there is
 303 * no resource table section, or error pointer.
 304 */
 305static Elf32_Shdr *rproc_elf32_find_rsc_table(struct udevice *dev,
 306                                              ulong fw_addr, ulong fw_size)
 307{
 308        int ret;
 309        unsigned int i;
 310        const char *name_table;
 311        struct resource_table *table;
 312        const u8 *elf_data = (void *)fw_addr;
 313        Elf32_Ehdr *ehdr = (Elf32_Ehdr *)fw_addr;
 314        Elf32_Shdr *shdr;
 315
 316        ret = rproc_elf32_sanity_check(fw_addr, fw_size);
 317        if (ret) {
 318                pr_debug("Invalid ELF32 Image %d\n", ret);
 319                return ERR_PTR(ret);
 320        }
 321
 322        /* look for the resource table and handle it */
 323        shdr = (Elf32_Shdr *)(elf_data + ehdr->e_shoff);
 324        name_table = (const char *)(elf_data +
 325                                    shdr[ehdr->e_shstrndx].sh_offset);
 326
 327        for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
 328                u32 size = shdr->sh_size;
 329                u32 offset = shdr->sh_offset;
 330
 331                if (strcmp(name_table + shdr->sh_name, ".resource_table"))
 332                        continue;
 333
 334                table = (struct resource_table *)(elf_data + offset);
 335
 336                /* make sure we have the entire table */
 337                if (offset + size > fw_size) {
 338                        pr_debug("resource table truncated\n");
 339                        return ERR_PTR(-ENOSPC);
 340                }
 341
 342                /* make sure table has at least the header */
 343                if (sizeof(*table) > size) {
 344                        pr_debug("header-less resource table\n");
 345                        return ERR_PTR(-ENOSPC);
 346                }
 347
 348                /* we don't support any version beyond the first */
 349                if (table->ver != 1) {
 350                        pr_debug("unsupported fw ver: %d\n", table->ver);
 351                        return ERR_PTR(-EPROTONOSUPPORT);
 352                }
 353
 354                /* make sure reserved bytes are zeroes */
 355                if (table->reserved[0] || table->reserved[1]) {
 356                        pr_debug("non zero reserved bytes\n");
 357                        return ERR_PTR(-EBADF);
 358                }
 359
 360                /* make sure the offsets array isn't truncated */
 361                if (table->num * sizeof(table->offset[0]) +
 362                                 sizeof(*table) > size) {
 363                        pr_debug("resource table incomplete\n");
 364                        return ERR_PTR(-ENOSPC);
 365                }
 366
 367                return shdr;
 368        }
 369
 370        return NULL;
 371}
 372
 373/* Load the resource table from an ELF32 image */
 374int rproc_elf32_load_rsc_table(struct udevice *dev, ulong fw_addr,
 375                               ulong fw_size, ulong *rsc_addr, ulong *rsc_size)
 376{
 377        const struct dm_rproc_ops *ops;
 378        Elf32_Shdr *shdr;
 379        void *src, *dst;
 380
 381        shdr = rproc_elf32_find_rsc_table(dev, fw_addr, fw_size);
 382        if (!shdr)
 383                return -ENODATA;
 384        if (IS_ERR(shdr))
 385                return PTR_ERR(shdr);
 386
 387        ops = rproc_get_ops(dev);
 388        *rsc_addr = (ulong)shdr->sh_addr;
 389        *rsc_size = (ulong)shdr->sh_size;
 390
 391        src = (void *)fw_addr + shdr->sh_offset;
 392        if (ops->device_to_virt)
 393                dst = (void *)ops->device_to_virt(dev, *rsc_addr, *rsc_size);
 394        else
 395                dst = (void *)rsc_addr;
 396
 397        dev_dbg(dev, "Loading resource table to 0x%8lx (%ld bytes)\n",
 398                (ulong)dst, *rsc_size);
 399
 400        memcpy(dst, src, *rsc_size);
 401        flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
 402                    roundup((unsigned long)dst + *rsc_size,
 403                            ARCH_DMA_MINALIGN) -
 404                    rounddown((unsigned long)dst, ARCH_DMA_MINALIGN));
 405
 406        return 0;
 407}
 408
 409/*
 410 * Search for the resource table in an ELF64 image.
 411 * Returns the address of the resource table section if found, NULL if there is
 412 * no resource table section, or error pointer.
 413 */
 414static Elf64_Shdr *rproc_elf64_find_rsc_table(struct udevice *dev,
 415                                              ulong fw_addr, ulong fw_size)
 416{
 417        int ret;
 418        unsigned int i;
 419        const char *name_table;
 420        struct resource_table *table;
 421        const u8 *elf_data = (void *)fw_addr;
 422        Elf64_Ehdr *ehdr = (Elf64_Ehdr *)fw_addr;
 423        Elf64_Shdr *shdr;
 424
 425        ret = rproc_elf64_sanity_check(fw_addr, fw_size);
 426        if (ret) {
 427                pr_debug("Invalid ELF64 Image %d\n", ret);
 428                return ERR_PTR(ret);
 429        }
 430
 431        /* look for the resource table and handle it */
 432        shdr = (Elf64_Shdr *)(elf_data + ehdr->e_shoff);
 433        name_table = (const char *)(elf_data +
 434                                    shdr[ehdr->e_shstrndx].sh_offset);
 435
 436        for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
 437                u64 size = shdr->sh_size;
 438                u64 offset = shdr->sh_offset;
 439
 440                if (strcmp(name_table + shdr->sh_name, ".resource_table"))
 441                        continue;
 442
 443                table = (struct resource_table *)(elf_data + offset);
 444
 445                /* make sure we have the entire table */
 446                if (offset + size > fw_size) {
 447                        pr_debug("resource table truncated\n");
 448                        return ERR_PTR(-ENOSPC);
 449                }
 450
 451                /* make sure table has at least the header */
 452                if (sizeof(*table) > size) {
 453                        pr_debug("header-less resource table\n");
 454                        return ERR_PTR(-ENOSPC);
 455                }
 456
 457                /* we don't support any version beyond the first */
 458                if (table->ver != 1) {
 459                        pr_debug("unsupported fw ver: %d\n", table->ver);
 460                        return ERR_PTR(-EPROTONOSUPPORT);
 461                }
 462
 463                /* make sure reserved bytes are zeroes */
 464                if (table->reserved[0] || table->reserved[1]) {
 465                        pr_debug("non zero reserved bytes\n");
 466                        return ERR_PTR(-EBADF);
 467                }
 468
 469                /* make sure the offsets array isn't truncated */
 470                if (table->num * sizeof(table->offset[0]) +
 471                                 sizeof(*table) > size) {
 472                        pr_debug("resource table incomplete\n");
 473                        return ERR_PTR(-ENOSPC);
 474                }
 475
 476                return shdr;
 477        }
 478
 479        return NULL;
 480}
 481
 482/* Load the resource table from an ELF64 image */
 483int rproc_elf64_load_rsc_table(struct udevice *dev, ulong fw_addr,
 484                               ulong fw_size, ulong *rsc_addr, ulong *rsc_size)
 485{
 486        const struct dm_rproc_ops *ops;
 487        Elf64_Shdr *shdr;
 488        void *src, *dst;
 489
 490        shdr = rproc_elf64_find_rsc_table(dev, fw_addr, fw_size);
 491        if (!shdr)
 492                return -ENODATA;
 493        if (IS_ERR(shdr))
 494                return PTR_ERR(shdr);
 495
 496        ops = rproc_get_ops(dev);
 497        *rsc_addr = (ulong)shdr->sh_addr;
 498        *rsc_size = (ulong)shdr->sh_size;
 499
 500        src = (void *)fw_addr + shdr->sh_offset;
 501        if (ops->device_to_virt)
 502                dst = (void *)ops->device_to_virt(dev, *rsc_addr, *rsc_size);
 503        else
 504                dst = (void *)rsc_addr;
 505
 506        dev_dbg(dev, "Loading resource table to 0x%8lx (%ld bytes)\n",
 507                (ulong)dst, *rsc_size);
 508
 509        memcpy(dst, src, *rsc_size);
 510        flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN),
 511                    roundup((unsigned long)dst + *rsc_size,
 512                            ARCH_DMA_MINALIGN) -
 513                    rounddown((unsigned long)dst, ARCH_DMA_MINALIGN));
 514
 515        return 0;
 516}
 517
 518/* Load the resource table from an ELF32 or ELF64 image */
 519int rproc_elf_load_rsc_table(struct udevice *dev, ulong fw_addr,
 520                             ulong fw_size, ulong *rsc_addr, ulong *rsc_size)
 521
 522{
 523        Elf32_Ehdr *ehdr = (Elf32_Ehdr *)fw_addr;
 524
 525        if (!fw_addr)
 526                return -EFAULT;
 527
 528        if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
 529                return rproc_elf64_load_rsc_table(dev, fw_addr, fw_size,
 530                                                  rsc_addr, rsc_size);
 531        else
 532                return rproc_elf32_load_rsc_table(dev, fw_addr, fw_size,
 533                                                  rsc_addr, rsc_size);
 534}
 535