staging: android: ram_console: split out persistent ram
[linux-block.git] / drivers / staging / android / persistent_ram.c
CommitLineData
c672528a
CC
1/*
2 * Copyright (C) 2012 Google, Inc.
3 *
4 * This software is licensed under the terms of the GNU General Public
5 * License version 2, as published by the Free Software Foundation, and
6 * may be copied, distributed, and modified under those terms.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include <linux/errno.h>
16#include <linux/kernel.h>
17#include <linux/init.h>
18#include <linux/io.h>
19#include <linux/slab.h>
20#include "persistent_ram.h"
21
22#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
23#include <linux/rslib.h>
24#endif
25
26struct persistent_ram_buffer {
27 uint32_t sig;
28 uint32_t start;
29 uint32_t size;
30 uint8_t data[0];
31};
32
33#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
34
35static LIST_HEAD(zone_list);
36
37#define ECC_BLOCK_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE
38#define ECC_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE
39#define ECC_SYMSIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE
40#define ECC_POLY CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL
41
42#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
43static void persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
44 uint8_t *data, size_t len, uint8_t *ecc)
45{
46 int i;
47 uint16_t par[ECC_SIZE];
48 /* Initialize the parity buffer */
49 memset(par, 0, sizeof(par));
50 encode_rs8(prz->rs_decoder, data, len, par, 0);
51 for (i = 0; i < ECC_SIZE; i++)
52 ecc[i] = par[i];
53}
54
55static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
56 void *data, size_t len, uint8_t *ecc)
57{
58 int i;
59 uint16_t par[ECC_SIZE];
60 for (i = 0; i < ECC_SIZE; i++)
61 par[i] = ecc[i];
62 return decode_rs8(prz->rs_decoder, data, par, len,
63 NULL, 0, NULL, 0, NULL);
64}
65#endif
66
67static void persistent_ram_update(struct persistent_ram_zone *prz,
68 const void *s, unsigned int count)
69{
70 struct persistent_ram_buffer *buffer = prz->buffer;
71#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
72 uint8_t *buffer_end = buffer->data + prz->buffer_size;
73 uint8_t *block;
74 uint8_t *par;
75 int size = ECC_BLOCK_SIZE;
76#endif
77 memcpy(buffer->data + buffer->start, s, count);
78#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
79 block = buffer->data + (buffer->start & ~(ECC_BLOCK_SIZE - 1));
80 par = prz->par_buffer +
81 (buffer->start / ECC_BLOCK_SIZE) * ECC_SIZE;
82 do {
83 if (block + ECC_BLOCK_SIZE > buffer_end)
84 size = buffer_end - block;
85 persistent_ram_encode_rs8(prz, block, size, par);
86 block += ECC_BLOCK_SIZE;
87 par += ECC_SIZE;
88 } while (block < buffer->data + buffer->start + count);
89#endif
90}
91
92static void persistent_ram_update_header(struct persistent_ram_zone *prz)
93{
94#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
95 struct persistent_ram_buffer *buffer = prz->buffer;
96
97 persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
98 prz->par_header);
99#endif
100}
101
102static void __init
103persistent_ram_save_old(struct persistent_ram_zone *prz)
104{
105 struct persistent_ram_buffer *buffer = prz->buffer;
106 size_t old_log_size = buffer->size;
107 char *dest;
108#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
109 uint8_t *block;
110 uint8_t *par;
111
112 block = buffer->data;
113 par = prz->par_buffer;
114 while (block < buffer->data + buffer->size) {
115 int numerr;
116 int size = ECC_BLOCK_SIZE;
117 if (block + size > buffer->data + prz->buffer_size)
118 size = buffer->data + prz->buffer_size - block;
119 numerr = persistent_ram_decode_rs8(prz, block, size, par);
120 if (numerr > 0) {
121#if 0
122 pr_info("persistent_ram: error in block %p, %d\n",
123 block, numerr);
124#endif
125 prz->corrected_bytes += numerr;
126 } else if (numerr < 0) {
127#if 0
128 pr_info("persistent_ram: uncorrectable error in block %p\n",
129 block);
130#endif
131 prz->bad_blocks++;
132 }
133 block += ECC_BLOCK_SIZE;
134 par += ECC_SIZE;
135 }
136#endif
137
138 dest = kmalloc(old_log_size, GFP_KERNEL);
139 if (dest == NULL) {
140 pr_err("persistent_ram: failed to allocate buffer\n");
141 return;
142 }
143
144 prz->old_log = dest;
145 prz->old_log_size = old_log_size;
146 memcpy(prz->old_log,
147 &buffer->data[buffer->start], buffer->size - buffer->start);
148 memcpy(prz->old_log + buffer->size - buffer->start,
149 &buffer->data[0], buffer->start);
150}
151
152int persistent_ram_write(struct persistent_ram_zone *prz,
153 const void *s, unsigned int count)
154{
155 int rem;
156 int c = count;
157 struct persistent_ram_buffer *buffer = prz->buffer;
158
159 if (c > prz->buffer_size) {
160 s += c - prz->buffer_size;
161 c = prz->buffer_size;
162 }
163 rem = prz->buffer_size - buffer->start;
164 if (rem < c) {
165 persistent_ram_update(prz, s, rem);
166 s += rem;
167 c -= rem;
168 buffer->start = 0;
169 buffer->size = prz->buffer_size;
170 }
171 persistent_ram_update(prz, s, c);
172
173 buffer->start += c;
174 if (buffer->size < prz->buffer_size)
175 buffer->size += c;
176 persistent_ram_update_header(prz);
177
178 return count;
179}
180
181ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
182 char *str, size_t len)
183{
184#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
185 ssize_t ret;
186
187 if (prz->corrected_bytes || prz->bad_blocks)
188 ret = snprintf(str, len, ""
189 "\n%d Corrected bytes, %d unrecoverable blocks\n",
190 prz->corrected_bytes, prz->bad_blocks);
191 else
192 ret = snprintf(str, len, "\nNo errors detected\n");
193
194 return ret;
195#else
196 return 0;
197#endif
198}
199
200size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
201{
202 return prz->old_log_size;
203}
204
205void *persistent_ram_old(struct persistent_ram_zone *prz)
206{
207 return prz->old_log;
208}
209
210void persistent_ram_free_old(struct persistent_ram_zone *prz)
211{
212 kfree(prz->old_log);
213 prz->old_log = NULL;
214 prz->old_log_size = 0;
215}
216
217static int __init __persistent_ram_init(struct persistent_ram_zone *prz,
218 void __iomem *mem, size_t buffer_size)
219{
220 struct persistent_ram_buffer *buffer = mem;
221#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
222 int numerr;
223#endif
224
225 INIT_LIST_HEAD(&prz->node);
226
227 prz->buffer = buffer;
228 prz->buffer_size = buffer_size - sizeof(struct persistent_ram_buffer);
229
230 if (prz->buffer_size > buffer_size) {
231 pr_err("persistent_ram: buffer %p, invalid size %zu, datasize %zu\n",
232 buffer, buffer_size, prz->buffer_size);
233 return -EINVAL;
234 }
235
236#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION
237 prz->buffer_size -= (DIV_ROUND_UP(prz->buffer_size,
238 ECC_BLOCK_SIZE) + 1) * ECC_SIZE;
239
240 if (prz->buffer_size > buffer_size) {
241 pr_err("persistent_ram: buffer %p, invalid size %zu, non-ecc datasize %zu\n",
242 buffer, buffer_size, prz->buffer_size);
243 return -EINVAL;
244 }
245
246 prz->par_buffer = buffer->data + prz->buffer_size;
247 prz->par_header = prz->par_buffer +
248 DIV_ROUND_UP(prz->buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE;
249
250
251 /* first consecutive root is 0
252 * primitive element to generate roots = 1
253 */
254 prz->rs_decoder = init_rs(ECC_SYMSIZE, ECC_POLY, 0, 1, ECC_SIZE);
255 if (prz->rs_decoder == NULL) {
256 pr_info("persistent_ram: init_rs failed\n");
257 return -EINVAL;
258 }
259
260 prz->corrected_bytes = 0;
261 prz->bad_blocks = 0;
262
263 numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
264 prz->par_header);
265 if (numerr > 0) {
266 pr_info("persistent_ram: error in header, %d\n", numerr);
267 prz->corrected_bytes += numerr;
268 } else if (numerr < 0) {
269 pr_info("persistent_ram: uncorrectable error in header\n");
270 prz->bad_blocks++;
271 }
272#endif
273
274 if (buffer->sig == PERSISTENT_RAM_SIG) {
275 if (buffer->size > prz->buffer_size
276 || buffer->start > buffer->size)
277 pr_info("persistent_ram: found existing invalid buffer, size %d, start %d\n",
278 buffer->size, buffer->start);
279 else {
280 pr_info("persistent_ram: found existing buffer, size %d, start %d\n",
281 buffer->size, buffer->start);
282 persistent_ram_save_old(prz);
283 }
284 } else {
285 pr_info("persistent_ram: no valid data in buffer (sig = 0x%08x)\n",
286 buffer->sig);
287 }
288
289 buffer->sig = PERSISTENT_RAM_SIG;
290 buffer->start = 0;
291 buffer->size = 0;
292
293 list_add_tail(&prz->node, &zone_list);
294
295 return 0;
296}
297
298int __init persistent_ram_init_ringbuffer(struct persistent_ram_zone *prz,
299 void __iomem *mem, size_t buffer_size)
300{
301 return __persistent_ram_init(prz, mem, buffer_size);
302}