19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cddb8751SAnton Vorontsov /*
3cddb8751SAnton Vorontsov * Copyright (C) 2012 Google, Inc.
4cddb8751SAnton Vorontsov */
5cddb8751SAnton Vorontsov
69ee85b8bSKees Cook #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7ef748853SFabian Frederick
8cddb8751SAnton Vorontsov #include <linux/device.h>
9cddb8751SAnton Vorontsov #include <linux/err.h>
10cddb8751SAnton Vorontsov #include <linux/errno.h>
11cddb8751SAnton Vorontsov #include <linux/init.h>
12cddb8751SAnton Vorontsov #include <linux/io.h>
135bf6d1b9SMark Salyzyn #include <linux/kernel.h>
14cddb8751SAnton Vorontsov #include <linux/list.h>
15cddb8751SAnton Vorontsov #include <linux/memblock.h>
16cddb8751SAnton Vorontsov #include <linux/rslib.h>
17cddb8751SAnton Vorontsov #include <linux/slab.h>
185bf6d1b9SMark Salyzyn #include <linux/uaccess.h>
19cddb8751SAnton Vorontsov #include <linux/vmalloc.h>
20104fd0b5SYuxiao Zhang #include <linux/mm.h>
21cddb8751SAnton Vorontsov #include <asm/page.h>
22cddb8751SAnton Vorontsov
238bd4da0fSKees Cook #include "ram_internal.h"
248bd4da0fSKees Cook
25c208f7d4SKees Cook /**
26c208f7d4SKees Cook * struct persistent_ram_buffer - persistent circular RAM buffer
27c208f7d4SKees Cook *
28af58740dSMatthew Wilcox (Oracle) * @sig: Signature to indicate header (PERSISTENT_RAM_SIG xor PRZ-type value)
29af58740dSMatthew Wilcox (Oracle) * @start: First valid byte in the buffer.
30af58740dSMatthew Wilcox (Oracle) * @size: Number of valid bytes in the buffer.
31af58740dSMatthew Wilcox (Oracle) * @data: The contents of the buffer.
32c208f7d4SKees Cook */
33cddb8751SAnton Vorontsov struct persistent_ram_buffer {
34cddb8751SAnton Vorontsov uint32_t sig;
35cddb8751SAnton Vorontsov atomic_t start;
36cddb8751SAnton Vorontsov atomic_t size;
378128d3aaSGustavo A. R. Silva uint8_t data[];
38cddb8751SAnton Vorontsov };
39cddb8751SAnton Vorontsov
40cddb8751SAnton Vorontsov #define PERSISTENT_RAM_SIG (0x43474244) /* DBGC */
41cddb8751SAnton Vorontsov
buffer_size(struct persistent_ram_zone * prz)42cddb8751SAnton Vorontsov static inline size_t buffer_size(struct persistent_ram_zone *prz)
43cddb8751SAnton Vorontsov {
44cddb8751SAnton Vorontsov return atomic_read(&prz->buffer->size);
45cddb8751SAnton Vorontsov }
46cddb8751SAnton Vorontsov
buffer_start(struct persistent_ram_zone * prz)47cddb8751SAnton Vorontsov static inline size_t buffer_start(struct persistent_ram_zone *prz)
48cddb8751SAnton Vorontsov {
49cddb8751SAnton Vorontsov return atomic_read(&prz->buffer->start);
50cddb8751SAnton Vorontsov }
51cddb8751SAnton Vorontsov
520405a5ceSRob Herring /* increase and wrap the start pointer, returning the old value */
buffer_start_add(struct persistent_ram_zone * prz,size_t a)53d5a9bf0bSSebastian Andrzej Siewior static size_t buffer_start_add(struct persistent_ram_zone *prz, size_t a)
540405a5ceSRob Herring {
550405a5ceSRob Herring int old;
560405a5ceSRob Herring int new;
57663deb47SJoel Fernandes unsigned long flags = 0;
580405a5ceSRob Herring
59663deb47SJoel Fernandes if (!(prz->flags & PRZ_FLAG_NO_LOCK))
6010970449SJoel Fernandes raw_spin_lock_irqsave(&prz->buffer_lock, flags);
610405a5ceSRob Herring
620405a5ceSRob Herring old = atomic_read(&prz->buffer->start);
630405a5ceSRob Herring new = old + a;
64017321cfSLiu ShuoX while (unlikely(new >= prz->buffer_size))
650405a5ceSRob Herring new -= prz->buffer_size;
660405a5ceSRob Herring atomic_set(&prz->buffer->start, new);
670405a5ceSRob Herring
68663deb47SJoel Fernandes if (!(prz->flags & PRZ_FLAG_NO_LOCK))
6910970449SJoel Fernandes raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
700405a5ceSRob Herring
710405a5ceSRob Herring return old;
720405a5ceSRob Herring }
730405a5ceSRob Herring
740405a5ceSRob Herring /* increase the size counter until it hits the max size */
buffer_size_add(struct persistent_ram_zone * prz,size_t a)75d5a9bf0bSSebastian Andrzej Siewior static void buffer_size_add(struct persistent_ram_zone *prz, size_t a)
760405a5ceSRob Herring {
770405a5ceSRob Herring size_t old;
780405a5ceSRob Herring size_t new;
79663deb47SJoel Fernandes unsigned long flags = 0;
800405a5ceSRob Herring
81663deb47SJoel Fernandes if (!(prz->flags & PRZ_FLAG_NO_LOCK))
8210970449SJoel Fernandes raw_spin_lock_irqsave(&prz->buffer_lock, flags);
830405a5ceSRob Herring
840405a5ceSRob Herring old = atomic_read(&prz->buffer->size);
850405a5ceSRob Herring if (old == prz->buffer_size)
860405a5ceSRob Herring goto exit;
870405a5ceSRob Herring
880405a5ceSRob Herring new = old + a;
890405a5ceSRob Herring if (new > prz->buffer_size)
900405a5ceSRob Herring new = prz->buffer_size;
910405a5ceSRob Herring atomic_set(&prz->buffer->size, new);
920405a5ceSRob Herring
930405a5ceSRob Herring exit:
94663deb47SJoel Fernandes if (!(prz->flags & PRZ_FLAG_NO_LOCK))
9510970449SJoel Fernandes raw_spin_unlock_irqrestore(&prz->buffer_lock, flags);
960405a5ceSRob Herring }
970405a5ceSRob Herring
persistent_ram_encode_rs8(struct persistent_ram_zone * prz,uint8_t * data,size_t len,uint8_t * ecc)98cddb8751SAnton Vorontsov static void notrace persistent_ram_encode_rs8(struct persistent_ram_zone *prz,
99cddb8751SAnton Vorontsov uint8_t *data, size_t len, uint8_t *ecc)
100cddb8751SAnton Vorontsov {
101cddb8751SAnton Vorontsov int i;
102cddb8751SAnton Vorontsov
103cddb8751SAnton Vorontsov /* Initialize the parity buffer */
104f2531f19SKees Cook memset(prz->ecc_info.par, 0,
105f2531f19SKees Cook prz->ecc_info.ecc_size * sizeof(prz->ecc_info.par[0]));
106f2531f19SKees Cook encode_rs8(prz->rs_decoder, data, len, prz->ecc_info.par, 0);
107c31ad081SArve Hjønnevåg for (i = 0; i < prz->ecc_info.ecc_size; i++)
108f2531f19SKees Cook ecc[i] = prz->ecc_info.par[i];
109cddb8751SAnton Vorontsov }
110cddb8751SAnton Vorontsov
persistent_ram_decode_rs8(struct persistent_ram_zone * prz,void * data,size_t len,uint8_t * ecc)111cddb8751SAnton Vorontsov static int persistent_ram_decode_rs8(struct persistent_ram_zone *prz,
112cddb8751SAnton Vorontsov void *data, size_t len, uint8_t *ecc)
113cddb8751SAnton Vorontsov {
114cddb8751SAnton Vorontsov int i;
115cddb8751SAnton Vorontsov
116c31ad081SArve Hjønnevåg for (i = 0; i < prz->ecc_info.ecc_size; i++)
117f2531f19SKees Cook prz->ecc_info.par[i] = ecc[i];
118f2531f19SKees Cook return decode_rs8(prz->rs_decoder, data, prz->ecc_info.par, len,
119cddb8751SAnton Vorontsov NULL, 0, NULL, 0, NULL);
120cddb8751SAnton Vorontsov }
121cddb8751SAnton Vorontsov
persistent_ram_update_ecc(struct persistent_ram_zone * prz,unsigned int start,unsigned int count)122cddb8751SAnton Vorontsov static void notrace persistent_ram_update_ecc(struct persistent_ram_zone *prz,
123cddb8751SAnton Vorontsov unsigned int start, unsigned int count)
124cddb8751SAnton Vorontsov {
125cddb8751SAnton Vorontsov struct persistent_ram_buffer *buffer = prz->buffer;
126cddb8751SAnton Vorontsov uint8_t *buffer_end = buffer->data + prz->buffer_size;
127cddb8751SAnton Vorontsov uint8_t *block;
128cddb8751SAnton Vorontsov uint8_t *par;
129c31ad081SArve Hjønnevåg int ecc_block_size = prz->ecc_info.block_size;
130c31ad081SArve Hjønnevåg int ecc_size = prz->ecc_info.ecc_size;
131c31ad081SArve Hjønnevåg int size = ecc_block_size;
132cddb8751SAnton Vorontsov
133c31ad081SArve Hjønnevåg if (!ecc_size)
134cddb8751SAnton Vorontsov return;
135cddb8751SAnton Vorontsov
136cddb8751SAnton Vorontsov block = buffer->data + (start & ~(ecc_block_size - 1));
137c31ad081SArve Hjønnevåg par = prz->par_buffer + (start / ecc_block_size) * ecc_size;
138cddb8751SAnton Vorontsov
139cddb8751SAnton Vorontsov do {
140cddb8751SAnton Vorontsov if (block + ecc_block_size > buffer_end)
141cddb8751SAnton Vorontsov size = buffer_end - block;
142cddb8751SAnton Vorontsov persistent_ram_encode_rs8(prz, block, size, par);
143cddb8751SAnton Vorontsov block += ecc_block_size;
144cddb8751SAnton Vorontsov par += ecc_size;
145cddb8751SAnton Vorontsov } while (block < buffer->data + start + count);
146cddb8751SAnton Vorontsov }
147cddb8751SAnton Vorontsov
persistent_ram_update_header_ecc(struct persistent_ram_zone * prz)148cddb8751SAnton Vorontsov static void persistent_ram_update_header_ecc(struct persistent_ram_zone *prz)
149cddb8751SAnton Vorontsov {
150cddb8751SAnton Vorontsov struct persistent_ram_buffer *buffer = prz->buffer;
151cddb8751SAnton Vorontsov
152c31ad081SArve Hjønnevåg if (!prz->ecc_info.ecc_size)
153cddb8751SAnton Vorontsov return;
154cddb8751SAnton Vorontsov
155cddb8751SAnton Vorontsov persistent_ram_encode_rs8(prz, (uint8_t *)buffer, sizeof(*buffer),
156cddb8751SAnton Vorontsov prz->par_header);
157cddb8751SAnton Vorontsov }
158cddb8751SAnton Vorontsov
persistent_ram_ecc_old(struct persistent_ram_zone * prz)159cddb8751SAnton Vorontsov static void persistent_ram_ecc_old(struct persistent_ram_zone *prz)
160cddb8751SAnton Vorontsov {
161cddb8751SAnton Vorontsov struct persistent_ram_buffer *buffer = prz->buffer;
162cddb8751SAnton Vorontsov uint8_t *block;
163cddb8751SAnton Vorontsov uint8_t *par;
164cddb8751SAnton Vorontsov
165c31ad081SArve Hjønnevåg if (!prz->ecc_info.ecc_size)
166cddb8751SAnton Vorontsov return;
167cddb8751SAnton Vorontsov
168cddb8751SAnton Vorontsov block = buffer->data;
169cddb8751SAnton Vorontsov par = prz->par_buffer;
170cddb8751SAnton Vorontsov while (block < buffer->data + buffer_size(prz)) {
171cddb8751SAnton Vorontsov int numerr;
172c31ad081SArve Hjønnevåg int size = prz->ecc_info.block_size;
173cddb8751SAnton Vorontsov if (block + size > buffer->data + prz->buffer_size)
174cddb8751SAnton Vorontsov size = buffer->data + prz->buffer_size - block;
175cddb8751SAnton Vorontsov numerr = persistent_ram_decode_rs8(prz, block, size, par);
176cddb8751SAnton Vorontsov if (numerr > 0) {
177ef748853SFabian Frederick pr_devel("error in block %p, %d\n", block, numerr);
178cddb8751SAnton Vorontsov prz->corrected_bytes += numerr;
179cddb8751SAnton Vorontsov } else if (numerr < 0) {
180ef748853SFabian Frederick pr_devel("uncorrectable error in block %p\n", block);
181cddb8751SAnton Vorontsov prz->bad_blocks++;
182cddb8751SAnton Vorontsov }
183c31ad081SArve Hjønnevåg block += prz->ecc_info.block_size;
184c31ad081SArve Hjønnevåg par += prz->ecc_info.ecc_size;
185cddb8751SAnton Vorontsov }
186cddb8751SAnton Vorontsov }
187cddb8751SAnton Vorontsov
persistent_ram_init_ecc(struct persistent_ram_zone * prz,struct persistent_ram_ecc_info * ecc_info)1885ca5d4e6SAnton Vorontsov static int persistent_ram_init_ecc(struct persistent_ram_zone *prz,
189c31ad081SArve Hjønnevåg struct persistent_ram_ecc_info *ecc_info)
190cddb8751SAnton Vorontsov {
191cddb8751SAnton Vorontsov int numerr;
192cddb8751SAnton Vorontsov struct persistent_ram_buffer *buffer = prz->buffer;
193*a34946ecSSergey Shtylyov size_t ecc_blocks;
1941e6a9e56SAnton Vorontsov size_t ecc_total;
195cddb8751SAnton Vorontsov
196c31ad081SArve Hjønnevåg if (!ecc_info || !ecc_info->ecc_size)
197cddb8751SAnton Vorontsov return 0;
198cddb8751SAnton Vorontsov
199c31ad081SArve Hjønnevåg prz->ecc_info.block_size = ecc_info->block_size ?: 128;
200c31ad081SArve Hjønnevåg prz->ecc_info.ecc_size = ecc_info->ecc_size ?: 16;
201c31ad081SArve Hjønnevåg prz->ecc_info.symsize = ecc_info->symsize ?: 8;
202c31ad081SArve Hjønnevåg prz->ecc_info.poly = ecc_info->poly ?: 0x11d;
203cddb8751SAnton Vorontsov
204c31ad081SArve Hjønnevåg ecc_blocks = DIV_ROUND_UP(prz->buffer_size - prz->ecc_info.ecc_size,
205c31ad081SArve Hjønnevåg prz->ecc_info.block_size +
206c31ad081SArve Hjønnevåg prz->ecc_info.ecc_size);
207c31ad081SArve Hjønnevåg ecc_total = (ecc_blocks + 1) * prz->ecc_info.ecc_size;
2081e6a9e56SAnton Vorontsov if (ecc_total >= prz->buffer_size) {
2091e6a9e56SAnton Vorontsov pr_err("%s: invalid ecc_size %u (total %zu, buffer size %zu)\n",
210c31ad081SArve Hjønnevåg __func__, prz->ecc_info.ecc_size,
211c31ad081SArve Hjønnevåg ecc_total, prz->buffer_size);
212cddb8751SAnton Vorontsov return -EINVAL;
213cddb8751SAnton Vorontsov }
214cddb8751SAnton Vorontsov
2151e6a9e56SAnton Vorontsov prz->buffer_size -= ecc_total;
216cddb8751SAnton Vorontsov prz->par_buffer = buffer->data + prz->buffer_size;
217c31ad081SArve Hjønnevåg prz->par_header = prz->par_buffer +
218c31ad081SArve Hjønnevåg ecc_blocks * prz->ecc_info.ecc_size;
219cddb8751SAnton Vorontsov
220cddb8751SAnton Vorontsov /*
221cddb8751SAnton Vorontsov * first consecutive root is 0
222cddb8751SAnton Vorontsov * primitive element to generate roots = 1
223cddb8751SAnton Vorontsov */
224c31ad081SArve Hjønnevåg prz->rs_decoder = init_rs(prz->ecc_info.symsize, prz->ecc_info.poly,
225c31ad081SArve Hjønnevåg 0, 1, prz->ecc_info.ecc_size);
226cddb8751SAnton Vorontsov if (prz->rs_decoder == NULL) {
227ef748853SFabian Frederick pr_info("init_rs failed\n");
228cddb8751SAnton Vorontsov return -EINVAL;
229cddb8751SAnton Vorontsov }
230cddb8751SAnton Vorontsov
231f2531f19SKees Cook /* allocate workspace instead of using stack VLA */
232f2531f19SKees Cook prz->ecc_info.par = kmalloc_array(prz->ecc_info.ecc_size,
233f2531f19SKees Cook sizeof(*prz->ecc_info.par),
234f2531f19SKees Cook GFP_KERNEL);
235f2531f19SKees Cook if (!prz->ecc_info.par) {
236f2531f19SKees Cook pr_err("cannot allocate ECC parity workspace\n");
237f2531f19SKees Cook return -ENOMEM;
238f2531f19SKees Cook }
239f2531f19SKees Cook
240cddb8751SAnton Vorontsov prz->corrected_bytes = 0;
241cddb8751SAnton Vorontsov prz->bad_blocks = 0;
242cddb8751SAnton Vorontsov
243cddb8751SAnton Vorontsov numerr = persistent_ram_decode_rs8(prz, buffer, sizeof(*buffer),
244cddb8751SAnton Vorontsov prz->par_header);
245cddb8751SAnton Vorontsov if (numerr > 0) {
246ef748853SFabian Frederick pr_info("error in header, %d\n", numerr);
247cddb8751SAnton Vorontsov prz->corrected_bytes += numerr;
248cddb8751SAnton Vorontsov } else if (numerr < 0) {
2497db688e9SDmitry Osipenko pr_info_ratelimited("uncorrectable error in header\n");
250cddb8751SAnton Vorontsov prz->bad_blocks++;
251cddb8751SAnton Vorontsov }
252cddb8751SAnton Vorontsov
253cddb8751SAnton Vorontsov return 0;
254cddb8751SAnton Vorontsov }
255cddb8751SAnton Vorontsov
persistent_ram_ecc_string(struct persistent_ram_zone * prz,char * str,size_t len)256cddb8751SAnton Vorontsov ssize_t persistent_ram_ecc_string(struct persistent_ram_zone *prz,
257cddb8751SAnton Vorontsov char *str, size_t len)
258cddb8751SAnton Vorontsov {
259cddb8751SAnton Vorontsov ssize_t ret;
260cddb8751SAnton Vorontsov
261bd08ec33SArve Hjønnevåg if (!prz->ecc_info.ecc_size)
262bd08ec33SArve Hjønnevåg return 0;
263bd08ec33SArve Hjønnevåg
264cddb8751SAnton Vorontsov if (prz->corrected_bytes || prz->bad_blocks)
265cddb8751SAnton Vorontsov ret = snprintf(str, len, ""
266023bbde3SVincent Whitchurch "\nECC: %d Corrected bytes, %d unrecoverable blocks\n",
267cddb8751SAnton Vorontsov prz->corrected_bytes, prz->bad_blocks);
268cddb8751SAnton Vorontsov else
269023bbde3SVincent Whitchurch ret = snprintf(str, len, "\nECC: No errors detected\n");
270cddb8751SAnton Vorontsov
271cddb8751SAnton Vorontsov return ret;
272cddb8751SAnton Vorontsov }
273cddb8751SAnton Vorontsov
persistent_ram_update(struct persistent_ram_zone * prz,const void * s,unsigned int start,unsigned int count)274cddb8751SAnton Vorontsov static void notrace persistent_ram_update(struct persistent_ram_zone *prz,
275cddb8751SAnton Vorontsov const void *s, unsigned int start, unsigned int count)
276cddb8751SAnton Vorontsov {
277cddb8751SAnton Vorontsov struct persistent_ram_buffer *buffer = prz->buffer;
2787e75678dSFurquan Shaikh memcpy_toio(buffer->data + start, s, count);
279cddb8751SAnton Vorontsov persistent_ram_update_ecc(prz, start, count);
280cddb8751SAnton Vorontsov }
281cddb8751SAnton Vorontsov
persistent_ram_update_user(struct persistent_ram_zone * prz,const void __user * s,unsigned int start,unsigned int count)2825bf6d1b9SMark Salyzyn static int notrace persistent_ram_update_user(struct persistent_ram_zone *prz,
2835bf6d1b9SMark Salyzyn const void __user *s, unsigned int start, unsigned int count)
2845bf6d1b9SMark Salyzyn {
2855bf6d1b9SMark Salyzyn struct persistent_ram_buffer *buffer = prz->buffer;
286ff847781SAl Viro int ret = unlikely(copy_from_user(buffer->data + start, s, count)) ?
2875bf6d1b9SMark Salyzyn -EFAULT : 0;
2885bf6d1b9SMark Salyzyn persistent_ram_update_ecc(prz, start, count);
2895bf6d1b9SMark Salyzyn return ret;
2905bf6d1b9SMark Salyzyn }
2915bf6d1b9SMark Salyzyn
persistent_ram_save_old(struct persistent_ram_zone * prz)292201e4acaSAnton Vorontsov void persistent_ram_save_old(struct persistent_ram_zone *prz)
293cddb8751SAnton Vorontsov {
294cddb8751SAnton Vorontsov struct persistent_ram_buffer *buffer = prz->buffer;
295cddb8751SAnton Vorontsov size_t size = buffer_size(prz);
296cddb8751SAnton Vorontsov size_t start = buffer_start(prz);
297cddb8751SAnton Vorontsov
298201e4acaSAnton Vorontsov if (!size)
299201e4acaSAnton Vorontsov return;
300201e4acaSAnton Vorontsov
301201e4acaSAnton Vorontsov if (!prz->old_log) {
302cddb8751SAnton Vorontsov persistent_ram_ecc_old(prz);
303104fd0b5SYuxiao Zhang prz->old_log = kvzalloc(size, GFP_KERNEL);
304201e4acaSAnton Vorontsov }
305201e4acaSAnton Vorontsov if (!prz->old_log) {
306ef748853SFabian Frederick pr_err("failed to allocate buffer\n");
307cddb8751SAnton Vorontsov return;
308cddb8751SAnton Vorontsov }
309cddb8751SAnton Vorontsov
310cddb8751SAnton Vorontsov prz->old_log_size = size;
311d771fdf9SAndrew Bresticker memcpy_fromio(prz->old_log, &buffer->data[start], size - start);
312d771fdf9SAndrew Bresticker memcpy_fromio(prz->old_log + size - start, &buffer->data[0], start);
313cddb8751SAnton Vorontsov }
314cddb8751SAnton Vorontsov
persistent_ram_write(struct persistent_ram_zone * prz,const void * s,unsigned int count)315cddb8751SAnton Vorontsov int notrace persistent_ram_write(struct persistent_ram_zone *prz,
316cddb8751SAnton Vorontsov const void *s, unsigned int count)
317cddb8751SAnton Vorontsov {
318cddb8751SAnton Vorontsov int rem;
319cddb8751SAnton Vorontsov int c = count;
320cddb8751SAnton Vorontsov size_t start;
321cddb8751SAnton Vorontsov
322cddb8751SAnton Vorontsov if (unlikely(c > prz->buffer_size)) {
323cddb8751SAnton Vorontsov s += c - prz->buffer_size;
324cddb8751SAnton Vorontsov c = prz->buffer_size;
325cddb8751SAnton Vorontsov }
326cddb8751SAnton Vorontsov
327cddb8751SAnton Vorontsov buffer_size_add(prz, c);
328cddb8751SAnton Vorontsov
329cddb8751SAnton Vorontsov start = buffer_start_add(prz, c);
330cddb8751SAnton Vorontsov
331cddb8751SAnton Vorontsov rem = prz->buffer_size - start;
332cddb8751SAnton Vorontsov if (unlikely(rem < c)) {
333cddb8751SAnton Vorontsov persistent_ram_update(prz, s, start, rem);
334cddb8751SAnton Vorontsov s += rem;
335cddb8751SAnton Vorontsov c -= rem;
336cddb8751SAnton Vorontsov start = 0;
337cddb8751SAnton Vorontsov }
338cddb8751SAnton Vorontsov persistent_ram_update(prz, s, start, c);
339cddb8751SAnton Vorontsov
340cddb8751SAnton Vorontsov persistent_ram_update_header_ecc(prz);
341cddb8751SAnton Vorontsov
342cddb8751SAnton Vorontsov return count;
343cddb8751SAnton Vorontsov }
344cddb8751SAnton Vorontsov
persistent_ram_write_user(struct persistent_ram_zone * prz,const void __user * s,unsigned int count)3455bf6d1b9SMark Salyzyn int notrace persistent_ram_write_user(struct persistent_ram_zone *prz,
3465bf6d1b9SMark Salyzyn const void __user *s, unsigned int count)
3475bf6d1b9SMark Salyzyn {
3485bf6d1b9SMark Salyzyn int rem, ret = 0, c = count;
3495bf6d1b9SMark Salyzyn size_t start;
3505bf6d1b9SMark Salyzyn
3515bf6d1b9SMark Salyzyn if (unlikely(c > prz->buffer_size)) {
3525bf6d1b9SMark Salyzyn s += c - prz->buffer_size;
3535bf6d1b9SMark Salyzyn c = prz->buffer_size;
3545bf6d1b9SMark Salyzyn }
3555bf6d1b9SMark Salyzyn
3565bf6d1b9SMark Salyzyn buffer_size_add(prz, c);
3575bf6d1b9SMark Salyzyn
3585bf6d1b9SMark Salyzyn start = buffer_start_add(prz, c);
3595bf6d1b9SMark Salyzyn
3605bf6d1b9SMark Salyzyn rem = prz->buffer_size - start;
3615bf6d1b9SMark Salyzyn if (unlikely(rem < c)) {
3625bf6d1b9SMark Salyzyn ret = persistent_ram_update_user(prz, s, start, rem);
3635bf6d1b9SMark Salyzyn s += rem;
3645bf6d1b9SMark Salyzyn c -= rem;
3655bf6d1b9SMark Salyzyn start = 0;
3665bf6d1b9SMark Salyzyn }
3675bf6d1b9SMark Salyzyn if (likely(!ret))
3685bf6d1b9SMark Salyzyn ret = persistent_ram_update_user(prz, s, start, c);
3695bf6d1b9SMark Salyzyn
3705bf6d1b9SMark Salyzyn persistent_ram_update_header_ecc(prz);
3715bf6d1b9SMark Salyzyn
3725bf6d1b9SMark Salyzyn return unlikely(ret) ? ret : count;
3735bf6d1b9SMark Salyzyn }
3745bf6d1b9SMark Salyzyn
persistent_ram_old_size(struct persistent_ram_zone * prz)375cddb8751SAnton Vorontsov size_t persistent_ram_old_size(struct persistent_ram_zone *prz)
376cddb8751SAnton Vorontsov {
377cddb8751SAnton Vorontsov return prz->old_log_size;
378cddb8751SAnton Vorontsov }
379cddb8751SAnton Vorontsov
persistent_ram_old(struct persistent_ram_zone * prz)380cddb8751SAnton Vorontsov void *persistent_ram_old(struct persistent_ram_zone *prz)
381cddb8751SAnton Vorontsov {
382cddb8751SAnton Vorontsov return prz->old_log;
383cddb8751SAnton Vorontsov }
384cddb8751SAnton Vorontsov
persistent_ram_free_old(struct persistent_ram_zone * prz)385cddb8751SAnton Vorontsov void persistent_ram_free_old(struct persistent_ram_zone *prz)
386cddb8751SAnton Vorontsov {
387104fd0b5SYuxiao Zhang kvfree(prz->old_log);
388cddb8751SAnton Vorontsov prz->old_log = NULL;
389cddb8751SAnton Vorontsov prz->old_log_size = 0;
390cddb8751SAnton Vorontsov }
391cddb8751SAnton Vorontsov
persistent_ram_zap(struct persistent_ram_zone * prz)392fce39793SAnton Vorontsov void persistent_ram_zap(struct persistent_ram_zone *prz)
393fce39793SAnton Vorontsov {
394fce39793SAnton Vorontsov atomic_set(&prz->buffer->start, 0);
395fce39793SAnton Vorontsov atomic_set(&prz->buffer->size, 0);
396fce39793SAnton Vorontsov persistent_ram_update_header_ecc(prz);
397fce39793SAnton Vorontsov }
398fce39793SAnton Vorontsov
3999d843e8fSMukesh Ojha #define MEM_TYPE_WCOMBINE 0
4009d843e8fSMukesh Ojha #define MEM_TYPE_NONCACHED 1
4019d843e8fSMukesh Ojha #define MEM_TYPE_NORMAL 2
4029d843e8fSMukesh Ojha
persistent_ram_vmap(phys_addr_t start,size_t size,unsigned int memtype)403027bc8b0STony Lindgren static void *persistent_ram_vmap(phys_addr_t start, size_t size,
404027bc8b0STony Lindgren unsigned int memtype)
405cddb8751SAnton Vorontsov {
406cddb8751SAnton Vorontsov struct page **pages;
407cddb8751SAnton Vorontsov phys_addr_t page_start;
408cddb8751SAnton Vorontsov unsigned int page_count;
409cddb8751SAnton Vorontsov pgprot_t prot;
410cddb8751SAnton Vorontsov unsigned int i;
411cddb8751SAnton Vorontsov void *vaddr;
412cddb8751SAnton Vorontsov
413cddb8751SAnton Vorontsov page_start = start - offset_in_page(start);
414cddb8751SAnton Vorontsov page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE);
415cddb8751SAnton Vorontsov
4169d843e8fSMukesh Ojha switch (memtype) {
4179d843e8fSMukesh Ojha case MEM_TYPE_NORMAL:
4189d843e8fSMukesh Ojha prot = PAGE_KERNEL;
4199d843e8fSMukesh Ojha break;
4209d843e8fSMukesh Ojha case MEM_TYPE_NONCACHED:
421027bc8b0STony Lindgren prot = pgprot_noncached(PAGE_KERNEL);
4229d843e8fSMukesh Ojha break;
4239d843e8fSMukesh Ojha case MEM_TYPE_WCOMBINE:
4247ae9cb81SRob Herring prot = pgprot_writecombine(PAGE_KERNEL);
4259d843e8fSMukesh Ojha break;
4269d843e8fSMukesh Ojha default:
4279d843e8fSMukesh Ojha pr_err("invalid mem_type=%d\n", memtype);
4289d843e8fSMukesh Ojha return NULL;
4299d843e8fSMukesh Ojha }
430cddb8751SAnton Vorontsov
431b8f52d89SFabian Frederick pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
432cddb8751SAnton Vorontsov if (!pages) {
433ef748853SFabian Frederick pr_err("%s: Failed to allocate array for %u pages\n",
434ef748853SFabian Frederick __func__, page_count);
435cddb8751SAnton Vorontsov return NULL;
436cddb8751SAnton Vorontsov }
437cddb8751SAnton Vorontsov
438cddb8751SAnton Vorontsov for (i = 0; i < page_count; i++) {
439cddb8751SAnton Vorontsov phys_addr_t addr = page_start + i * PAGE_SIZE;
440cddb8751SAnton Vorontsov pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
441cddb8751SAnton Vorontsov }
442e6b84274SStephen Boyd /*
443e6b84274SStephen Boyd * VM_IOREMAP used here to bypass this region during vread()
444e6b84274SStephen Boyd * and kmap_atomic() (i.e. kcore) to avoid __va() failures.
445e6b84274SStephen Boyd */
446e6b84274SStephen Boyd vaddr = vmap(pages, page_count, VM_MAP | VM_IOREMAP, prot);
447cddb8751SAnton Vorontsov kfree(pages);
448cddb8751SAnton Vorontsov
449831b624dSBin Yang /*
450831b624dSBin Yang * Since vmap() uses page granularity, we must add the offset
451831b624dSBin Yang * into the page here, to get the byte granularity address
452831b624dSBin Yang * into the mapping to represent the actual "start" location.
453831b624dSBin Yang */
454831b624dSBin Yang return vaddr + offset_in_page(start);
455cddb8751SAnton Vorontsov }
456cddb8751SAnton Vorontsov
persistent_ram_iomap(phys_addr_t start,size_t size,unsigned int memtype,char * label)457027bc8b0STony Lindgren static void *persistent_ram_iomap(phys_addr_t start, size_t size,
4581227daa4SKees Cook unsigned int memtype, char *label)
459cddb8751SAnton Vorontsov {
460027bc8b0STony Lindgren void *va;
461027bc8b0STony Lindgren
4621227daa4SKees Cook if (!request_mem_region(start, size, label ?: "ramoops")) {
4639ee85b8bSKees Cook pr_err("request mem region (%s 0x%llx@0x%llx) failed\n",
4649ee85b8bSKees Cook label ?: "ramoops",
465cddb8751SAnton Vorontsov (unsigned long long)size, (unsigned long long)start);
466cddb8751SAnton Vorontsov return NULL;
467cddb8751SAnton Vorontsov }
468cddb8751SAnton Vorontsov
469027bc8b0STony Lindgren if (memtype)
470027bc8b0STony Lindgren va = ioremap(start, size);
471027bc8b0STony Lindgren else
472027bc8b0STony Lindgren va = ioremap_wc(start, size);
473027bc8b0STony Lindgren
474831b624dSBin Yang /*
475831b624dSBin Yang * Since request_mem_region() and ioremap() are byte-granularity
476831b624dSBin Yang * there is no need handle anything special like we do when the
477831b624dSBin Yang * vmap() case in persistent_ram_vmap() above.
478831b624dSBin Yang */
479027bc8b0STony Lindgren return va;
480cddb8751SAnton Vorontsov }
481cddb8751SAnton Vorontsov
persistent_ram_buffer_map(phys_addr_t start,phys_addr_t size,struct persistent_ram_zone * prz,int memtype)482cddb8751SAnton Vorontsov static int persistent_ram_buffer_map(phys_addr_t start, phys_addr_t size,
483027bc8b0STony Lindgren struct persistent_ram_zone *prz, int memtype)
484cddb8751SAnton Vorontsov {
485cddb8751SAnton Vorontsov prz->paddr = start;
486cddb8751SAnton Vorontsov prz->size = size;
487cddb8751SAnton Vorontsov
488cddb8751SAnton Vorontsov if (pfn_valid(start >> PAGE_SHIFT))
489027bc8b0STony Lindgren prz->vaddr = persistent_ram_vmap(start, size, memtype);
490cddb8751SAnton Vorontsov else
4911227daa4SKees Cook prz->vaddr = persistent_ram_iomap(start, size, memtype,
4921227daa4SKees Cook prz->label);
493cddb8751SAnton Vorontsov
494cddb8751SAnton Vorontsov if (!prz->vaddr) {
495cddb8751SAnton Vorontsov pr_err("%s: Failed to map 0x%llx pages at 0x%llx\n", __func__,
496cddb8751SAnton Vorontsov (unsigned long long)size, (unsigned long long)start);
497cddb8751SAnton Vorontsov return -ENOMEM;
498cddb8751SAnton Vorontsov }
499cddb8751SAnton Vorontsov
500831b624dSBin Yang prz->buffer = prz->vaddr;
501cddb8751SAnton Vorontsov prz->buffer_size = size - sizeof(struct persistent_ram_buffer);
502cddb8751SAnton Vorontsov
503cddb8751SAnton Vorontsov return 0;
504cddb8751SAnton Vorontsov }
505cddb8751SAnton Vorontsov
persistent_ram_post_init(struct persistent_ram_zone * prz,u32 sig,struct persistent_ram_ecc_info * ecc_info)506f568f6caSGreg Kroah-Hartman static int persistent_ram_post_init(struct persistent_ram_zone *prz, u32 sig,
50776d5692aSKees Cook struct persistent_ram_ecc_info *ecc_info)
508cddb8751SAnton Vorontsov {
509cddb8751SAnton Vorontsov int ret;
5107684bd33SPeng Wang bool zap = !!(prz->flags & PRZ_FLAG_ZAP_OLD);
511cddb8751SAnton Vorontsov
512c31ad081SArve Hjønnevåg ret = persistent_ram_init_ecc(prz, ecc_info);
5130eed84ffSKees Cook if (ret) {
5140eed84ffSKees Cook pr_warn("ECC failed %s\n", prz->label);
515cddb8751SAnton Vorontsov return ret;
5160eed84ffSKees Cook }
517cddb8751SAnton Vorontsov
518cbe7cbf5SAnton Vorontsov sig ^= PERSISTENT_RAM_SIG;
519cbe7cbf5SAnton Vorontsov
520cbe7cbf5SAnton Vorontsov if (prz->buffer->sig == sig) {
521fe8c3623SEnlin Mu if (buffer_size(prz) == 0 && buffer_start(prz) == 0) {
52230696378SJoel Fernandes (Google) pr_debug("found existing empty buffer\n");
52330696378SJoel Fernandes (Google) return 0;
52430696378SJoel Fernandes (Google) }
52530696378SJoel Fernandes (Google)
526cddb8751SAnton Vorontsov if (buffer_size(prz) > prz->buffer_size ||
5277684bd33SPeng Wang buffer_start(prz) > buffer_size(prz)) {
528ef748853SFabian Frederick pr_info("found existing invalid buffer, size %zu, start %zu\n",
529cddb8751SAnton Vorontsov buffer_size(prz), buffer_start(prz));
5307684bd33SPeng Wang zap = true;
5317684bd33SPeng Wang } else {
532ef748853SFabian Frederick pr_debug("found existing buffer, size %zu, start %zu\n",
533cddb8751SAnton Vorontsov buffer_size(prz), buffer_start(prz));
534cddb8751SAnton Vorontsov persistent_ram_save_old(prz);
535cddb8751SAnton Vorontsov }
536cddb8751SAnton Vorontsov } else {
537ef748853SFabian Frederick pr_debug("no valid data in buffer (sig = 0x%08x)\n",
538ef748853SFabian Frederick prz->buffer->sig);
5397684bd33SPeng Wang prz->buffer->sig = sig;
5407684bd33SPeng Wang zap = true;
541cddb8751SAnton Vorontsov }
542cddb8751SAnton Vorontsov
5437684bd33SPeng Wang /* Reset missing, invalid, or single-use memory area. */
5447684bd33SPeng Wang if (zap)
545fce39793SAnton Vorontsov persistent_ram_zap(prz);
546cddb8751SAnton Vorontsov
547cddb8751SAnton Vorontsov return 0;
548cddb8751SAnton Vorontsov }
549cddb8751SAnton Vorontsov
persistent_ram_free(struct persistent_ram_zone ** _prz)55006b4e09aSKees Cook void persistent_ram_free(struct persistent_ram_zone **_prz)
551cddb8751SAnton Vorontsov {
55206b4e09aSKees Cook struct persistent_ram_zone *prz;
55306b4e09aSKees Cook
55406b4e09aSKees Cook if (!_prz)
55506b4e09aSKees Cook return;
55606b4e09aSKees Cook
55706b4e09aSKees Cook prz = *_prz;
558beeb9432SAnton Vorontsov if (!prz)
559beeb9432SAnton Vorontsov return;
560beeb9432SAnton Vorontsov
561beeb9432SAnton Vorontsov if (prz->vaddr) {
562cddb8751SAnton Vorontsov if (pfn_valid(prz->paddr >> PAGE_SHIFT)) {
563831b624dSBin Yang /* We must vunmap() at page-granularity. */
564831b624dSBin Yang vunmap(prz->vaddr - offset_in_page(prz->paddr));
565cddb8751SAnton Vorontsov } else {
566cddb8751SAnton Vorontsov iounmap(prz->vaddr);
567cddb8751SAnton Vorontsov release_mem_region(prz->paddr, prz->size);
568cddb8751SAnton Vorontsov }
569beeb9432SAnton Vorontsov prz->vaddr = NULL;
570beeb9432SAnton Vorontsov }
571f2531f19SKees Cook if (prz->rs_decoder) {
572f2531f19SKees Cook free_rs(prz->rs_decoder);
573f2531f19SKees Cook prz->rs_decoder = NULL;
574f2531f19SKees Cook }
575f2531f19SKees Cook kfree(prz->ecc_info.par);
576f2531f19SKees Cook prz->ecc_info.par = NULL;
577f2531f19SKees Cook
578cddb8751SAnton Vorontsov persistent_ram_free_old(prz);
5791227daa4SKees Cook kfree(prz->label);
580cddb8751SAnton Vorontsov kfree(prz);
58106b4e09aSKees Cook *_prz = NULL;
582cddb8751SAnton Vorontsov }
583cddb8751SAnton Vorontsov
persistent_ram_new(phys_addr_t start,size_t size,u32 sig,struct persistent_ram_ecc_info * ecc_info,unsigned int memtype,u32 flags,char * label)584f568f6caSGreg Kroah-Hartman struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size,
585027bc8b0STony Lindgren u32 sig, struct persistent_ram_ecc_info *ecc_info,
5861227daa4SKees Cook unsigned int memtype, u32 flags, char *label)
587cddb8751SAnton Vorontsov {
588cddb8751SAnton Vorontsov struct persistent_ram_zone *prz;
589cddb8751SAnton Vorontsov int ret = -ENOMEM;
590cddb8751SAnton Vorontsov
591cddb8751SAnton Vorontsov prz = kzalloc(sizeof(struct persistent_ram_zone), GFP_KERNEL);
592cddb8751SAnton Vorontsov if (!prz) {
593ef748853SFabian Frederick pr_err("failed to allocate persistent ram zone\n");
594cddb8751SAnton Vorontsov goto err;
595cddb8751SAnton Vorontsov }
596cddb8751SAnton Vorontsov
59776d5692aSKees Cook /* Initialize general buffer state. */
598e9a330c4SKees Cook raw_spin_lock_init(&prz->buffer_lock);
59976d5692aSKees Cook prz->flags = flags;
600e163fdb3SKees Cook prz->label = kstrdup(label, GFP_KERNEL);
601d97038d5SJiasheng Jiang if (!prz->label)
602d97038d5SJiasheng Jiang goto err;
60376d5692aSKees Cook
604027bc8b0STony Lindgren ret = persistent_ram_buffer_map(start, size, prz, memtype);
605cddb8751SAnton Vorontsov if (ret)
606cddb8751SAnton Vorontsov goto err;
607cddb8751SAnton Vorontsov
60876d5692aSKees Cook ret = persistent_ram_post_init(prz, sig, ecc_info);
609beeb9432SAnton Vorontsov if (ret)
610beeb9432SAnton Vorontsov goto err;
611cddb8751SAnton Vorontsov
612dc80b1eaSKees Cook pr_debug("attached %s 0x%zx@0x%llx: %zu header, %zu data, %zu ecc (%d/%d)\n",
613dc80b1eaSKees Cook prz->label, prz->size, (unsigned long long)prz->paddr,
614dc80b1eaSKees Cook sizeof(*prz->buffer), prz->buffer_size,
615dc80b1eaSKees Cook prz->size - sizeof(*prz->buffer) - prz->buffer_size,
616dc80b1eaSKees Cook prz->ecc_info.ecc_size, prz->ecc_info.block_size);
617dc80b1eaSKees Cook
618cddb8751SAnton Vorontsov return prz;
619cddb8751SAnton Vorontsov err:
62006b4e09aSKees Cook persistent_ram_free(&prz);
621cddb8751SAnton Vorontsov return ERR_PTR(ret);
622cddb8751SAnton Vorontsov }
623