aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc/qcom/mdt_loader.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/soc/qcom/mdt_loader.c')
-rw-r--r--drivers/soc/qcom/mdt_loader.c88
1 files changed, 85 insertions, 3 deletions
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index 1c488024c698..1c970d6dd532 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -74,6 +74,66 @@ ssize_t qcom_mdt_get_size(const struct firmware *fw)
74} 74}
75EXPORT_SYMBOL_GPL(qcom_mdt_get_size); 75EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
76 76
77/**
78 * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn
79 * @fw: firmware of mdt header or mbn
80 * @data_len: length of the read metadata blob
81 *
82 * The mechanism that performs the authentication of the loading firmware
83 * expects an ELF header directly followed by the segment of hashes, with no
84 * padding inbetween. This function allocates a chunk of memory for this pair
85 * and copy the two pieces into the buffer.
86 *
87 * In the case of split firmware the hash is found directly following the ELF
88 * header, rather than at p_offset described by the second program header.
89 *
90 * The caller is responsible to free (kfree()) the returned pointer.
91 *
92 * Return: pointer to data, or ERR_PTR()
93 */
94void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len)
95{
96 const struct elf32_phdr *phdrs;
97 const struct elf32_hdr *ehdr;
98 size_t hash_offset;
99 size_t hash_size;
100 size_t ehdr_size;
101 void *data;
102
103 ehdr = (struct elf32_hdr *)fw->data;
104 phdrs = (struct elf32_phdr *)(ehdr + 1);
105
106 if (ehdr->e_phnum < 2)
107 return ERR_PTR(-EINVAL);
108
109 if (phdrs[0].p_type == PT_LOAD || phdrs[1].p_type == PT_LOAD)
110 return ERR_PTR(-EINVAL);
111
112 if ((phdrs[1].p_flags & QCOM_MDT_TYPE_MASK) != QCOM_MDT_TYPE_HASH)
113 return ERR_PTR(-EINVAL);
114
115 ehdr_size = phdrs[0].p_filesz;
116 hash_size = phdrs[1].p_filesz;
117
118 data = kmalloc(ehdr_size + hash_size, GFP_KERNEL);
119 if (!data)
120 return ERR_PTR(-ENOMEM);
121
122 /* Is the header and hash already packed */
123 if (ehdr_size + hash_size == fw->size)
124 hash_offset = phdrs[0].p_filesz;
125 else
126 hash_offset = phdrs[1].p_offset;
127
128 memcpy(data, fw->data, ehdr_size);
129 memcpy(data + ehdr_size, fw->data + hash_offset, hash_size);
130
131 *data_len = ehdr_size + hash_size;
132
133 return data;
134}
135EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata);
136
77static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, 137static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
78 const char *firmware, int pas_id, void *mem_region, 138 const char *firmware, int pas_id, void *mem_region,
79 phys_addr_t mem_phys, size_t mem_size, 139 phys_addr_t mem_phys, size_t mem_size,
@@ -86,12 +146,14 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
86 phys_addr_t mem_reloc; 146 phys_addr_t mem_reloc;
87 phys_addr_t min_addr = PHYS_ADDR_MAX; 147 phys_addr_t min_addr = PHYS_ADDR_MAX;
88 phys_addr_t max_addr = 0; 148 phys_addr_t max_addr = 0;
149 size_t metadata_len;
89 size_t fw_name_len; 150 size_t fw_name_len;
90 ssize_t offset; 151 ssize_t offset;
152 void *metadata;
91 char *fw_name; 153 char *fw_name;
92 bool relocate = false; 154 bool relocate = false;
93 void *ptr; 155 void *ptr;
94 int ret; 156 int ret = 0;
95 int i; 157 int i;
96 158
97 if (!fw || !mem_region || !mem_phys || !mem_size) 159 if (!fw || !mem_region || !mem_phys || !mem_size)
@@ -109,7 +171,15 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
109 return -ENOMEM; 171 return -ENOMEM;
110 172
111 if (pas_init) { 173 if (pas_init) {
112 ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); 174 metadata = qcom_mdt_read_metadata(fw, &metadata_len);
175 if (IS_ERR(metadata)) {
176 ret = PTR_ERR(metadata);
177 goto out;
178 }
179
180 ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len);
181
182 kfree(metadata);
113 if (ret) { 183 if (ret) {
114 dev_err(dev, "invalid firmware metadata\n"); 184 dev_err(dev, "invalid firmware metadata\n");
115 goto out; 185 goto out;
@@ -170,7 +240,19 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw,
170 240
171 ptr = mem_region + offset; 241 ptr = mem_region + offset;
172 242
173 if (phdr->p_filesz) { 243 if (phdr->p_filesz && phdr->p_offset < fw->size) {
244 /* Firmware is large enough to be non-split */
245 if (phdr->p_offset + phdr->p_filesz > fw->size) {
246 dev_err(dev,
247 "failed to load segment %d from truncated file %s\n",
248 i, firmware);
249 ret = -EINVAL;
250 break;
251 }
252
253 memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz);
254 } else if (phdr->p_filesz) {
255 /* Firmware not large enough, load split-out segments */
174 sprintf(fw_name + fw_name_len - 3, "b%02d", i); 256 sprintf(fw_name + fw_name_len - 3, "b%02d", i);
175 ret = request_firmware_into_buf(&seg_fw, fw_name, dev, 257 ret = request_firmware_into_buf(&seg_fw, fw_name, dev,
176 ptr, phdr->p_filesz); 258 ptr, phdr->p_filesz);