blob: 05b12bf95f22b1611a9e19e71cdfa0a72cde3915 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2011-2012, The Linux Foundation. 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/kernel.h>
14#include <linux/interrupt.h>
15#include <linux/reboot.h>
16#include <linux/workqueue.h>
17#include <linux/io.h>
18#include <linux/jiffies.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070019#include <linux/sched.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/stringify.h>
21#include <linux/delay.h>
22#include <linux/module.h>
23#include <linux/miscdevice.h>
24#include <linux/fs.h>
25#include <linux/mm.h>
26#include <linux/slab.h>
27#include <linux/poll.h>
28#include <linux/uaccess.h>
Devin Kim7d549cb2012-06-27 15:34:12 -070029#include <linux/mutex.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#include <asm-generic/poll.h>
32
33#include "ramdump.h"
34
35#define RAMDUMP_WAIT_MSECS 120000
36
Devin Kim7d549cb2012-06-27 15:34:12 -070037/*
38 * Head entry for linked list
39 */
40static LIST_HEAD(ramdump_list);
41static DEFINE_MUTEX(ramdump_mtx);
42
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043struct ramdump_device {
44 char name[256];
45
46 unsigned int data_ready;
47 unsigned int consumer_present;
48 int ramdump_status;
49
50 struct completion ramdump_complete;
51 struct miscdevice device;
52
53 wait_queue_head_t dump_wait_q;
54 int nsegments;
55 struct ramdump_segment *segments;
Devin Kim7d549cb2012-06-27 15:34:12 -070056 struct list_head list;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057};
58
59static int ramdump_open(struct inode *inode, struct file *filep)
60{
61 struct ramdump_device *rd_dev = container_of(filep->private_data,
62 struct ramdump_device, device);
63 rd_dev->consumer_present = 1;
64 rd_dev->ramdump_status = 0;
65 return 0;
66}
67
68static int ramdump_release(struct inode *inode, struct file *filep)
69{
70 struct ramdump_device *rd_dev = container_of(filep->private_data,
71 struct ramdump_device, device);
72 rd_dev->consumer_present = 0;
73 rd_dev->data_ready = 0;
74 complete(&rd_dev->ramdump_complete);
75 return 0;
76}
77
78static unsigned long offset_translate(loff_t user_offset,
79 struct ramdump_device *rd_dev, unsigned long *data_left)
80{
81 int i = 0;
82
83 for (i = 0; i < rd_dev->nsegments; i++)
84 if (user_offset >= rd_dev->segments[i].size)
85 user_offset -= rd_dev->segments[i].size;
86 else
87 break;
88
89 if (i == rd_dev->nsegments) {
90 pr_debug("Ramdump(%s): offset_translate returning zero\n",
91 rd_dev->name);
92 *data_left = 0;
93 return 0;
94 }
95
96 *data_left = rd_dev->segments[i].size - user_offset;
97
98 pr_debug("Ramdump(%s): Returning address: %llx, data_left = %ld\n",
99 rd_dev->name, rd_dev->segments[i].address + user_offset,
100 *data_left);
101
102 return rd_dev->segments[i].address + user_offset;
103}
104
105#define MAX_IOREMAP_SIZE SZ_1M
106
107static int ramdump_read(struct file *filep, char __user *buf, size_t count,
108 loff_t *pos)
109{
110 struct ramdump_device *rd_dev = container_of(filep->private_data,
111 struct ramdump_device, device);
112 void *device_mem = NULL;
113 unsigned long data_left = 0;
114 unsigned long addr = 0;
115 size_t copy_size = 0;
116 int ret = 0;
117
118 if (rd_dev->data_ready == 0) {
119 pr_err("Ramdump(%s): Read when there's no dump available!",
120 rd_dev->name);
121 return -EPIPE;
122 }
123
124 addr = offset_translate(*pos, rd_dev, &data_left);
125
126 /* EOF check */
127 if (data_left == 0) {
128 pr_debug("Ramdump(%s): Ramdump complete. %lld bytes read.",
129 rd_dev->name, *pos);
130 rd_dev->ramdump_status = 0;
131 ret = 0;
132 goto ramdump_done;
133 }
134
135 copy_size = min(count, (size_t)MAX_IOREMAP_SIZE);
136 copy_size = min((unsigned long)copy_size, data_left);
137 device_mem = ioremap_nocache(addr, copy_size);
138
139 if (device_mem == NULL) {
140 pr_err("Ramdump(%s): Unable to ioremap: addr %lx, size %x\n",
141 rd_dev->name, addr, copy_size);
142 rd_dev->ramdump_status = -1;
143 ret = -ENOMEM;
144 goto ramdump_done;
145 }
146
147 if (copy_to_user(buf, device_mem, copy_size)) {
148 pr_err("Ramdump(%s): Couldn't copy all data to user.",
149 rd_dev->name);
150 iounmap(device_mem);
151 rd_dev->ramdump_status = -1;
152 ret = -EFAULT;
153 goto ramdump_done;
154 }
155
156 iounmap(device_mem);
157 *pos += copy_size;
158
159 pr_debug("Ramdump(%s): Read %d bytes from address %lx.",
160 rd_dev->name, copy_size, addr);
161
162 return copy_size;
163
164ramdump_done:
165 rd_dev->data_ready = 0;
166 *pos = 0;
167 complete(&rd_dev->ramdump_complete);
168 return ret;
169}
170
171static unsigned int ramdump_poll(struct file *filep,
172 struct poll_table_struct *wait)
173{
174 struct ramdump_device *rd_dev = container_of(filep->private_data,
175 struct ramdump_device, device);
176 unsigned int mask = 0;
177
178 if (rd_dev->data_ready)
179 mask |= (POLLIN | POLLRDNORM);
180
181 poll_wait(filep, &rd_dev->dump_wait_q, wait);
182 return mask;
183}
184
185const struct file_operations ramdump_file_ops = {
186 .open = ramdump_open,
187 .release = ramdump_release,
188 .read = ramdump_read,
189 .poll = ramdump_poll
190};
191
192void *create_ramdump_device(const char *dev_name)
193{
194 int ret;
195 struct ramdump_device *rd_dev;
Devin Kim7d549cb2012-06-27 15:34:12 -0700196 char name[256];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197
198 if (!dev_name) {
199 pr_err("%s: Invalid device name.\n", __func__);
200 return NULL;
201 }
202
Devin Kim7d549cb2012-06-27 15:34:12 -0700203 snprintf(name, ARRAY_SIZE(name), "ramdump_%s", dev_name);
204 mutex_lock(&ramdump_mtx);
205 list_for_each_entry(rd_dev, &ramdump_list, list) {
206 if (!strcmp(rd_dev->device.name, name)) {
207 mutex_unlock(&ramdump_mtx);
208 pr_warning("%s: already exist: %s",
209 __func__, name);
210 return (void *)rd_dev;
211 }
212 }
213 mutex_unlock(&ramdump_mtx);
214
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 rd_dev = kzalloc(sizeof(struct ramdump_device), GFP_KERNEL);
216
217 if (!rd_dev) {
218 pr_err("%s: Couldn't alloc space for ramdump device!",
219 __func__);
220 return NULL;
221 }
Devin Kim7d549cb2012-06-27 15:34:12 -0700222 INIT_LIST_HEAD(&rd_dev->list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700223
Matt Wagantallf9563142011-11-10 19:04:51 -0800224 snprintf(rd_dev->name, ARRAY_SIZE(rd_dev->name), "ramdump_%s",
225 dev_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226
227 init_completion(&rd_dev->ramdump_complete);
228
229 rd_dev->device.minor = MISC_DYNAMIC_MINOR;
230 rd_dev->device.name = rd_dev->name;
231 rd_dev->device.fops = &ramdump_file_ops;
232
233 init_waitqueue_head(&rd_dev->dump_wait_q);
234
235 ret = misc_register(&rd_dev->device);
236
237 if (ret) {
238 pr_err("%s: misc_register failed for %s (%d)", __func__,
239 dev_name, ret);
240 kfree(rd_dev);
241 return NULL;
242 }
243
Devin Kim7d549cb2012-06-27 15:34:12 -0700244 mutex_lock(&ramdump_mtx);
245 list_add(&rd_dev->list, &ramdump_list);
246 mutex_unlock(&ramdump_mtx);
247
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248 return (void *)rd_dev;
249}
250
Stephen Boydb9fab142012-06-20 15:33:32 -0700251void destroy_ramdump_device(void *dev)
252{
253 struct ramdump_device *rd_dev = dev;
254
255 if (IS_ERR_OR_NULL(rd_dev))
256 return;
257
258 misc_deregister(&rd_dev->device);
259 kfree(rd_dev);
260}
261
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262int do_ramdump(void *handle, struct ramdump_segment *segments,
263 int nsegments)
264{
265 int ret, i;
266 struct ramdump_device *rd_dev = (struct ramdump_device *)handle;
267
268 if (!rd_dev->consumer_present) {
269 pr_err("Ramdump(%s): No consumers. Aborting..\n", rd_dev->name);
270 return -EPIPE;
271 }
272
273 for (i = 0; i < nsegments; i++)
274 segments[i].size = PAGE_ALIGN(segments[i].size);
275
276 rd_dev->segments = segments;
277 rd_dev->nsegments = nsegments;
278
279 rd_dev->data_ready = 1;
280 rd_dev->ramdump_status = -1;
281
282 INIT_COMPLETION(rd_dev->ramdump_complete);
283
284 /* Tell userspace that the data is ready */
285 wake_up(&rd_dev->dump_wait_q);
286
287 /* Wait (with a timeout) to let the ramdump complete */
288 ret = wait_for_completion_timeout(&rd_dev->ramdump_complete,
289 msecs_to_jiffies(RAMDUMP_WAIT_MSECS));
290
291 if (!ret) {
292 pr_err("Ramdump(%s): Timed out waiting for userspace.\n",
293 rd_dev->name);
294 ret = -EPIPE;
295 } else
296 ret = (rd_dev->ramdump_status == 0) ? 0 : -EPIPE;
297
298 rd_dev->data_ready = 0;
299 return ret;
300}