^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Qualcomm Peripheral Image Loader
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2016 Linaro Ltd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2015 Sony Mobile Communications Inc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/elf.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/firmware.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/qcom_scm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/sizes.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/soc/qcom/mdt_loader.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) static bool mdt_phdr_valid(const struct elf32_phdr *phdr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) if (phdr->p_type != PT_LOAD)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) if (!phdr->p_memsz)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) return false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) return true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * @fw: firmware object for the mdt file
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * Returns size of the loaded firmware blob, or -EINVAL on failure.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) ssize_t qcom_mdt_get_size(const struct firmware *fw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) const struct elf32_phdr *phdrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) const struct elf32_phdr *phdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) const struct elf32_hdr *ehdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) phys_addr_t min_addr = PHYS_ADDR_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) phys_addr_t max_addr = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) ehdr = (struct elf32_hdr *)fw->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) phdrs = (struct elf32_phdr *)(ehdr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) for (i = 0; i < ehdr->e_phnum; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) phdr = &phdrs[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) if (!mdt_phdr_valid(phdr))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) if (phdr->p_paddr < min_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) min_addr = phdr->p_paddr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) if (phdr->p_paddr + phdr->p_memsz > max_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) return min_addr < max_addr ? max_addr - min_addr : -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * @fw: firmware of mdt header or mbn
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * @data_len: length of the read metadata blob
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * The mechanism that performs the authentication of the loading firmware
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) * expects an ELF header directly followed by the segment of hashes, with no
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) * padding inbetween. This function allocates a chunk of memory for this pair
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) * and copy the two pieces into the buffer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) * In the case of split firmware the hash is found directly following the ELF
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) * header, rather than at p_offset described by the second program header.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) * The caller is responsible to free (kfree()) the returned pointer.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) * Return: pointer to data, or ERR_PTR()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) const struct elf32_phdr *phdrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) const struct elf32_hdr *ehdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) size_t hash_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) size_t hash_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) size_t ehdr_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) void *data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) ehdr = (struct elf32_hdr *)fw->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) phdrs = (struct elf32_phdr *)(ehdr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (ehdr->e_phnum < 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) return ERR_PTR(-EINVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) if (phdrs[0].p_type == PT_LOAD)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) return ERR_PTR(-EINVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) if ((phdrs[1].p_flags & QCOM_MDT_TYPE_MASK) != QCOM_MDT_TYPE_HASH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) return ERR_PTR(-EINVAL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) ehdr_size = phdrs[0].p_filesz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) hash_size = phdrs[1].p_filesz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) data = kmalloc(ehdr_size + hash_size, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) if (!data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) return ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) /* Is the header and hash already packed */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) if (ehdr_size + hash_size == fw->size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) hash_offset = phdrs[0].p_filesz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) hash_offset = phdrs[1].p_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) memcpy(data, fw->data, ehdr_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) memcpy(data + ehdr_size, fw->data + hash_offset, hash_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) *data_len = ehdr_size + hash_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) const char *firmware, int pas_id, void *mem_region,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) phys_addr_t mem_phys, size_t mem_size,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) phys_addr_t *reloc_base, bool pas_init)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) const struct elf32_phdr *phdrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) const struct elf32_phdr *phdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) const struct elf32_hdr *ehdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) const struct firmware *seg_fw;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) phys_addr_t mem_reloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) phys_addr_t min_addr = PHYS_ADDR_MAX;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) phys_addr_t max_addr = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) size_t metadata_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) size_t fw_name_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) ssize_t offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) void *metadata;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) char *fw_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) bool relocate = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) void *ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) if (!fw || !mem_region || !mem_phys || !mem_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) ehdr = (struct elf32_hdr *)fw->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) phdrs = (struct elf32_phdr *)(ehdr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) fw_name_len = strlen(firmware);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) if (fw_name_len <= 4)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) fw_name = kstrdup(firmware, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) if (!fw_name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (pas_init) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) metadata = qcom_mdt_read_metadata(fw, &metadata_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) if (IS_ERR(metadata)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) ret = PTR_ERR(metadata);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) kfree(metadata);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) dev_err(dev, "invalid firmware metadata\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) for (i = 0; i < ehdr->e_phnum; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) phdr = &phdrs[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (!mdt_phdr_valid(phdr))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) if (phdr->p_flags & QCOM_MDT_RELOCATABLE)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) relocate = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) if (phdr->p_paddr < min_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) min_addr = phdr->p_paddr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) if (phdr->p_paddr + phdr->p_memsz > max_addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) if (relocate) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) if (pas_init) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) ret = qcom_scm_pas_mem_setup(pas_id, mem_phys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) max_addr - min_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) dev_err(dev, "unable to setup relocation\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) * The image is relocatable, so offset each segment based on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) * the lowest segment address.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) mem_reloc = min_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) * Image is not relocatable, so offset each segment based on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) * the allocated physical chunk of memory.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) mem_reloc = mem_phys;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) for (i = 0; i < ehdr->e_phnum; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) phdr = &phdrs[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) if (!mdt_phdr_valid(phdr))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) offset = phdr->p_paddr - mem_reloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (offset < 0 || offset + phdr->p_memsz > mem_size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) dev_err(dev, "segment outside memory range\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) if (phdr->p_filesz > phdr->p_memsz) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) dev_err(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) "refusing to load segment %d with p_filesz > p_memsz\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) ptr = mem_region + offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) if (phdr->p_filesz && phdr->p_offset < fw->size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) /* Firmware is large enough to be non-split */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) if (phdr->p_offset + phdr->p_filesz > fw->size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) dev_err(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) "failed to load segment %d from truncated file %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) i, firmware);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) } else if (phdr->p_filesz) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) /* Firmware not large enough, load split-out segments */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) sprintf(fw_name + fw_name_len - 3, "b%02d", i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) ret = request_firmware_into_buf(&seg_fw, fw_name, dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) ptr, phdr->p_filesz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) dev_err(dev, "failed to load %s\n", fw_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) if (seg_fw->size != phdr->p_filesz) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) dev_err(dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) "failed to load segment %d from truncated file %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) i, fw_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) release_firmware(seg_fw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) release_firmware(seg_fw);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) if (phdr->p_memsz > phdr->p_filesz)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) if (reloc_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) *reloc_base = mem_reloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) kfree(fw_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) * qcom_mdt_load() - load the firmware which header is loaded as fw
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) * @dev: device handle to associate resources with
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) * @fw: firmware object for the mdt file
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) * @firmware: name of the firmware, for construction of segment file names
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) * @pas_id: PAS identifier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) * @mem_region: allocated memory region to load firmware into
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) * @mem_phys: physical address of allocated memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) * @mem_size: size of the allocated memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) * @reloc_base: adjusted physical address after relocation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) * Returns 0 on success, negative errno otherwise.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) int qcom_mdt_load(struct device *dev, const struct firmware *fw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) const char *firmware, int pas_id, void *mem_region,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) phys_addr_t mem_phys, size_t mem_size,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) phys_addr_t *reloc_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) mem_size, reloc_base, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) EXPORT_SYMBOL_GPL(qcom_mdt_load);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) * @dev: device handle to associate resources with
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) * @fw: firmware object for the mdt file
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) * @firmware: name of the firmware, for construction of segment file names
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) * @pas_id: PAS identifier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) * @mem_region: allocated memory region to load firmware into
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) * @mem_phys: physical address of allocated memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) * @mem_size: size of the allocated memory region
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) * @reloc_base: adjusted physical address after relocation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) * Returns 0 on success, negative errno otherwise.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) const char *firmware, int pas_id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) void *mem_region, phys_addr_t mem_phys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) size_t mem_size, phys_addr_t *reloc_base)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) mem_size, reloc_base, false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) MODULE_LICENSE("GPL v2");