blob: 1969efc4778c4dcba7391868665faeca28368991 [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 }
Stephen Boyd96a9f902011-07-18 18:43:00 -0700181 if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
182 sizeof(struct elf32_hdr) > fw->size) {
183 dev_err(&pil->pdev.dev, "Program headers not within mdt\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 ret = -EIO;
185 goto release_fw;
186 }
187
188 ret = pil->ops->init_image(fw->data, fw->size);
189 if (ret) {
190 dev_err(&pil->pdev.dev, "Invalid firmware metadata\n");
191 goto release_fw;
192 }
193
Stephen Boydc9753e12011-07-13 17:58:48 -0700194 phdr = (const struct elf32_phdr *)(fw->data + sizeof(struct elf32_hdr));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
196 if (!segment_is_loadable(phdr))
197 continue;
198
199 ret = load_segment(phdr, i, pil);
200 if (ret) {
201 dev_err(&pil->pdev.dev, "Failed to load segment %d\n",
202 i);
203 goto release_fw;
204 }
205 }
206
207 ret = pil->ops->auth_and_reset();
208 if (ret) {
209 dev_err(&pil->pdev.dev, "Failed to bring out of reset\n");
210 goto release_fw;
211 }
212
213release_fw:
214 release_firmware(fw);
215out:
216 return ret;
217}
218
219/**
220 * pil_get() - Load a peripheral into memory and take it out of reset
221 * @name: pointer to a string containing the name of the peripheral to load
222 *
223 * This function returns a pointer if it succeeds. If an error occurs an
224 * ERR_PTR is returned.
225 *
226 * If PIL is not enabled in the kernel, the value %NULL will be returned.
227 */
228void *pil_get(const char *name)
229{
230 int ret;
231 struct pil_device *pil;
232 struct pil_device *pil_d;
233 void *retval;
234
235 pil = retval = find_peripheral(name);
236 if (!pil)
237 return ERR_PTR(-ENODEV);
238
239 pil_d = find_peripheral(pil->depends_on);
240 if (pil_d) {
241 void *p = pil_get(pil_d->name);
242 if (IS_ERR(p))
243 return p;
244 }
245
246 mutex_lock(&pil->lock);
247 if (pil->count) {
248 pil->count++;
249 goto unlock;
250 }
251
252 ret = load_image(pil);
253 if (ret) {
254 retval = ERR_PTR(ret);
255 goto unlock;
256 }
257
258 pil->count++;
259unlock:
260 mutex_unlock(&pil->lock);
261 return retval;
262}
263EXPORT_SYMBOL(pil_get);
264
265/**
266 * pil_put() - Inform PIL the peripheral no longer needs to be active
267 * @peripheral_handle: pointer from a previous call to pil_get()
268 *
269 * This doesn't imply that a peripheral is shutdown or in reset since another
270 * driver could be using the peripheral.
271 */
272void pil_put(void *peripheral_handle)
273{
274 struct pil_device *pil_d;
275 struct pil_device *pil = peripheral_handle;
Stephen Boyd545e8362011-09-13 15:04:38 -0700276 if (!pil || IS_ERR(pil))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277 return;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278
279 mutex_lock(&pil->lock);
280 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
281 /* TODO: Peripheral shutdown support */
282 if (pil->count == 1)
283 goto unlock;
284 if (pil->count)
285 pil->count--;
286 if (pil->count == 0)
287 pil->ops->shutdown();
288unlock:
289 mutex_unlock(&pil->lock);
290
291 pil_d = find_peripheral(pil->depends_on);
292 if (pil_d)
293 pil_put(pil_d);
294}
295EXPORT_SYMBOL(pil_put);
296
297void pil_force_shutdown(const char *name)
298{
299 struct pil_device *pil;
300
301 pil = find_peripheral(name);
302 if (!pil)
303 return;
304
305 mutex_lock(&pil->lock);
306 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
307 pil->ops->shutdown();
308 mutex_unlock(&pil->lock);
309}
310EXPORT_SYMBOL(pil_force_shutdown);
311
312int pil_force_boot(const char *name)
313{
314 int ret = -EINVAL;
315 struct pil_device *pil;
316
317 pil = find_peripheral(name);
318 if (!pil)
319 return -EINVAL;
320
321 mutex_lock(&pil->lock);
322 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
323 ret = load_image(pil);
324 mutex_unlock(&pil->lock);
325
326 return ret;
327}
328EXPORT_SYMBOL(pil_force_boot);
329
330#ifdef CONFIG_DEBUG_FS
331int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
332{
333 filp->private_data = inode->i_private;
334 return 0;
335}
336
337static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
338 size_t cnt, loff_t *ppos)
339{
340 int r;
341 char buf[40];
342 struct pil_device *pil = filp->private_data;
343
344 mutex_lock(&pil->lock);
345 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
346 mutex_unlock(&pil->lock);
347 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
348}
349
350static ssize_t msm_pil_debugfs_write(struct file *filp,
351 const char __user *ubuf, size_t cnt, loff_t *ppos)
352{
353 struct pil_device *pil = filp->private_data;
354 char buf[4];
355
356 if (cnt > sizeof(buf))
357 return -EINVAL;
358
359 if (copy_from_user(&buf, ubuf, cnt))
360 return -EFAULT;
361
362 if (!strncmp(buf, "get", 3)) {
363 if (IS_ERR(pil_get(pil->name)))
364 return -EIO;
365 } else if (!strncmp(buf, "put", 3))
366 pil_put(pil);
367 else
368 return -EINVAL;
369
370 return cnt;
371}
372
373static const struct file_operations msm_pil_debugfs_fops = {
374 .open = msm_pil_debugfs_open,
375 .read = msm_pil_debugfs_read,
376 .write = msm_pil_debugfs_write,
377};
378
379static struct dentry *pil_base_dir;
380
381static int msm_pil_debugfs_init(void)
382{
383 pil_base_dir = debugfs_create_dir("pil", NULL);
384 if (!pil_base_dir) {
385 pil_base_dir = NULL;
386 return -ENOMEM;
387 }
388
389 return 0;
390}
391arch_initcall(msm_pil_debugfs_init);
392
393static int msm_pil_debugfs_add(struct pil_device *pil)
394{
395 if (!pil_base_dir)
396 return -ENOMEM;
397
398 if (!debugfs_create_file(pil->name, S_IRUGO | S_IWUSR, pil_base_dir,
399 pil, &msm_pil_debugfs_fops))
400 return -ENOMEM;
401 return 0;
402}
403#else
404static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
405#endif
406
407static int msm_pil_shutdown_at_boot(void)
408{
409 struct pil_device *pil;
410
411 mutex_lock(&pil_list_lock);
412 list_for_each_entry(pil, &pil_list, list)
413 pil->ops->shutdown();
414 mutex_unlock(&pil_list_lock);
415
416 return 0;
417}
418late_initcall(msm_pil_shutdown_at_boot);
419
420int msm_pil_add_device(struct pil_device *pil)
421{
422 int ret;
423 ret = platform_device_register(&pil->pdev);
424 if (ret)
425 return ret;
426
427 mutex_init(&pil->lock);
428
429 mutex_lock(&pil_list_lock);
430 list_add(&pil->list, &pil_list);
431 mutex_unlock(&pil_list_lock);
432
433 msm_pil_debugfs_add(pil);
434 return 0;
435}
436
437MODULE_LICENSE("GPL v2");
438MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");