ram_core.c 13.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright (C) 2012 Google, Inc.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

F
Fabian Frederick 已提交
15 16
#define pr_fmt(fmt) "persistent_ram: " fmt

17 18
#include <linux/device.h>
#include <linux/err.h>
19 20 21
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
M
Mark Salyzyn 已提交
22
#include <linux/kernel.h>
23 24
#include <linux/list.h>
#include <linux/memblock.h>
M
Mark Salyzyn 已提交
25
#include <linux/pstore_ram.h>
26
#include <linux/rslib.h>
27
#include <linux/slab.h>
M
Mark Salyzyn 已提交
28
#include <linux/uaccess.h>
29
#include <linux/vmalloc.h>
30
#include <asm/page.h>
31 32 33

struct persistent_ram_buffer {
	uint32_t    sig;
34 35
	atomic_t    start;
	atomic_t    size;
36 37 38 39 40
	uint8_t     data[0];
};

#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */

41 42 43 44 45 46 47 48 49 50
static inline size_t buffer_size(struct persistent_ram_zone *prz)
{
	return atomic_read(&prz->buffer->size);
}

static inline size_t buffer_start(struct persistent_ram_zone *prz)
{
	return atomic_read(&prz->buffer->start);
}

51 52 53
static DEFINE_RAW_SPINLOCK(buffer_lock);

/* increase and wrap the start pointer, returning the old value */
54
static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
55 56 57 58 59 60 61 62 63
{
	int old;
	int new;
	unsigned long flags;

	raw_spin_lock_irqsave(&buffer_lock, flags);

	old = atomic_read(&prz->buffer->start);
	new = old + a;
64
	while (unlikely(new >= prz->buffer_size))
65 66 67 68 69 70 71 72 73
		new -= prz->buffer_size;
	atomic_set(&prz->buffer->start, new);

	raw_spin_unlock_irqrestore(&buffer_lock, flags);

	return old;
}

/* increase the size counter until it hits the max size */
74
static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
{
	size_t old;
	size_t new;
	unsigned long flags;

	raw_spin_lock_irqsave(&buffer_lock, flags);

	old = atomic_read(&prz->buffer->size);
	if (old == prz->buffer_size)
		goto exit;

	new = old + a;
	if (new > prz->buffer_size)
		new = prz->buffer_size;
	atomic_set(&prz->buffer->size, new);

exit:
	raw_spin_unlock_irqrestore(&buffer_lock, flags);
}

95
static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
96 97 98
	uint8_t *data, size_t len, uint8_t *ecc)
{
	int i;
99
	uint16_t par[prz->ecc_info.ecc_size];
100

101 102 103
	/* Initialize the parity buffer */
	memset(par, 0, sizeof(par));
	encode_rs8(prz->rs_decoder, data, len, par, 0);
104
	for (i = 0; i < prz->ecc_info.ecc_size; i++)
105 106 107 108 109 110 111
		ecc[i] = par[i];
}

static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
	void *data, size_t len, uint8_t *ecc)
{
	int i;
112
	uint16_t par[prz->ecc_info.ecc_size];
113

114
	for (i = 0; i < prz->ecc_info.ecc_size; i++)
115 116 117 118 119
		par[i] = ecc[i];
	return decode_rs8(prz->rs_decoder, data, par, len,
				NULL, 0, NULL, 0, NULL);
}

120
static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
121
	unsigned int start, unsigned int count)
122 123 124 125 126
{
	struct persistent_ram_buffer *buffer = prz->buffer;
	uint8_t *buffer_end = buffer->data + prz->buffer_size;
	uint8_t *block;
	uint8_t *par;
127 128 129
	int ecc_block_size = prz->ecc_info.block_size;
	int ecc_size = prz->ecc_info.ecc_size;
	int size = ecc_block_size;
130

131
	if (!ecc_size)
132 133
		return;

134
	block = buffer->data + (start & ~(ecc_block_size - 1));
135
	par = prz->par_buffer + (start / ecc_block_size) * ecc_size;
136

137
	do {
138
		if (block + ecc_block_size > buffer_end)
139 140
			size = buffer_end - block;
		persistent_ram_encode_rs8(prz, block, size, par);
141 142
		block += ecc_block_size;
		par += ecc_size;
143
	} while (block < buffer->data + start + count);
144 145
}

146
static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
147 148 149
{
	struct persistent_ram_buffer *buffer = prz->buffer;

150
	if (!prz->ecc_info.ecc_size)
151 152
		return;

153 154 155 156
	persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
				  prz->par_header);
}

157
static void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
158 159 160 161 162
{
	struct persistent_ram_buffer *buffer = prz->buffer;
	uint8_t *block;
	uint8_t *par;

163
	if (!prz->ecc_info.ecc_size)
164 165
		return;

166 167
	block = buffer->data;
	par = prz->par_buffer;
168
	while (block < buffer->data + buffer_size(prz)) {
169
		int numerr;
170
		int size = prz->ecc_info.block_size;
171 172 173 174
		if (block + size > buffer->data + prz->buffer_size)
			size = buffer->data + prz->buffer_size - block;
		numerr = persistent_ram_decode_rs8(prz, block, size, par);
		if (numerr > 0) {
F
Fabian Frederick 已提交
175
			pr_devel("error in block %p, %d\n", block, numerr);
176 177
			prz->corrected_bytes += numerr;
		} else if (numerr < 0) {
F
Fabian Frederick 已提交
178
			pr_devel("uncorrectable error in block %p\n", block);
179 180
			prz->bad_blocks++;
		}
181 182
		block += prz->ecc_info.block_size;
		par += prz->ecc_info.ecc_size;
183 184 185
	}
}

186
static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
187
				   struct persistent_ram_ecc_info *ecc_info)
188 189 190 191
{
	int numerr;
	struct persistent_ram_buffer *buffer = prz->buffer;
	int ecc_blocks;
192
	size_t ecc_total;
193

194
	if (!ecc_info || !ecc_info->ecc_size)
195 196
		return 0;

197 198 199 200
	prz->ecc_info.block_size = ecc_info->block_size ?: 128;
	prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16;
	prz->ecc_info.symsize = ecc_info->symsize ?: 8;
	prz->ecc_info.poly = ecc_info->poly ?: 0x11d;
201

202 203 204 205
	ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size,
				  prz->ecc_info.block_size +
				  prz->ecc_info.ecc_size);
	ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size;
206 207
	if (ecc_total >= prz->buffer_size) {
		pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n",
208 209
		       __func__, prz->ecc_info.ecc_size,
		       ecc_total, prz->buffer_size);
210 211 212
		return -EINVAL;
	}

213
	prz->buffer_size -= ecc_total;
214
	prz->par_buffer = buffer->data + prz->buffer_size;
215 216
	prz->par_header = prz->par_buffer +
			  ecc_blocks * prz->ecc_info.ecc_size;
217 218 219 220 221

	/*
	 * first consecutive root is 0
	 * primitive element to generate roots = 1
	 */
222 223
	prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly,
				  0, 1, prz->ecc_info.ecc_size);
224
	if (prz->rs_decoder == NULL) {
F
Fabian Frederick 已提交
225
		pr_info("init_rs failed\n");
226
		return -EINVAL;
227
	}
228 229 230 231 232 233 234

	prz->corrected_bytes = 0;
	prz->bad_blocks = 0;

	numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
					   prz->par_header);
	if (numerr > 0) {
F
Fabian Frederick 已提交
235
		pr_info("error in header, %d\n", numerr);
236 237
		prz->corrected_bytes += numerr;
	} else if (numerr < 0) {
F
Fabian Frederick 已提交
238
		pr_info("uncorrectable error in header\n");
239 240 241 242 243 244 245 246 247 248 249
		prz->bad_blocks++;
	}

	return 0;
}

ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
	char *str, size_t len)
{
	ssize_t ret;

250 251 252
	if (!prz->ecc_info.ecc_size)
		return 0;

253 254 255 256 257 258 259 260 261 262
	if (prz->corrected_bytes || prz->bad_blocks)
		ret = snprintf(str, len, ""
			"\n%d Corrected bytes, %d unrecoverable blocks\n",
			prz->corrected_bytes, prz->bad_blocks);
	else
		ret = snprintf(str, len, "\nNo errors detected\n");

	return ret;
}

263
static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
264
	const void *s, unsigned int start, unsigned int count)
265 266
{
	struct persistent_ram_buffer *buffer = prz->buffer;
267
	memcpy_toio(buffer->data + start, s, count);
268
	persistent_ram_update_ecc(prz, start, count);
269 270
}

M
Mark Salyzyn 已提交
271 272 273 274 275 276 277 278 279 280
static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz,
	const void __user *s, unsigned int start, unsigned int count)
{
	struct persistent_ram_buffer *buffer = prz->buffer;
	int ret = unlikely(__copy_from_user(buffer->data + start, s, count)) ?
		-EFAULT : 0;
	persistent_ram_update_ecc(prz, start, count);
	return ret;
}

281
void persistent_ram_save_old(struct persistent_ram_zone *prz)
282 283
{
	struct persistent_ram_buffer *buffer = prz->buffer;
284 285
	size_t size = buffer_size(prz);
	size_t start = buffer_start(prz);
286

287 288
	if (!size)
		return;
289

290 291 292 293 294
	if (!prz->old_log) {
		persistent_ram_ecc_old(prz);
		prz->old_log = kmalloc(size, GFP_KERNEL);
	}
	if (!prz->old_log) {
F
Fabian Frederick 已提交
295
		pr_err("failed to allocate buffer\n");
296 297 298
		return;
	}

299
	prz->old_log_size = size;
300 301
	memcpy_fromio(prz->old_log, &buffer->data[start], size - start);
	memcpy_fromio(prz->old_log + size - start, &buffer->data[0], start);
302 303
}

304
int notrace persistent_ram_write(struct persistent_ram_zone *prz,
305 306 307 308
	const void *s, unsigned int count)
{
	int rem;
	int c = count;
309
	size_t start;
310

311
	if (unlikely(c > prz->buffer_size)) {
312 313 314
		s += c - prz->buffer_size;
		c = prz->buffer_size;
	}
315

316
	buffer_size_add(prz, c);
317 318 319 320 321 322

	start = buffer_start_add(prz, c);

	rem = prz->buffer_size - start;
	if (unlikely(rem < c)) {
		persistent_ram_update(prz, s, start, rem);
323 324
		s += rem;
		c -= rem;
325
		start = 0;
326
	}
327
	persistent_ram_update(prz, s, start, c);
328

329
	persistent_ram_update_header_ecc(prz);
330 331 332 333

	return count;
}

M
Mark Salyzyn 已提交
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
int notrace persistent_ram_write_user(struct persistent_ram_zone *prz,
	const void __user *s, unsigned int count)
{
	int rem, ret = 0, c = count;
	size_t start;

	if (unlikely(!access_ok(VERIFY_READ, s, count)))
		return -EFAULT;
	if (unlikely(c > prz->buffer_size)) {
		s += c - prz->buffer_size;
		c = prz->buffer_size;
	}

	buffer_size_add(prz, c);

	start = buffer_start_add(prz, c);

	rem = prz->buffer_size - start;
	if (unlikely(rem < c)) {
		ret = persistent_ram_update_user(prz, s, start, rem);
		s += rem;
		c -= rem;
		start = 0;
	}
	if (likely(!ret))
		ret = persistent_ram_update_user(prz, s, start, c);

	persistent_ram_update_header_ecc(prz);

	return unlikely(ret) ? ret : count;
}

366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
{
	return prz->old_log_size;
}

void *persistent_ram_old(struct persistent_ram_zone *prz)
{
	return prz->old_log;
}

void persistent_ram_free_old(struct persistent_ram_zone *prz)
{
	kfree(prz->old_log);
	prz->old_log = NULL;
	prz->old_log_size = 0;
}

383 384 385 386 387 388 389
void persistent_ram_zap(struct persistent_ram_zone *prz)
{
	atomic_set(&prz->buffer->start, 0);
	atomic_set(&prz->buffer->size, 0);
	persistent_ram_update_header_ecc(prz);
}

390 391
static void *persistent_ram_vmap(phys_addr_t start, size_t size,
		unsigned int memtype)
392
{
393 394 395 396 397
	struct page **pages;
	phys_addr_t page_start;
	unsigned int page_count;
	pgprot_t prot;
	unsigned int i;
398
	void *vaddr;
399 400 401 402

	page_start = start - offset_in_page(start);
	page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);

403 404 405 406
	if (memtype)
		prot = pgprot_noncached(PAGE_KERNEL);
	else
		prot = pgprot_writecombine(PAGE_KERNEL);
407

408
	pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
409
	if (!pages) {
F
Fabian Frederick 已提交
410 411
		pr_err("%s: Failed to allocate array for %u pages\n",
		       __func__, page_count);
412
		return NULL;
413 414 415 416 417 418
	}

	for (i = 0; i < page_count; i++) {
		phys_addr_t addr = page_start + i * PAGE_SIZE;
		pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
	}
419
	vaddr = vmap(pages, page_count, VM_MAP, prot);
420
	kfree(pages);
421 422 423 424

	return vaddr;
}

425 426
static void *persistent_ram_iomap(phys_addr_t start, size_t size,
		unsigned int memtype)
427
{
428 429
	void *va;

430 431 432 433 434 435
	if (!request_mem_region(start, size, "persistent_ram")) {
		pr_err("request mem region (0x%llx@0x%llx) failed\n",
			(unsigned long long)size, (unsigned long long)start);
		return NULL;
	}

436 437 438 439 440 441
	if (memtype)
		va = ioremap(start, size);
	else
		va = ioremap_wc(start, size);

	return va;
442 443
}

444
static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
445
		struct persistent_ram_zone *prz, int memtype)
446
{
447 448 449
	prz->paddr = start;
	prz->size = size;

450
	if (pfn_valid(start >> PAGE_SHIFT))
451
		prz->vaddr = persistent_ram_vmap(start, size, memtype);
452
	else
453
		prz->vaddr = persistent_ram_iomap(start, size, memtype);
454

455
	if (!prz->vaddr) {
456 457
		pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
			(unsigned long long)size, (unsigned long long)start);
458 459 460 461 462 463 464 465 466
		return -ENOMEM;
	}

	prz->buffer = prz->vaddr + offset_in_page(start);
	prz->buffer_size = size - sizeof(struct persistent_ram_buffer);

	return 0;
}

467
static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
468
				    struct persistent_ram_ecc_info *ecc_info)
469
{
470
	int ret;
471

472
	ret = persistent_ram_init_ecc(prz, ecc_info);
473
	if (ret)
474
		return ret;
475

476 477 478
	sig ^= PERSISTENT_RAM_SIG;

	if (prz->buffer->sig == sig) {
479 480
		if (buffer_size(prz) > prz->buffer_size ||
		    buffer_start(prz) > buffer_size(prz))
F
Fabian Frederick 已提交
481 482
			pr_info("found existing invalid buffer, size %zu, start %zu\n",
				buffer_size(prz), buffer_start(prz));
483
		else {
F
Fabian Frederick 已提交
484 485
			pr_debug("found existing buffer, size %zu, start %zu\n",
				 buffer_size(prz), buffer_start(prz));
486
			persistent_ram_save_old(prz);
487
			return 0;
488 489
		}
	} else {
F
Fabian Frederick 已提交
490 491
		pr_debug("no valid data in buffer (sig = 0x%08x)\n",
			 prz->buffer->sig);
492 493
	}

494
	prz->buffer->sig = sig;
495
	persistent_ram_zap(prz);
496

497 498 499
	return 0;
}

500 501
void persistent_ram_free(struct persistent_ram_zone *prz)
{
502 503 504 505 506 507 508 509 510 511 512
	if (!prz)
		return;

	if (prz->vaddr) {
		if (pfn_valid(prz->paddr >> PAGE_SHIFT)) {
			vunmap(prz->vaddr);
		} else {
			iounmap(prz->vaddr);
			release_mem_region(prz->paddr, prz->size);
		}
		prz->vaddr = NULL;
513 514 515 516 517
	}
	persistent_ram_free_old(prz);
	kfree(prz);
}

518
struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
519 520
			u32 sig, struct persistent_ram_ecc_info *ecc_info,
			unsigned int memtype)
521 522 523 524 525 526
{
	struct persistent_ram_zone *prz;
	int ret = -ENOMEM;

	prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
	if (!prz) {
F
Fabian Frederick 已提交
527
		pr_err("failed to allocate persistent ram zone\n");
528 529 530
		goto err;
	}

531
	ret = persistent_ram_buffer_map(start, size, prz, memtype);
532 533 534
	if (ret)
		goto err;

535
	ret = persistent_ram_post_init(prz, sig, ecc_info);
536 537
	if (ret)
		goto err;
538 539 540

	return prz;
err:
541
	persistent_ram_free(prz);
542 543
	return ERR_PTR(ret);
}