1c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross/* 2c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * Copyright (C) 2012 Google, Inc. 3c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * 4c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * This software is licensed under the terms of the GNU General Public 5c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * License version 2, as published by the Free Software Foundation, and 6c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * may be copied, distributed, and modified under those terms. 7c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * 8c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * This program is distributed in the hope that it will be useful, 9c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * but WITHOUT ANY WARRANTY; without even the implied warranty of 10c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * GNU General Public License for more details. 12c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross * 13c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross */ 14c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 15404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/device.h> 16404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/err.h> 17c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/errno.h> 18c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/kernel.h> 19c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/init.h> 20c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/io.h> 21404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/list.h> 22404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/memblock.h> 23a3a0f312f78c4321a33314edb18b565f97ba489fColin Cross#include <linux/persistent_ram.h> 249cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross#include <linux/rslib.h> 25c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#include <linux/slab.h> 26404a6043385de17273624b076599669db5ad891fColin Cross#include <linux/vmalloc.h> 27c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 28c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossstruct persistent_ram_buffer { 29c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint32_t sig; 30808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross atomic_t start; 31808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross atomic_t size; 32c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t data[0]; 33c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross}; 34c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 35c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross#define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */ 36c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 374a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic __devinitdata LIST_HEAD(persistent_ram_list); 38c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 39808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline size_t buffer_size(struct persistent_ram_zone *prz) 40808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{ 41808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross return atomic_read(&prz->buffer->size); 42808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross} 43808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 44808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline size_t buffer_start(struct persistent_ram_zone *prz) 45808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{ 46808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross return atomic_read(&prz->buffer->start); 47808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross} 48808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 49808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross/* increase and wrap the start pointer, returning the old value */ 50808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a) 51808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{ 52808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross int old; 53808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross int new; 54808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 55808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross do { 56808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross old = atomic_read(&prz->buffer->start); 57808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross new = old + a; 58808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross while (unlikely(new > prz->buffer_size)) 59808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross new -= prz->buffer_size; 60808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross } while (atomic_cmpxchg(&prz->buffer->start, old, new) != old); 61808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 62808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross return old; 63808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross} 64808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 65808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross/* increase the size counter until it hits the max size */ 66808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Crossstatic inline void buffer_size_add(struct persistent_ram_zone *prz, size_t a) 67808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross{ 68808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross size_t old; 69808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross size_t new; 70808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 71808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross if (atomic_read(&prz->buffer->size) == prz->buffer_size) 72808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross return; 73808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 74808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross do { 75808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross old = atomic_read(&prz->buffer->size); 76808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross new = old + a; 77808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross if (new > prz->buffer_size) 78808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross new = prz->buffer_size; 79808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross } while (atomic_cmpxchg(&prz->buffer->size, old, new) != old); 80808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross} 81808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 82a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossstatic void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz, 83c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t *data, size_t len, uint8_t *ecc) 84c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 85c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross int i; 869cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross uint16_t par[prz->ecc_size]; 879cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 88c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross /* Initialize the parity buffer */ 89c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross memset(par, 0, sizeof(par)); 90c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross encode_rs8(prz->rs_decoder, data, len, par, 0); 919cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross for (i = 0; i < prz->ecc_size; i++) 92c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross ecc[i] = par[i]; 93c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 94c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 95c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossstatic int persistent_ram_decode_rs8(struct persistent_ram_zone *prz, 96c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross void *data, size_t len, uint8_t *ecc) 97c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 98c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross int i; 999cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross uint16_t par[prz->ecc_size]; 1009cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1019cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross for (i = 0; i < prz->ecc_size; i++) 102c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross par[i] = ecc[i]; 103c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross return decode_rs8(prz->rs_decoder, data, par, len, 104c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross NULL, 0, NULL, 0, NULL); 105c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 106c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 107a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossstatic void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz, 108808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross unsigned int start, unsigned int count) 109c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 110c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross struct persistent_ram_buffer *buffer = prz->buffer; 111c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t *buffer_end = buffer->data + prz->buffer_size; 112c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t *block; 113c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t *par; 1149cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross int ecc_block_size = prz->ecc_block_size; 1159cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross int ecc_size = prz->ecc_size; 1169cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross int size = prz->ecc_block_size; 1179cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1189cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (!prz->ecc) 1199cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return; 1209cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 121808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross block = buffer->data + (start & ~(ecc_block_size - 1)); 122808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross par = prz->par_buffer + (start / ecc_block_size) * prz->ecc_size; 123808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 124c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross do { 1259cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (block + ecc_block_size > buffer_end) 126c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross size = buffer_end - block; 127c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross persistent_ram_encode_rs8(prz, block, size, par); 1289cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross block += ecc_block_size; 1299cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross par += ecc_size; 130808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross } while (block < buffer->data + start + count); 131c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 132c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 1339cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossstatic void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz) 134c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 135c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross struct persistent_ram_buffer *buffer = prz->buffer; 136c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 1379cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (!prz->ecc) 1389cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return; 1399cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 140c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer), 141c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross prz->par_header); 142c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 143c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 1449cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossstatic void persistent_ram_ecc_old(struct persistent_ram_zone *prz) 145c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 146c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross struct persistent_ram_buffer *buffer = prz->buffer; 147c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t *block; 148c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross uint8_t *par; 149c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 1509cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (!prz->ecc) 1519cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return; 1529cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 153c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross block = buffer->data; 154c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross par = prz->par_buffer; 155808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross while (block < buffer->data + buffer_size(prz)) { 156c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross int numerr; 1579cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross int size = prz->ecc_block_size; 158c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross if (block + size > buffer->data + prz->buffer_size) 159c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross size = buffer->data + prz->buffer_size - block; 160c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross numerr = persistent_ram_decode_rs8(prz, block, size, par); 161c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross if (numerr > 0) { 1629cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross pr_devel("persistent_ram: error in block %p, %d\n", 163c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross block, numerr); 164c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross prz->corrected_bytes += numerr; 165c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } else if (numerr < 0) { 1669cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross pr_devel("persistent_ram: uncorrectable error in block %p\n", 167c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross block); 168c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross prz->bad_blocks++; 169c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 1709cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross block += prz->ecc_block_size; 1719cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross par += prz->ecc_size; 1729cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross } 1739cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross} 1749cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1759cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossstatic int persistent_ram_init_ecc(struct persistent_ram_zone *prz, 1766411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg size_t buffer_size, struct persistent_ram *ram) 1779cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{ 1789cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross int numerr; 1799cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross struct persistent_ram_buffer *buffer = prz->buffer; 1809cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross int ecc_blocks; 1819cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1829cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (!prz->ecc) 1839cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return 0; 1849cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1856411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg prz->ecc_block_size = ram->ecc_block_size ?: 128; 1866411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg prz->ecc_size = ram->ecc_size ?: 16; 1876411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg prz->ecc_symsize = ram->ecc_symsize ?: 8; 1886411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg prz->ecc_poly = ram->ecc_poly ?: 0x11d; 1899cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1906ce7d7aa4b53b6171c835a6964a24201ac89ee64Arve Hjønnevåg ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_size, 1916ce7d7aa4b53b6171c835a6964a24201ac89ee64Arve Hjønnevåg prz->ecc_block_size + prz->ecc_size); 1929cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->buffer_size -= (ecc_blocks + 1) * prz->ecc_size; 1939cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 1949cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (prz->buffer_size > buffer_size) { 1959cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross pr_err("persistent_ram: invalid size %zu, non-ecc datasize %zu\n", 1969cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross buffer_size, prz->buffer_size); 1979cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return -EINVAL; 1989cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross } 1999cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2009cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->par_buffer = buffer->data + prz->buffer_size; 2019cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->par_header = prz->par_buffer + ecc_blocks * prz->ecc_size; 2029cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2039cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross /* 2049cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross * first consecutive root is 0 2059cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross * primitive element to generate roots = 1 2069cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross */ 2079cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->rs_decoder = init_rs(prz->ecc_symsize, prz->ecc_poly, 0, 1, 2089cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->ecc_size); 2099cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (prz->rs_decoder == NULL) { 2109cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross pr_info("persistent_ram: init_rs failed\n"); 2119cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return -EINVAL; 212c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 2139cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2149cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->corrected_bytes = 0; 2159cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->bad_blocks = 0; 2169cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2179cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer), 2189cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->par_header); 2199cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (numerr > 0) { 2209cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross pr_info("persistent_ram: error in header, %d\n", numerr); 2219cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->corrected_bytes += numerr; 2229cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross } else if (numerr < 0) { 2239cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross pr_info("persistent_ram: uncorrectable error in header\n"); 2249cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->bad_blocks++; 2259cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross } 2269cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2279cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return 0; 2289cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross} 2299cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2309cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crossssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz, 2319cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross char *str, size_t len) 2329cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{ 2339cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross ssize_t ret; 2349cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2359cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (prz->corrected_bytes || prz->bad_blocks) 2369cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross ret = snprintf(str, len, "" 2379cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross "\n%d Corrected bytes, %d unrecoverable blocks\n", 2389cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->corrected_bytes, prz->bad_blocks); 2399cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross else 2409cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross ret = snprintf(str, len, "\nNo errors detected\n"); 2419cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2429cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross return ret; 2439cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross} 2449cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 245a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossstatic void notrace persistent_ram_update(struct persistent_ram_zone *prz, 246808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross const void *s, unsigned int start, unsigned int count) 2479cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{ 2489cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross struct persistent_ram_buffer *buffer = prz->buffer; 249808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross memcpy(buffer->data + start, s, count); 250808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross persistent_ram_update_ecc(prz, start, count); 2519cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross} 2529cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2534a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic void __devinit 2549cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Crosspersistent_ram_save_old(struct persistent_ram_zone *prz) 2559cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross{ 2569cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross struct persistent_ram_buffer *buffer = prz->buffer; 257808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross size_t size = buffer_size(prz); 258808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross size_t start = buffer_start(prz); 2599cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross char *dest; 2609cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross 2619cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross persistent_ram_ecc_old(prz); 262c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 263808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross dest = kmalloc(size, GFP_KERNEL); 264c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross if (dest == NULL) { 265c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross pr_err("persistent_ram: failed to allocate buffer\n"); 266c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross return; 267c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 268c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 269c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross prz->old_log = dest; 270808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross prz->old_log_size = size; 271808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross memcpy(prz->old_log, &buffer->data[start], size - start); 272808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross memcpy(prz->old_log + size - start, &buffer->data[0], start); 273c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 274c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 275a15d0b365e9bbf04dacb44fbe69d15f6594460e1Colin Crossint notrace persistent_ram_write(struct persistent_ram_zone *prz, 276c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross const void *s, unsigned int count) 277c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 278c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross int rem; 279c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross int c = count; 280808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross size_t start; 281c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 282808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross if (unlikely(c > prz->buffer_size)) { 283c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross s += c - prz->buffer_size; 284c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross c = prz->buffer_size; 285c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 286808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 287bbe784b683f6e49bc345577fc5398344bc7c289eAnton Vorontsov buffer_size_add(prz, c); 288808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 289808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross start = buffer_start_add(prz, c); 290808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross 291808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross rem = prz->buffer_size - start; 292808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross if (unlikely(rem < c)) { 293808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross persistent_ram_update(prz, s, start, rem); 294c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross s += rem; 295c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross c -= rem; 296808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross start = 0; 297c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 298808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross persistent_ram_update(prz, s, start, c); 299c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 3009cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross persistent_ram_update_header_ecc(prz); 301c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 302c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross return count; 303c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 304c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 305c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crosssize_t persistent_ram_old_size(struct persistent_ram_zone *prz) 306c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 307c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross return prz->old_log_size; 308c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 309c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 310c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossvoid *persistent_ram_old(struct persistent_ram_zone *prz) 311c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 312c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross return prz->old_log; 313c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 314c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 315c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Crossvoid persistent_ram_free_old(struct persistent_ram_zone *prz) 316c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 317c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross kfree(prz->old_log); 318c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross prz->old_log = NULL; 319c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross prz->old_log_size = 0; 320c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 321c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 322404a6043385de17273624b076599669db5ad891fColin Crossstatic int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size, 323404a6043385de17273624b076599669db5ad891fColin Cross struct persistent_ram_zone *prz) 324c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 325404a6043385de17273624b076599669db5ad891fColin Cross struct page **pages; 326404a6043385de17273624b076599669db5ad891fColin Cross phys_addr_t page_start; 327404a6043385de17273624b076599669db5ad891fColin Cross unsigned int page_count; 328404a6043385de17273624b076599669db5ad891fColin Cross pgprot_t prot; 329404a6043385de17273624b076599669db5ad891fColin Cross unsigned int i; 330404a6043385de17273624b076599669db5ad891fColin Cross 331404a6043385de17273624b076599669db5ad891fColin Cross page_start = start - offset_in_page(start); 332404a6043385de17273624b076599669db5ad891fColin Cross page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); 333404a6043385de17273624b076599669db5ad891fColin Cross 334404a6043385de17273624b076599669db5ad891fColin Cross prot = pgprot_noncached(PAGE_KERNEL); 335404a6043385de17273624b076599669db5ad891fColin Cross 336404a6043385de17273624b076599669db5ad891fColin Cross pages = kmalloc(sizeof(struct page *) * page_count, GFP_KERNEL); 337404a6043385de17273624b076599669db5ad891fColin Cross if (!pages) { 338404a6043385de17273624b076599669db5ad891fColin Cross pr_err("%s: Failed to allocate array for %u pages\n", __func__, 339404a6043385de17273624b076599669db5ad891fColin Cross page_count); 340404a6043385de17273624b076599669db5ad891fColin Cross return -ENOMEM; 341404a6043385de17273624b076599669db5ad891fColin Cross } 342404a6043385de17273624b076599669db5ad891fColin Cross 343404a6043385de17273624b076599669db5ad891fColin Cross for (i = 0; i < page_count; i++) { 344404a6043385de17273624b076599669db5ad891fColin Cross phys_addr_t addr = page_start + i * PAGE_SIZE; 345404a6043385de17273624b076599669db5ad891fColin Cross pages[i] = pfn_to_page(addr >> PAGE_SHIFT); 346404a6043385de17273624b076599669db5ad891fColin Cross } 347404a6043385de17273624b076599669db5ad891fColin Cross prz->vaddr = vmap(pages, page_count, VM_MAP, prot); 348404a6043385de17273624b076599669db5ad891fColin Cross kfree(pages); 349404a6043385de17273624b076599669db5ad891fColin Cross if (!prz->vaddr) { 350404a6043385de17273624b076599669db5ad891fColin Cross pr_err("%s: Failed to map %u pages\n", __func__, page_count); 351404a6043385de17273624b076599669db5ad891fColin Cross return -ENOMEM; 352404a6043385de17273624b076599669db5ad891fColin Cross } 353404a6043385de17273624b076599669db5ad891fColin Cross 354404a6043385de17273624b076599669db5ad891fColin Cross prz->buffer = prz->vaddr + offset_in_page(start); 355404a6043385de17273624b076599669db5ad891fColin Cross prz->buffer_size = size - sizeof(struct persistent_ram_buffer); 356404a6043385de17273624b076599669db5ad891fColin Cross 357404a6043385de17273624b076599669db5ad891fColin Cross return 0; 358404a6043385de17273624b076599669db5ad891fColin Cross} 359404a6043385de17273624b076599669db5ad891fColin Cross 3604a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic int __devinit persistent_ram_buffer_init(const char *name, 3616411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg struct persistent_ram_zone *prz, struct persistent_ram **ramp) 362404a6043385de17273624b076599669db5ad891fColin Cross{ 363404a6043385de17273624b076599669db5ad891fColin Cross int i; 364404a6043385de17273624b076599669db5ad891fColin Cross struct persistent_ram *ram; 365404a6043385de17273624b076599669db5ad891fColin Cross struct persistent_ram_descriptor *desc; 366404a6043385de17273624b076599669db5ad891fColin Cross phys_addr_t start; 367404a6043385de17273624b076599669db5ad891fColin Cross 368404a6043385de17273624b076599669db5ad891fColin Cross list_for_each_entry(ram, &persistent_ram_list, node) { 369404a6043385de17273624b076599669db5ad891fColin Cross start = ram->start; 370404a6043385de17273624b076599669db5ad891fColin Cross for (i = 0; i < ram->num_descs; i++) { 371404a6043385de17273624b076599669db5ad891fColin Cross desc = &ram->descs[i]; 3726411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg if (!strcmp(desc->name, name)) { 3736411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg *ramp = ram; 374404a6043385de17273624b076599669db5ad891fColin Cross return persistent_ram_buffer_map(start, 375404a6043385de17273624b076599669db5ad891fColin Cross desc->size, prz); 3766411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg } 377404a6043385de17273624b076599669db5ad891fColin Cross start += desc->size; 378404a6043385de17273624b076599669db5ad891fColin Cross } 379404a6043385de17273624b076599669db5ad891fColin Cross } 380404a6043385de17273624b076599669db5ad891fColin Cross 381404a6043385de17273624b076599669db5ad891fColin Cross return -EINVAL; 382404a6043385de17273624b076599669db5ad891fColin Cross} 383404a6043385de17273624b076599669db5ad891fColin Cross 3844a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstatic __devinit 385404a6043385de17273624b076599669db5ad891fColin Crossstruct persistent_ram_zone *__persistent_ram_init(struct device *dev, bool ecc) 386404a6043385de17273624b076599669db5ad891fColin Cross{ 3876411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg struct persistent_ram *ram; 388404a6043385de17273624b076599669db5ad891fColin Cross struct persistent_ram_zone *prz; 389474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl int ret = -ENOMEM; 390c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 391404a6043385de17273624b076599669db5ad891fColin Cross prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL); 392404a6043385de17273624b076599669db5ad891fColin Cross if (!prz) { 393404a6043385de17273624b076599669db5ad891fColin Cross pr_err("persistent_ram: failed to allocate persistent ram zone\n"); 394474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl goto err; 395404a6043385de17273624b076599669db5ad891fColin Cross } 396c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 397404a6043385de17273624b076599669db5ad891fColin Cross INIT_LIST_HEAD(&prz->node); 398c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 3996411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg ret = persistent_ram_buffer_init(dev_name(dev), prz, &ram); 400404a6043385de17273624b076599669db5ad891fColin Cross if (ret) { 401404a6043385de17273624b076599669db5ad891fColin Cross pr_err("persistent_ram: failed to initialize buffer\n"); 402474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl goto err; 403c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 404c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 4059cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross prz->ecc = ecc; 4066411d5781dee41da3e2e29b024140e8f4a0f0ac7Arve Hjønnevåg ret = persistent_ram_init_ecc(prz, prz->buffer_size, ram); 4079cc05ad97c5728aaf4db94490daf41f8958b5aeeColin Cross if (ret) 408474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl goto err; 409c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 410404a6043385de17273624b076599669db5ad891fColin Cross if (prz->buffer->sig == PERSISTENT_RAM_SIG) { 411808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross if (buffer_size(prz) > prz->buffer_size || 412808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross buffer_start(prz) > buffer_size(prz)) 413808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross pr_info("persistent_ram: found existing invalid buffer," 4148543065b05c5a2aadbaf476a3af3099753fd710eColin Cross " size %zu, start %zu\n", 415808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross buffer_size(prz), buffer_start(prz)); 416c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross else { 417808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross pr_info("persistent_ram: found existing buffer," 4188543065b05c5a2aadbaf476a3af3099753fd710eColin Cross " size %zu, start %zu\n", 419808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross buffer_size(prz), buffer_start(prz)); 420c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross persistent_ram_save_old(prz); 421c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 422c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } else { 423808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross pr_info("persistent_ram: no valid data in buffer" 424808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross " (sig = 0x%08x)\n", prz->buffer->sig); 425c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross } 426c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 427404a6043385de17273624b076599669db5ad891fColin Cross prz->buffer->sig = PERSISTENT_RAM_SIG; 428808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross atomic_set(&prz->buffer->start, 0); 429808d0387eb7df3f83352ca41f8c943cb3ed61246Colin Cross atomic_set(&prz->buffer->size, 0); 430c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 431404a6043385de17273624b076599669db5ad891fColin Cross return prz; 432474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhlerr: 433474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl kfree(prz); 434474a89885f77953b12bce9f23660c31ef5c2630eJesper Juhl return ERR_PTR(ret); 435404a6043385de17273624b076599669db5ad891fColin Cross} 436c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 4374a2212f2e6a4938180bfced93efb3adea4c6418dColin Crossstruct persistent_ram_zone * __devinit 438404a6043385de17273624b076599669db5ad891fColin Crosspersistent_ram_init_ringbuffer(struct device *dev, bool ecc) 439404a6043385de17273624b076599669db5ad891fColin Cross{ 440404a6043385de17273624b076599669db5ad891fColin Cross return __persistent_ram_init(dev, ecc); 441c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 442c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross 443404a6043385de17273624b076599669db5ad891fColin Crossint __init persistent_ram_early_init(struct persistent_ram *ram) 444c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross{ 445404a6043385de17273624b076599669db5ad891fColin Cross int ret; 446404a6043385de17273624b076599669db5ad891fColin Cross 447404a6043385de17273624b076599669db5ad891fColin Cross ret = memblock_reserve(ram->start, ram->size); 448404a6043385de17273624b076599669db5ad891fColin Cross if (ret) { 449404a6043385de17273624b076599669db5ad891fColin Cross pr_err("Failed to reserve persistent memory from %08lx-%08lx\n", 450404a6043385de17273624b076599669db5ad891fColin Cross (long)ram->start, (long)(ram->start + ram->size - 1)); 451404a6043385de17273624b076599669db5ad891fColin Cross return ret; 452404a6043385de17273624b076599669db5ad891fColin Cross } 453404a6043385de17273624b076599669db5ad891fColin Cross 454404a6043385de17273624b076599669db5ad891fColin Cross list_add_tail(&ram->node, &persistent_ram_list); 455404a6043385de17273624b076599669db5ad891fColin Cross 456404a6043385de17273624b076599669db5ad891fColin Cross pr_info("Initialized persistent memory from %08lx-%08lx\n", 457404a6043385de17273624b076599669db5ad891fColin Cross (long)ram->start, (long)(ram->start + ram->size - 1)); 458404a6043385de17273624b076599669db5ad891fColin Cross 459404a6043385de17273624b076599669db5ad891fColin Cross return 0; 460c672528aec4a1cf6f3df7a6022e6823a20b20f8eColin Cross} 461