blob: 03d6fc236436f44a7fac8fa9df861ba44dc3eec6 [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;
276 if (!pil || IS_ERR(pil)) {
277 WARN(1, "Invalid peripheral handle\n");
278 return;
279 }
280
281 mutex_lock(&pil->lock);
282 WARN(!pil->count, "%s: Reference count mismatch\n", __func__);
283 /* TODO: Peripheral shutdown support */
284 if (pil->count == 1)
285 goto unlock;
286 if (pil->count)
287 pil->count--;
288 if (pil->count == 0)
289 pil->ops->shutdown();
290unlock:
291 mutex_unlock(&pil->lock);
292
293 pil_d = find_peripheral(pil->depends_on);
294 if (pil_d)
295 pil_put(pil_d);
296}
297EXPORT_SYMBOL(pil_put);
298
299void pil_force_shutdown(const char *name)
300{
301 struct pil_device *pil;
302
303 pil = find_peripheral(name);
304 if (!pil)
305 return;
306
307 mutex_lock(&pil->lock);
308 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
309 pil->ops->shutdown();
310 mutex_unlock(&pil->lock);
311}
312EXPORT_SYMBOL(pil_force_shutdown);
313
314int pil_force_boot(const char *name)
315{
316 int ret = -EINVAL;
317 struct pil_device *pil;
318
319 pil = find_peripheral(name);
320 if (!pil)
321 return -EINVAL;
322
323 mutex_lock(&pil->lock);
324 if (!WARN(!pil->count, "%s: Reference count mismatch\n", __func__))
325 ret = load_image(pil);
326 mutex_unlock(&pil->lock);
327
328 return ret;
329}
330EXPORT_SYMBOL(pil_force_boot);
331
332#ifdef CONFIG_DEBUG_FS
333int msm_pil_debugfs_open(struct inode *inode, struct file *filp)
334{
335 filp->private_data = inode->i_private;
336 return 0;
337}
338
339static ssize_t msm_pil_debugfs_read(struct file *filp, char __user *ubuf,
340 size_t cnt, loff_t *ppos)
341{
342 int r;
343 char buf[40];
344 struct pil_device *pil = filp->private_data;
345
346 mutex_lock(&pil->lock);
347 r = snprintf(buf, sizeof(buf), "%d\n", pil->count);
348 mutex_unlock(&pil->lock);
349 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
350}
351
352static ssize_t msm_pil_debugfs_write(struct file *filp,
353 const char __user *ubuf, size_t cnt, loff_t *ppos)
354{
355 struct pil_device *pil = filp->private_data;
356 char buf[4];
357
358 if (cnt > sizeof(buf))
359 return -EINVAL;
360
361 if (copy_from_user(&buf, ubuf, cnt))
362 return -EFAULT;
363
364 if (!strncmp(buf, "get", 3)) {
365 if (IS_ERR(pil_get(pil->name)))
366 return -EIO;
367 } else if (!strncmp(buf, "put", 3))
368 pil_put(pil);
369 else
370 return -EINVAL;
371
372 return cnt;
373}
374
375static const struct file_operations msm_pil_debugfs_fops = {
376 .open = msm_pil_debugfs_open,
377 .read = msm_pil_debugfs_read,
378 .write = msm_pil_debugfs_write,
379};
380
381static struct dentry *pil_base_dir;
382
383static int msm_pil_debugfs_init(void)
384{
385 pil_base_dir = debugfs_create_dir("pil", NULL);
386 if (!pil_base_dir) {
387 pil_base_dir = NULL;
388 return -ENOMEM;
389 }
390
391 return 0;
392}
393arch_initcall(msm_pil_debugfs_init);
394
395static int msm_pil_debugfs_add(struct pil_device *pil)
396{
397 if (!pil_base_dir)
398 return -ENOMEM;
399
400 if (!debugfs_create_file(pil->name, S_IRUGO | S_IWUSR, pil_base_dir,
401 pil, &msm_pil_debugfs_fops))
402 return -ENOMEM;
403 return 0;
404}
405#else
406static int msm_pil_debugfs_add(struct pil_device *pil) { return 0; }
407#endif
408
409static int msm_pil_shutdown_at_boot(void)
410{
411 struct pil_device *pil;
412
413 mutex_lock(&pil_list_lock);
414 list_for_each_entry(pil, &pil_list, list)
415 pil->ops->shutdown();
416 mutex_unlock(&pil_list_lock);
417
418 return 0;
419}
420late_initcall(msm_pil_shutdown_at_boot);
421
422int msm_pil_add_device(struct pil_device *pil)
423{
424 int ret;
425 ret = platform_device_register(&pil->pdev);
426 if (ret)
427 return ret;
428
429 mutex_init(&pil->lock);
430
431 mutex_lock(&pil_list_lock);
432 list_add(&pil->list, &pil_list);
433 mutex_unlock(&pil_list_lock);
434
435 msm_pil_debugfs_add(pil);
436 return 0;
437}
438
439MODULE_LICENSE("GPL v2");
440MODULE_DESCRIPTION("Load peripheral images and bring peripherals out of reset");