/* Copyright (c) 2016, 2018 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include "remoteproc_internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define QCA_MDT_TYPE_MASK (7 << 24) #define QCA_MDT_TYPE_HASH (2 << 24) int mdt_load(struct rproc *rproc, const struct firmware *fw) { int ret = 0; size_t name_len; const char *fname = rproc->firmware; char *segment_name; struct device *dev_rproc = rproc->dev.parent; struct elf32_hdr *ehdr; int i = 0; const u8 *elf_data; struct elf32_phdr *phdr; name_len = strlen(fname); if (name_len <= 4) { dev_err(dev_rproc, "Firmware name should be >4bytes (*.mdt)\n"); return -EINVAL; } segment_name = kstrdup(fname, GFP_KERNEL); if (!segment_name) return -ENOMEM; if (!fw) { dev_err(dev_rproc, "failed to load %s\n", fname); return -EINVAL; } elf_data = fw->data; ehdr = (struct elf32_hdr *)fw->data; phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); /* go through the available ELF segments and load it */ for (i = 0; i < ehdr->e_phnum; i++, phdr++) { u32 pa = phdr->p_paddr; u32 memsz = phdr->p_memsz; u32 filesz = phdr->p_filesz; void *ptr; if ((phdr->p_type != PT_LOAD) || ((phdr->p_flags & QCA_MDT_TYPE_MASK) == QCA_MDT_TYPE_HASH) || (!phdr->p_memsz)) continue; /* grab the kernel address for this device address */ ptr = rproc_da_to_va(rproc, pa, memsz); if (!ptr) { dev_err(dev_rproc, "bad phdr pa 0x%x mem 0x%x\n", pa, memsz); ret = -EINVAL; return ret; } /* put the segment where the remote processor expects it */ if (phdr->p_filesz) { /* The firmware file has .mdt as the ELF header, * hash segment, .b00, .b01, etc * for every ELF segment of the firmware file. The * rproc loads the first .mdt file, and for the * ELF segments that we need to load, we make the * filename as .b"segment_number" */ snprintf(segment_name + name_len - 3, (size_t)4, "b%02d", i); ret = request_firmware(&fw, segment_name, dev_rproc); if (ret) { dev_err(dev_rproc, "can't to load %s\n", segment_name); iounmap(ptr); break; } memcpy_toio(ptr, fw->data, fw->size); release_firmware(fw); } /* for .bss and sections that needs to be memset to zero */ if (memsz > filesz) memset_io(ptr + filesz, 0, memsz - filesz); iounmap(ptr); } kfree(segment_name); return ret; }