blob: af6f596607618879d8cad57d30838aa5fab1712c [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
2 *
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>
15#include <linux/platform_device.h>
16#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>
22
23#include <asm/uaccess.h>
24#include <asm/setup.h>
25
26#include "peripheral-loader.h"
27
28static DEFINE_MUTEX(pil_list_lock);
29static LIST_HEAD(pil_list);
30
31static struct pil_device *__find_peripheral(const char *str)
32{
33 struct pil_device *dev;
34
35 list_for_each_entry(dev, &pil_list, list)
36 if (!strcmp(dev->name, str))
37 return dev;
38 return NULL;
39}
40
41static struct pil_device *find_peripheral(const char *str)
42{
43 struct pil_device *dev;
44
45 if (!str)
46 return NULL;
47
48 mutex_lock(&pil_list_lock);
49 dev = __find_peripheral(str);
50 mutex_unlock(&pil_list_lock);
51
52 return dev;
53}
54
55#define IOMAP_SIZE SZ_4M
56
57static int load_segment(const struct elf32_phdr *phdr, unsigned num,
58 struct pil_device *pil)
59{
60 int ret, count, paddr;
61 char fw_name[30];
62 const struct firmware *fw = NULL;
63 const u8 *data;
64
65 if (memblock_is_region_memory(phdr->p_paddr, phdr->p_memsz)) {
66 dev_err(&pil->pdev.dev, "Kernel memory would be overwritten");
67 return -EPERM;
68 }
69
70 if (phdr->p_filesz) {
71 snprintf(fw_name, ARRAY_SIZE(fw_name), "%s.b%02d", pil->name,
72 num);
73 ret = request_firmware(&fw, fw_name, &pil->pdev.dev);
74 if (ret) {
75 dev_err(&pil->pdev.dev, "Failed to locate blob %s\n",
76 fw_name);
77 return ret;
78 }
79
80 if (fw->size != phdr->p_filesz) {
81 dev_err(&pil->pdev.dev,
82 "Blob size %u doesn't match %u\n",
83 fw->size, phdr->p_filesz);
84 ret = -EPERM;
85 goto release_fw;
86 }
87 }
88
89 /* Load the segment into memory */
90 count = phdr->p_filesz;
91 paddr = phdr->p_paddr;
92 data = fw ? fw->data : NULL;
93 while (count > 0) {
94 int size;
95 u8 __iomem *buf;
96
97 size = min_t(size_t, IOMAP_SIZE, count);
98 buf = ioremap(paddr, size);
99 if (!buf) {
100 dev_err(&pil->pdev.dev, "Failed to map memory\n");
101 ret = -ENOMEM;
102 goto release_fw;
103 }
104 memcpy(buf, data, size);
105 iounmap(buf);
106
107 count -= size;
108 paddr += size;
109 data += size;
110 }
111
112 /* Zero out trailing memory */
113 count = phdr->p_memsz - phdr->p_filesz;
114 while (count > 0) {
115 int size;
116 u8 __iomem *buf;
117
118 size = min_t(size_t, IOMAP_SIZE, count);
119 buf = ioremap(paddr, size);
120 if (!buf) {
121 dev_err(&pil->pdev.dev, "Failed to map memory\n");
122 ret = -ENOMEM;
123 goto release_fw;
124 }
125 memset(buf, 0, size);
126 iounmap(buf);
127
128 count -= size;
129 paddr += size;
130 }
131
132 ret = pil->ops->verify_blob(phdr->p_paddr, phdr->p_memsz);
133 if (ret)
134 dev_err(&pil->pdev.dev, "Blob %u failed verification\n", num);
135
136release_fw:
137 release_firmware(fw);
138 return ret;
139}
140
141#define segment_is_hash(flag) (((flag) & (0x7 << 24)) == (0x2 << 24))
142
143static int segment_is_loadable(const struct elf32_phdr *p)
144{
145 return (p->p_type & PT_LOAD) && !segment_is_hash(p->p_flags);
146}
147
148static int load_image(struct pil_device *pil)
149{
150 int i, ret;
151 char fw_name[30];
152 struct elf32_hdr *ehdr;
153 const struct elf32_phdr *phdr;
154 const struct firmware *fw;
155
156 snprintf(fw_name, sizeof(fw_name), "%s.mdt", pil->name);
157 ret = request_firmware(&fw, fw_name, &pil->pdev.dev);
158 if (ret) {
159 dev_err(&pil->pdev.dev, "Failed to locate %s\n", fw_name);
160 goto out;
161 }
162
163 if (fw->size < sizeof(*ehdr)) {
164 dev_err(&pil->pdev.dev, "Not big enough to be an elf header\n");
165 ret = -EIO;
166 goto release_fw;
167 }
168
169 ehdr = (struct elf32_hdr *)fw->data;
170 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
171 dev_err(&pil->pdev.dev, "Not an elf header\n");
172 ret = -EIO;
173 goto release_fw;
174 }
175
176 if (ehdr->e_phnum == 0) {
177 dev_err(&pil->pdev.dev, "No loadable segments\n");
178 ret = -EIO;
179 goto release_fw;
180 }
181 if (ehdr->e_phoff > fw->size) {
182 dev_err(&pil->pdev.dev, "Program header beyond size of mdt\n");
183 ret = -EIO;
184 goto release_fw;
185 }
186
187 ret = pil->ops->init_image(fw->data, fw->size);
188 if (ret) {
189 dev_err(&pil->pdev.dev, "Invalid firmware metadata\n");
190 goto release_fw;
191 }
192
193 phdr = (const struct elf32_phdr *)(fw->data + ehdr->e_phoff);
194 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
195 if (!segment_is_loadable(phdr))
196 continue;
197
198 ret = load_segment(phdr, i, pil);
199 if (ret) {
200 dev_err(&pil->pdev.dev, "Failed to load segment %d\n",
201 i);
202 goto release_fw;
203 }
204 }
205
206 ret = pil->ops->auth_and_reset();
207 if (ret) {
208 dev_err(&pil->pdev.dev, "Failed to bring out of reset\n");
209 goto release_fw;
210 }
211
212release_fw:
213 release_firmware(fw);
214out:
215 return ret;
216}
217
218/**
219 * pil_get() - Load a peripheral into memory and take it out of reset
220 * @name: pointer to a string containing the name of the peripheral to load
221 *
222 * This function returns a pointer if it succeeds. If an error occurs an
223 * ERR_PTR is returned.
224 *
225 * If PIL is not enabled in the kernel, the value %NULL will be returned.
226 */
227void *pil_get(const char *name)
228{
229 int ret;
230 struct pil_device *pil;
231 struct pil_device *pil_d;
232 void *retval;
233
234 pil = retval = find_peripheral(name);
235 if (!pil)
236 return ERR_PTR(-ENODEV);
237
238 pil_d = find_peripheral(pil->depends_on);
239 if (pil_d) {
240 void *p = pil_get(pil_d->name);
241 if (IS_ERR(p))
242 return p;
243 }
244
245 mutex_lock(&pil->lock);
246 if (pil->count) {
247 pil->count++;
248 goto unlock;
249 }
250
251 ret = load_image(pil);
252 if (ret) {
253 retval = ERR_PTR(ret);
254 goto unlock;
255 }
256
257 pil->count++;
258unlock:
259 mutex_unlock(&pil->lock);
260 return retval;
261}
262EXPORT_SYMBOL(pil_get);
263
264/**
265 * pil_put() - Inform PIL the peripheral no longer needs to be active
266 * @peripheral_handle: pointer from a previous call to pil_get()
267 *
268 * This doesn't imply that a peripheral is shutdown or in reset since another
269 * driver could be using the peripheral.
270 */
271void pil_put(void *peripheral_handle)
272{
273 struct pil_device *pil_d;
274 struct pil_device *pil = peripheral_handle;
275 if (!pil || IS_ERR(pil)) {
276 WARN(1, "Invalid peripheral handle\n");
277 return;
278 }
279
280 mutex_lock(&pil->lock);
281 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
282 /* TODO: Peripheral shutdown support */
283 if (pil->count == 1)
284 goto unlock;
285 if (pil->count)
286 pil->count--;
287 if (pil->count == 0)
288 pil->ops->shutdown();
289unlock:
290 mutex_unlock(&pil->lock);
291
292 pil_d = find_peripheral(pil->depends_on);
293 if (pil_d)
294 pil_put(pil_d);
295}
296EXPORT_SYMBOL(pil_put);
297
298void pil_force_shutdown(const char *name)
299{
300 struct pil_device *pil;
301
302 pil = find_peripheral(name);
303 if (!pil)
304 return;
305
306 mutex_lock(&pil->lock);
307 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
308 pil->ops->shutdown();
309 mutex_unlock(&pil->lock);
310}
311EXPORT_SYMBOL(pil_force_shutdown);
312
313int pil_force_boot(const char *name)
314{
315 int ret = -EINVAL;
316 struct pil_device *pil;
317
318 pil = find_peripheral(name);
319 if (!pil)
320 return -EINVAL;
321
322 mutex_lock(&pil->lock);
323 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
324 ret = load_image(pil);
325 mutex_unlock(&pil->lock);
326
327 return ret;
328}
329EXPORT_SYMBOL(pil_force_boot);
330
331#ifdef CONFIG_DEBUG_FS
332int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
333{
334 filp->private_data = inode->i_private;
335 return 0;
336}
337
338static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
339 size_t cnt, loff_t *ppos)
340{
341 int r;
342 char buf[40];
343 struct pil_device *pil = filp->private_data;
344
345 mutex_lock(&pil->lock);
346 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
347 mutex_unlock(&pil->lock);
348 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
349}
350
351static ssize_t msm_pil_debugfs_write(struct file *filp,
352 const char __user *ubuf, size_t cnt, loff_t *ppos)
353{
354 struct pil_device *pil = filp->private_data;
355 char buf[4];
356
357 if (cnt > sizeof(buf))
358 return -EINVAL;
359
360 if (copy_from_user(&buf, ubuf, cnt))
361 return -EFAULT;
362
363 if (!strncmp(buf, "get", 3)) {
364 if (IS_ERR(pil_get(pil->name)))
365 return -EIO;
366 } else if (!strncmp(buf, "put", 3))
367 pil_put(pil);
368 else
369 return -EINVAL;
370
371 return cnt;
372}
373
374static const struct file_operations msm_pil_debugfs_fops = {
375 .open = msm_pil_debugfs_open,
376 .read = msm_pil_debugfs_read,
377 .write = msm_pil_debugfs_write,
378};
379
380static struct dentry *pil_base_dir;
381
382static int msm_pil_debugfs_init(void)
383{
384 pil_base_dir = debugfs_create_dir("pil", NULL);
385 if (!pil_base_dir) {
386 pil_base_dir = NULL;
387 return -ENOMEM;
388 }
389
390 return 0;
391}
392arch_initcall(msm_pil_debugfs_init);
393
394static int msm_pil_debugfs_add(struct pil_device *pil)
395{
396 if (!pil_base_dir)
397 return -ENOMEM;
398
399 if (!debugfs_create_file(pil->name, S_IRUGO | S_IWUSR, pil_base_dir,
400 pil, &msm_pil_debugfs_fops))
401 return -ENOMEM;
402 return 0;
403}
404#else
405static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
406#endif
407
408static int msm_pil_shutdown_at_boot(void)
409{
410 struct pil_device *pil;
411
412 mutex_lock(&pil_list_lock);
413 list_for_each_entry(pil, &pil_list, list)
414 pil->ops->shutdown();
415 mutex_unlock(&pil_list_lock);
416
417 return 0;
418}
419late_initcall(msm_pil_shutdown_at_boot);
420
421int msm_pil_add_device(struct pil_device *pil)
422{
423 int ret;
424 ret = platform_device_register(&pil->pdev);
425 if (ret)
426 return ret;
427
428 mutex_init(&pil->lock);
429
430 mutex_lock(&pil_list_lock);
431 list_add(&pil->list, &pil_list);
432 mutex_unlock(&pil_list_lock);
433
434 msm_pil_debugfs_add(pil);
435 return 0;
436}
437
438MODULE_LICENSE("GPL v2");
439MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");