Commit | Line | Data |
---|---|---|
355b0502 GKH |
1 | /* drivers/android/ram_console.c |
2 | * | |
3 | * Copyright (C) 2007-2008 Google, Inc. | |
4 | * | |
5 | * This software is licensed under the terms of the GNU General Public | |
6 | * License version 2, as published by the Free Software Foundation, and | |
7 | * may be copied, distributed, and modified under those terms. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <linux/console.h> | |
17 | #include <linux/init.h> | |
18 | #include <linux/module.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/proc_fs.h> | |
21 | #include <linux/string.h> | |
22 | #include <linux/uaccess.h> | |
23 | #include <linux/io.h> | |
cddb8751 | 24 | #include <linux/pstore_ram.h> |
a6707f83 | 25 | #include "ram_console.h" |
355b0502 | 26 | |
404a6043 | 27 | static struct persistent_ram_zone *ram_console_zone; |
f6ccb80e CC |
28 | static const char *bootinfo; |
29 | static size_t bootinfo_size; | |
355b0502 | 30 | |
355b0502 GKH |
31 | static void |
32 | ram_console_write(struct console *console, const char *s, unsigned int count) | |
33 | { | |
c672528a CC |
34 | struct persistent_ram_zone *prz = console->data; |
35 | persistent_ram_write(prz, s, count); | |
355b0502 GKH |
36 | } |
37 | ||
38 | static struct console ram_console = { | |
39 | .name = "ram", | |
40 | .write = ram_console_write, | |
74f6736e | 41 | .flags = CON_PRINTBUFFER | CON_ENABLED | CON_ANYTIME, |
355b0502 GKH |
42 | .index = -1, |
43 | }; | |
44 | ||
fdfc8089 SM |
45 | void ram_console_enable_console(int enabled) |
46 | { | |
47 | if (enabled) | |
48 | ram_console.flags |= CON_ENABLED; | |
49 | else | |
50 | ram_console.flags &= ~CON_ENABLED; | |
51 | } | |
52 | ||
06caa417 | 53 | static int __init ram_console_probe(struct platform_device *pdev) |
355b0502 | 54 | { |
a6707f83 | 55 | struct ram_console_platform_data *pdata = pdev->dev.platform_data; |
404a6043 | 56 | struct persistent_ram_zone *prz; |
355b0502 | 57 | |
404a6043 CC |
58 | prz = persistent_ram_init_ringbuffer(&pdev->dev, true); |
59 | if (IS_ERR(prz)) | |
60 | return PTR_ERR(prz); | |
355b0502 | 61 | |
c672528a | 62 | |
f6ccb80e CC |
63 | if (pdata) { |
64 | bootinfo = kstrdup(pdata->bootinfo, GFP_KERNEL); | |
65 | if (bootinfo) | |
66 | bootinfo_size = strlen(bootinfo); | |
67 | } | |
a6707f83 | 68 | |
404a6043 CC |
69 | ram_console_zone = prz; |
70 | ram_console.data = prz; | |
c672528a CC |
71 | |
72 | register_console(&ram_console); | |
c672528a | 73 | |
404a6043 | 74 | return 0; |
355b0502 GKH |
75 | } |
76 | ||
77 | static struct platform_driver ram_console_driver = { | |
355b0502 GKH |
78 | .driver = { |
79 | .name = "ram_console", | |
80 | }, | |
81 | }; | |
82 | ||
83 | static int __init ram_console_module_init(void) | |
84 | { | |
06caa417 | 85 | return platform_driver_probe(&ram_console_driver, ram_console_probe); |
355b0502 | 86 | } |
355b0502 | 87 | |
29673655 GKH |
88 | #ifndef CONFIG_PRINTK |
89 | #define dmesg_restrict 0 | |
90 | #endif | |
91 | ||
355b0502 GKH |
92 | static ssize_t ram_console_read_old(struct file *file, char __user *buf, |
93 | size_t len, loff_t *offset) | |
94 | { | |
95 | loff_t pos = *offset; | |
96 | ssize_t count; | |
404a6043 | 97 | struct persistent_ram_zone *prz = ram_console_zone; |
c672528a CC |
98 | size_t old_log_size = persistent_ram_old_size(prz); |
99 | const char *old_log = persistent_ram_old(prz); | |
f6ccb80e CC |
100 | char *str; |
101 | int ret; | |
102 | ||
dd099793 NK |
103 | if (dmesg_restrict && !capable(CAP_SYSLOG)) |
104 | return -EPERM; | |
105 | ||
f6ccb80e | 106 | /* Main last_kmsg log */ |
c672528a CC |
107 | if (pos < old_log_size) { |
108 | count = min(len, (size_t)(old_log_size - pos)); | |
109 | if (copy_to_user(buf, old_log + pos, count)) | |
f6ccb80e CC |
110 | return -EFAULT; |
111 | goto out; | |
112 | } | |
355b0502 | 113 | |
f6ccb80e | 114 | /* ECC correction notice */ |
c672528a CC |
115 | pos -= old_log_size; |
116 | count = persistent_ram_ecc_string(prz, NULL, 0); | |
f6ccb80e CC |
117 | if (pos < count) { |
118 | str = kmalloc(count, GFP_KERNEL); | |
119 | if (!str) | |
120 | return -ENOMEM; | |
c672528a | 121 | persistent_ram_ecc_string(prz, str, count + 1); |
f6ccb80e CC |
122 | count = min(len, (size_t)(count - pos)); |
123 | ret = copy_to_user(buf, str + pos, count); | |
124 | kfree(str); | |
125 | if (ret) | |
126 | return -EFAULT; | |
127 | goto out; | |
128 | } | |
355b0502 | 129 | |
f6ccb80e CC |
130 | /* Boot info passed through pdata */ |
131 | pos -= count; | |
132 | if (pos < bootinfo_size) { | |
133 | count = min(len, (size_t)(bootinfo_size - pos)); | |
134 | if (copy_to_user(buf, bootinfo + pos, count)) | |
135 | return -EFAULT; | |
136 | goto out; | |
137 | } | |
138 | ||
139 | /* EOF */ | |
140 | return 0; | |
355b0502 | 141 | |
f6ccb80e | 142 | out: |
355b0502 GKH |
143 | *offset += count; |
144 | return count; | |
145 | } | |
146 | ||
147 | static const struct file_operations ram_console_file_ops = { | |
148 | .owner = THIS_MODULE, | |
149 | .read = ram_console_read_old, | |
150 | }; | |
151 | ||
152 | static int __init ram_console_late_init(void) | |
153 | { | |
154 | struct proc_dir_entry *entry; | |
404a6043 | 155 | struct persistent_ram_zone *prz = ram_console_zone; |
355b0502 | 156 | |
3a21138d CC |
157 | if (!prz) |
158 | return 0; | |
159 | ||
c672528a | 160 | if (persistent_ram_old_size(prz) == 0) |
355b0502 | 161 | return 0; |
c672528a | 162 | |
355b0502 GKH |
163 | entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL); |
164 | if (!entry) { | |
165 | printk(KERN_ERR "ram_console: failed to create proc entry\n"); | |
c672528a | 166 | persistent_ram_free_old(prz); |
355b0502 GKH |
167 | return 0; |
168 | } | |
169 | ||
170 | entry->proc_fops = &ram_console_file_ops; | |
c672528a CC |
171 | entry->size = persistent_ram_old_size(prz) + |
172 | persistent_ram_ecc_string(prz, NULL, 0) + | |
173 | bootinfo_size; | |
174 | ||
355b0502 GKH |
175 | return 0; |
176 | } | |
177 | ||
355b0502 | 178 | late_initcall(ram_console_late_init); |
c672528a | 179 | postcore_initcall(ram_console_module_init); |