blob: 1004e0135133a58d11615756671807a005c7dedc [file] [log] [blame]
Matt Wagantall39f90cb2012-02-08 14:09:11 -08001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/module.h>
14#include <linux/string.h>
Stephen Boyd3f4da322011-08-30 01:03:23 -070015#include <linux/device.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070016#include <linux/firmware.h>
17#include <linux/io.h>
18#include <linux/debugfs.h>
19#include <linux/elf.h>
20#include <linux/mutex.h>
21#include <linux/memblock.h>
Stephen Boyd3f4da322011-08-30 01:03:23 -070022#include <linux/slab.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023
24#include <asm/uaccess.h>
25#include <asm/setup.h>
26
27#include "peripheral-loader.h"
28
Stephen Boyd3f4da322011-08-30 01:03:23 -070029struct pil_device {
30 struct pil_desc *desc;
31 int count;
32 struct mutex lock;
33 struct list_head list;
34};
35
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036static DEFINE_MUTEX(pil_list_lock);
37static LIST_HEAD(pil_list);
38
39static struct pil_device *__find_peripheral(const char *str)
40{
41 struct pil_device *dev;
42
43 list_for_each_entry(dev, &pil_list, list)
Stephen Boyd3f4da322011-08-30 01:03:23 -070044 if (!strcmp(dev->desc->name, str))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070045 return dev;
46 return NULL;
47}
48
49static struct pil_device *find_peripheral(const char *str)
50{
51 struct pil_device *dev;
52
53 if (!str)
54 return NULL;
55
56 mutex_lock(&pil_list_lock);
57 dev = __find_peripheral(str);
58 mutex_unlock(&pil_list_lock);
59
60 return dev;
61}
62
63#define IOMAP_SIZE SZ_4M
64
65static int load_segment(const struct elf32_phdr *phdr, unsigned num,
66 struct pil_device *pil)
67{
68 int ret, count, paddr;
69 char fw_name[30];
70 const struct firmware *fw = NULL;
71 const u8 *data;
72
73 if (memblock_is_region_memory(phdr->p_paddr, phdr->p_memsz)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070074 dev_err(pil->desc->dev, "Kernel memory would be overwritten");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075 return -EPERM;
76 }
77
78 if (phdr->p_filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070079 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d",
80 pil->desc->name, num);
81 ret = request_firmware(&fw, fw_name, pil->desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070083 dev_err(pil->desc->dev, "Failed to locate blob %s\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084 fw_name);
85 return ret;
86 }
87
88 if (fw->size != phdr->p_filesz) {
Stephen Boyd3f4da322011-08-30 01:03:23 -070089 dev_err(pil->desc->dev,
90 "Blob size %u doesn't match %u\n", fw->size,
91 phdr->p_filesz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092 ret = -EPERM;
93 goto release_fw;
94 }
95 }
96
97 /* Load the segment into memory */
98 count = phdr->p_filesz;
99 paddr = phdr->p_paddr;
100 data = fw ? fw->data : NULL;
101 while (count > 0) {
102 int size;
103 u8 __iomem *buf;
104
105 size = min_t(size_t, IOMAP_SIZE, count);
106 buf = ioremap(paddr, size);
107 if (!buf) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700108 dev_err(pil->desc->dev, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 ret = -ENOMEM;
110 goto release_fw;
111 }
112 memcpy(buf, data, size);
113 iounmap(buf);
114
115 count -= size;
116 paddr += size;
117 data += size;
118 }
119
120 /* Zero out trailing memory */
121 count = phdr->p_memsz - phdr->p_filesz;
122 while (count > 0) {
123 int size;
124 u8 __iomem *buf;
125
126 size = min_t(size_t, IOMAP_SIZE, count);
127 buf = ioremap(paddr, size);
128 if (!buf) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700129 dev_err(pil->desc->dev, "Failed to map memory\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130 ret = -ENOMEM;
131 goto release_fw;
132 }
133 memset(buf, 0, size);
134 iounmap(buf);
135
136 count -= size;
137 paddr += size;
138 }
139
Stephen Boyd3f4da322011-08-30 01:03:23 -0700140 ret = pil->desc->ops->verify_blob(pil->desc, phdr->p_paddr,
141 phdr->p_memsz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 if (ret)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700143 dev_err(pil->desc->dev, "Blob %u failed verification\n", num);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144
145release_fw:
146 release_firmware(fw);
147 return ret;
148}
149
150#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
151
152static int segment_is_loadable(const struct elf32_phdr *p)
153{
154 return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags);
155}
156
157static int load_image(struct pil_device *pil)
158{
159 int i, ret;
160 char fw_name[30];
161 struct elf32_hdr *ehdr;
162 const struct elf32_phdr *phdr;
163 const struct firmware *fw;
164
Stephen Boyd3f4da322011-08-30 01:03:23 -0700165 snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->desc->name);
166 ret = request_firmware(&fw, fw_name, pil->desc->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700168 dev_err(pil->desc->dev, "Failed to locate %s\n", fw_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 goto out;
170 }
171
172 if (fw->size < sizeof(*ehdr)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700173 dev_err(pil->desc->dev, "Not big enough to be an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174 ret = -EIO;
175 goto release_fw;
176 }
177
178 ehdr = (struct elf32_hdr *)fw->data;
179 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700180 dev_err(pil->desc->dev, "Not an elf header\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181 ret = -EIO;
182 goto release_fw;
183 }
184
185 if (ehdr->e_phnum == 0) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700186 dev_err(pil->desc->dev, "No loadable segments\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187 ret = -EIO;
188 goto release_fw;
189 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700190 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
191 sizeof(struct elf32_hdr) > fw->size) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700192 dev_err(pil->desc->dev, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 ret = -EIO;
194 goto release_fw;
195 }
196
Stephen Boyd3f4da322011-08-30 01:03:23 -0700197 ret = pil->desc->ops->init_image(pil->desc, fw->data, fw->size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700199 dev_err(pil->desc->dev, "Invalid firmware metadata\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700200 goto release_fw;
201 }
202
Stephen Boydc9753e12011-07-13 17:58:48 -0700203 phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
205 if (!segment_is_loadable(phdr))
206 continue;
207
208 ret = load_segment(phdr, i, pil);
209 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700210 dev_err(pil->desc->dev, "Failed to load segment %d\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 i);
212 goto release_fw;
213 }
214 }
215
Stephen Boyd3f4da322011-08-30 01:03:23 -0700216 ret = pil->desc->ops->auth_and_reset(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700217 if (ret) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700218 dev_err(pil->desc->dev, "Failed to bring out of reset\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700219 goto release_fw;
220 }
Stephen Boyde4389ad2011-12-08 10:20:27 -0800221 dev_info(pil->desc->dev, "brought out of reset\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222
223release_fw:
224 release_firmware(fw);
225out:
226 return ret;
227}
228
229/**
230 * pil_get() - Load a peripheral into memory and take it out of reset
231 * @name: pointer to a string containing the name of the peripheral to load
232 *
233 * This function returns a pointer if it succeeds. If an error occurs an
234 * ERR_PTR is returned.
235 *
236 * If PIL is not enabled in the kernel, the value %NULL will be returned.
237 */
238void *pil_get(const char *name)
239{
240 int ret;
241 struct pil_device *pil;
242 struct pil_device *pil_d;
243 void *retval;
244
245 pil = retval = find_peripheral(name);
246 if (!pil)
247 return ERR_PTR(-ENODEV);
248
Stephen Boyd3f4da322011-08-30 01:03:23 -0700249 pil_d = find_peripheral(pil->desc->depends_on);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700250 if (pil_d) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700251 void *p = pil_get(pil_d->desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252 if (IS_ERR(p))
253 return p;
254 }
255
256 mutex_lock(&pil->lock);
257 if (pil->count) {
258 pil->count++;
259 goto unlock;
260 }
261
262 ret = load_image(pil);
263 if (ret) {
264 retval = ERR_PTR(ret);
265 goto unlock;
266 }
267
268 pil->count++;
269unlock:
270 mutex_unlock(&pil->lock);
271 return retval;
272}
273EXPORT_SYMBOL(pil_get);
274
275/**
276 * pil_put() - Inform PIL the peripheral no longer needs to be active
277 * @peripheral_handle: pointer from a previous call to pil_get()
278 *
279 * This doesn't imply that a peripheral is shutdown or in reset since another
280 * driver could be using the peripheral.
281 */
282void pil_put(void *peripheral_handle)
283{
284 struct pil_device *pil_d;
285 struct pil_device *pil = peripheral_handle;
Stephen Boyd545e8362011-09-13 15:04:38 -0700286 if (!pil || IS_ERR(pil))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
289 mutex_lock(&pil->lock);
290 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 if (pil->count)
292 pil->count--;
293 if (pil->count == 0)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700294 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 mutex_unlock(&pil->lock);
296
Stephen Boyd3f4da322011-08-30 01:03:23 -0700297 pil_d = find_peripheral(pil->desc->depends_on);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298 if (pil_d)
299 pil_put(pil_d);
300}
301EXPORT_SYMBOL(pil_put);
302
303void pil_force_shutdown(const char *name)
304{
305 struct pil_device *pil;
306
307 pil = find_peripheral(name);
308 if (!pil)
309 return;
310
311 mutex_lock(&pil->lock);
312 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
Stephen Boyd3f4da322011-08-30 01:03:23 -0700313 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700314 mutex_unlock(&pil->lock);
315}
316EXPORT_SYMBOL(pil_force_shutdown);
317
318int pil_force_boot(const char *name)
319{
320 int ret = -EINVAL;
321 struct pil_device *pil;
322
323 pil = find_peripheral(name);
324 if (!pil)
325 return -EINVAL;
326
327 mutex_lock(&pil->lock);
328 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
329 ret = load_image(pil);
330 mutex_unlock(&pil->lock);
331
332 return ret;
333}
334EXPORT_SYMBOL(pil_force_boot);
335
336#ifdef CONFIG_DEBUG_FS
337int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
338{
339 filp->private_data = inode->i_private;
340 return 0;
341}
342
343static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
344 size_t cnt, loff_t *ppos)
345{
346 int r;
347 char buf[40];
348 struct pil_device *pil = filp->private_data;
349
350 mutex_lock(&pil->lock);
351 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
352 mutex_unlock(&pil->lock);
353 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
354}
355
356static ssize_t msm_pil_debugfs_write(struct file *filp,
357 const char __user *ubuf, size_t cnt, loff_t *ppos)
358{
359 struct pil_device *pil = filp->private_data;
360 char buf[4];
361
362 if (cnt > sizeof(buf))
363 return -EINVAL;
364
365 if (copy_from_user(&buf, ubuf, cnt))
366 return -EFAULT;
367
368 if (!strncmp(buf, "get", 3)) {
Stephen Boyd3f4da322011-08-30 01:03:23 -0700369 if (IS_ERR(pil_get(pil->desc->name)))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 return -EIO;
371 } else if (!strncmp(buf, "put", 3))
372 pil_put(pil);
373 else
374 return -EINVAL;
375
376 return cnt;
377}
378
379static const struct file_operations msm_pil_debugfs_fops = {
380 .open = msm_pil_debugfs_open,
381 .read = msm_pil_debugfs_read,
382 .write = msm_pil_debugfs_write,
383};
384
385static struct dentry *pil_base_dir;
386
387static int msm_pil_debugfs_init(void)
388{
389 pil_base_dir = debugfs_create_dir("pil", NULL);
390 if (!pil_base_dir) {
391 pil_base_dir = NULL;
392 return -ENOMEM;
393 }
394
395 return 0;
396}
397arch_initcall(msm_pil_debugfs_init);
398
399static int msm_pil_debugfs_add(struct pil_device *pil)
400{
401 if (!pil_base_dir)
402 return -ENOMEM;
403
Stephen Boyd3f4da322011-08-30 01:03:23 -0700404 if (!debugfs_create_file(pil->desc->name, S_IRUGO | S_IWUSR,
405 pil_base_dir, pil, &msm_pil_debugfs_fops))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 return -ENOMEM;
407 return 0;
408}
409#else
410static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
411#endif
412
413static int msm_pil_shutdown_at_boot(void)
414{
415 struct pil_device *pil;
416
417 mutex_lock(&pil_list_lock);
418 list_for_each_entry(pil, &pil_list, list)
Stephen Boyd3f4da322011-08-30 01:03:23 -0700419 pil->desc->ops->shutdown(pil->desc);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420 mutex_unlock(&pil_list_lock);
421
422 return 0;
423}
424late_initcall(msm_pil_shutdown_at_boot);
425
Stephen Boyd3f4da322011-08-30 01:03:23 -0700426int msm_pil_register(struct pil_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427{
Stephen Boyd3f4da322011-08-30 01:03:23 -0700428 struct pil_device *pil = kzalloc(sizeof(*pil), GFP_KERNEL);
429 if (!pil)
430 return -ENOMEM;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431
432 mutex_init(&pil->lock);
Stephen Boyd3f4da322011-08-30 01:03:23 -0700433 INIT_LIST_HEAD(&pil->list);
434 pil->desc = desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435
436 mutex_lock(&pil_list_lock);
437 list_add(&pil->list, &pil_list);
438 mutex_unlock(&pil_list_lock);
439
Stephen Boyd3f4da322011-08-30 01:03:23 -0700440 return msm_pil_debugfs_add(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441}
Stephen Boyd3f4da322011-08-30 01:03:23 -0700442EXPORT_SYMBOL(msm_pil_register);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443
444MODULE_LICENSE("GPL v2");
445MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");