From: Linus Torvalds Date: Thu, 26 Jul 2012 23:19:08 +0000 (-0700) Subject: Merge tag 'remoteproc-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad... X-Git-Url: https://git.stricted.de/?p=GitHub%2Fmt8127%2Fandroid_kernel_alcatel_ttab.git;a=commitdiff_plain;h=a9197f903f72a81393932d452379c8847fade544 Merge tag 'remoteproc-for-3.6' of git://git./linux/kernel/git/ohad/remoteproc Pull remoteproc update from Ohad Ben-Cohen: - custom binary format support from Sjur Brændeland - groundwork for recovery and runtime pm support - some cleanups and API simplifications Fix up conflicts in drivers/remoteproc/remoteproc_core.c due to clashes with earlier cleanups by Sjur Brændeland (with part of the cleanups moved into the new remoteproc_elf_loader.c file). * tag 'remoteproc-for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc: MAINTAINERS: add remoteproc's git remoteproc: Support custom firmware handlers remoteproc: Move Elf related functions to separate file remoteproc: Add function rproc_get_boot_addr remoteproc: Pass struct fw to load_segments and find_rsc_table. remoteproc: adopt the driver core's alloc/add/del/put naming remoteproc: remove the get_by_name/put API remoteproc: support non-iommu carveout assignment remoteproc: simplify unregister/free interfaces remoteproc: remove the now-redundant kref remoteproc: maintain a generic child device for each rproc remoteproc: allocate vrings on demand, free when not needed --- a9197f903f72a81393932d452379c8847fade544 diff --cc drivers/remoteproc/remoteproc_core.c index 66324ee4678f,f4d6f7bb91fd..d5c2dbfc7443 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@@ -1018,9 -784,7 +784,7 @@@ static int rproc_fw_boot(struct rproc * if (ret) return ret; - ehdr = (struct elf32_hdr *)fw->data; - - dev_info(dev, "Booting fw image %s, size %d\n", name, fw->size); + dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size); /* * if enabling an IOMMU isn't relevant for this rproc, this is @@@ -1032,19 -796,12 +796,14 @@@ return ret; } - /* - * The ELF entry point is the rproc's boot addr (though this is not - * a configurable property of all remote processors: some will always - * boot at a specific hardcoded address). - */ - rproc->bootaddr = ehdr->e_entry; + rproc->bootaddr = rproc_get_boot_addr(rproc, fw); /* look for the resource table */ - table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz); + table = rproc_find_rsc_table(rproc, fw, &tablesz); - if (!table) + if (!table) { + ret = -EINVAL; goto clean_up; + } /* handle fw resources which are required to boot rproc */ ret = rproc_handle_boot_rsc(rproc, table, tablesz); diff --cc drivers/remoteproc/remoteproc_elf_loader.c index 000000000000,6eebd01ea985..e1f89d649733 mode 000000,100644..100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@@ -1,0 -1,295 +1,295 @@@ + /* + * Remote Processor Framework Elf loader + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Brian Swetland + * Mark Grosen + * Fernando Guzman Lugo + * Suman Anna + * Robert Tivy + * Armando Uribe De Leon + * Sjur Brændeland + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + #define pr_fmt(fmt) "%s: " fmt, __func__ + + #include + #include + #include + #include + + #include "remoteproc_internal.h" + + /** + * rproc_elf_sanity_check() - Sanity Check ELF firmware image + * @rproc: the remote processor handle + * @fw: the ELF firmware image + * + * Make sure this fw image is sane. + */ + static int + rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) + { + const char *name = rproc->firmware; + struct device *dev = &rproc->dev; + struct elf32_hdr *ehdr; + char class; + + if (!fw) { + dev_err(dev, "failed to load %s\n", name); + return -EINVAL; + } + + if (fw->size < sizeof(struct elf32_hdr)) { + dev_err(dev, "Image is too small\n"); + return -EINVAL; + } + + ehdr = (struct elf32_hdr *)fw->data; + + /* We only support ELF32 at this point */ + class = ehdr->e_ident[EI_CLASS]; + if (class != ELFCLASS32) { + dev_err(dev, "Unsupported class: %d\n", class); + return -EINVAL; + } + + /* We assume the firmware has the same endianess as the host */ + # ifdef __LITTLE_ENDIAN + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { + # else /* BIG ENDIAN */ + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { + # endif + dev_err(dev, "Unsupported firmware endianess\n"); + return -EINVAL; + } + + if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { + dev_err(dev, "Image is too small\n"); + return -EINVAL; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(dev, "Image is corrupted (bad magic)\n"); + return -EINVAL; + } + + if (ehdr->e_phnum == 0) { + dev_err(dev, "No loadable segments\n"); + return -EINVAL; + } + + if (ehdr->e_phoff > fw->size) { + dev_err(dev, "Firmware size is too small\n"); + return -EINVAL; + } + + return 0; + } + + /** + * rproc_elf_get_boot_addr() - Get rproc's boot address. + * @rproc: the remote processor handle + * @fw: the ELF firmware image + * + * This function returns the entry point address of the ELF + * image. + * + * Note that the boot address is not a configurable property of all remote + * processors. Some will always boot at a specific hard-coded address. + */ + static + u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw) + { + struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; + + return ehdr->e_entry; + } + + /** + * rproc_elf_load_segments() - load firmware segments to memory + * @rproc: remote processor which will be booted using these fw segments + * @fw: the ELF firmware image + * + * This function loads the firmware segments to memory, where the remote + * processor expects them. + * + * Some remote processors will expect their code and data to be placed + * in specific device addresses, and can't have them dynamically assigned. + * + * We currently support only those kind of remote processors, and expect + * the program header's paddr member to contain those addresses. We then go + * through the physically contiguous "carveout" memory regions which we + * allocated (and mapped) earlier on behalf of the remote processor, + * and "translate" device address to kernel addresses, so we can copy the + * segments where they are expected. + * + * Currently we only support remote processors that required carveout + * allocations and got them mapped onto their iommus. Some processors + * might be different: they might not have iommus, and would prefer to + * directly allocate memory for every segment/resource. This is not yet + * supported, though. + */ + static int + rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) + { + struct device *dev = &rproc->dev; + struct elf32_hdr *ehdr; + struct elf32_phdr *phdr; + int i, ret = 0; + const u8 *elf_data = fw->data; + + ehdr = (struct elf32_hdr *)elf_data; + phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + u32 da = phdr->p_paddr; + u32 memsz = phdr->p_memsz; + u32 filesz = phdr->p_filesz; + u32 offset = phdr->p_offset; + void *ptr; + + if (phdr->p_type != PT_LOAD) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", + phdr->p_type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > fw->size) { - dev_err(dev, "truncated fw: need 0x%x avail 0x%x\n", ++ dev_err(dev, "truncated fw: need 0x%x avail 0x%zx\n", + offset + filesz, fw->size); + ret = -EINVAL; + break; + } + + /* grab the kernel address for this device address */ + ptr = rproc_da_to_va(rproc, da, memsz); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (phdr->p_filesz) + memcpy(ptr, elf_data + phdr->p_offset, filesz); + + /* + * Zero out remaining memory for this segment. + * + * This isn't strictly required since dma_alloc_coherent already + * did this for us. albeit harmless, we may consider removing + * this. + */ + if (memsz > filesz) + memset(ptr + filesz, 0, memsz - filesz); + } + + return ret; + } + + /** + * rproc_elf_find_rsc_table() - find the resource table + * @rproc: the rproc handle + * @fw: the ELF firmware image + * @tablesz: place holder for providing back the table size + * + * This function finds the resource table inside the remote processor's + * firmware. It is used both upon the registration of @rproc (in order + * to look for and register the supported virito devices), and when the + * @rproc is booted. + * + * Returns the pointer to the resource table if it is found, and write its + * size into @tablesz. If a valid table isn't found, NULL is returned + * (and @tablesz isn't set). + */ + static struct resource_table * + rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw, + int *tablesz) + { + struct elf32_hdr *ehdr; + struct elf32_shdr *shdr; + const char *name_table; + struct device *dev = &rproc->dev; + struct resource_table *table = NULL; + int i; + const u8 *elf_data = fw->data; + + ehdr = (struct elf32_hdr *)elf_data; + shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); + name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; + + /* look for the resource table and handle it */ + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + int size = shdr->sh_size; + int offset = shdr->sh_offset; + + if (strcmp(name_table + shdr->sh_name, ".resource_table")) + continue; + + table = (struct resource_table *)(elf_data + offset); + + /* make sure we have the entire table */ + if (offset + size > fw->size) { + dev_err(dev, "resource table truncated\n"); + return NULL; + } + + /* make sure table has at least the header */ + if (sizeof(struct resource_table) > size) { + dev_err(dev, "header-less resource table\n"); + return NULL; + } + + /* we don't support any version beyond the first */ + if (table->ver != 1) { + dev_err(dev, "unsupported fw ver: %d\n", table->ver); + return NULL; + } + + /* make sure reserved bytes are zeroes */ + if (table->reserved[0] || table->reserved[1]) { + dev_err(dev, "non zero reserved bytes\n"); + return NULL; + } + + /* make sure the offsets array isn't truncated */ + if (table->num * sizeof(table->offset[0]) + + sizeof(struct resource_table) > size) { + dev_err(dev, "resource table incomplete\n"); + return NULL; + } + + *tablesz = shdr->sh_size; + break; + } + + return table; + } + + const struct rproc_fw_ops rproc_elf_fw_ops = { + .load = rproc_elf_load_segments, + .find_rsc_table = rproc_elf_find_rsc_table, + .sanity_check = rproc_elf_sanity_check, + .get_boot_addr = rproc_elf_get_boot_addr + };