xref: /linux/arch/powerpc/platforms/pseries/nvram.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
269a80d3fSPaul Mackerras /*
369a80d3fSPaul Mackerras  *  c 2001 PPC 64 Team, IBM Corp
469a80d3fSPaul Mackerras  *
569a80d3fSPaul Mackerras  * /dev/nvram driver for PPC64
669a80d3fSPaul Mackerras  */
769a80d3fSPaul Mackerras 
869a80d3fSPaul Mackerras 
969a80d3fSPaul Mackerras #include <linux/types.h>
1069a80d3fSPaul Mackerras #include <linux/errno.h>
1169a80d3fSPaul Mackerras #include <linux/init.h>
1269a80d3fSPaul Mackerras #include <linux/spinlock.h>
13a5cf4b08SJim Keniston #include <linux/slab.h>
146c493685SJim Keniston #include <linux/ctype.h>
157c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
16e6f6390aSChristophe Leroy #include <linux/of.h>
1769a80d3fSPaul Mackerras #include <asm/nvram.h>
1869a80d3fSPaul Mackerras #include <asm/rtas.h>
1969a80d3fSPaul Mackerras #include <asm/machdep.h>
2069a80d3fSPaul Mackerras 
214e7c77a3SBenjamin Herrenschmidt /* Max bytes to read/write in one go */
224e7c77a3SBenjamin Herrenschmidt #define NVRW_CNT 0x20
234e7c77a3SBenjamin Herrenschmidt 
2469a80d3fSPaul Mackerras static unsigned int nvram_size;
2569a80d3fSPaul Mackerras static int nvram_fetch, nvram_store;
2669a80d3fSPaul Mackerras static char nvram_buf[NVRW_CNT];	/* assume this is in the first 4GB */
2769a80d3fSPaul Mackerras static DEFINE_SPINLOCK(nvram_lock);
2869a80d3fSPaul Mackerras 
29a5cf4b08SJim Keniston /* See clobbering_unread_rtas_event() */
30a5cf4b08SJim Keniston #define NVRAM_RTAS_READ_TIMEOUT 5		/* seconds */
31e4a9616cSHari Bathini static time64_t last_unread_rtas_event;		/* timestamp */
32a5cf4b08SJim Keniston 
33d7563c94SAruna Balakrishnaiah #ifdef CONFIG_PSTORE
34e4a9616cSHari Bathini time64_t last_rtas_event;
35d7563c94SAruna Balakrishnaiah #endif
36d7563c94SAruna Balakrishnaiah 
pSeries_nvram_read(char * buf,size_t count,loff_t * index)3769a80d3fSPaul Mackerras static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
3869a80d3fSPaul Mackerras {
3969a80d3fSPaul Mackerras 	unsigned int i;
4069a80d3fSPaul Mackerras 	unsigned long len;
4169a80d3fSPaul Mackerras 	int done;
4269a80d3fSPaul Mackerras 	unsigned long flags;
4369a80d3fSPaul Mackerras 	char *p = buf;
4469a80d3fSPaul Mackerras 
4569a80d3fSPaul Mackerras 
4669a80d3fSPaul Mackerras 	if (nvram_size == 0 || nvram_fetch == RTAS_UNKNOWN_SERVICE)
4769a80d3fSPaul Mackerras 		return -ENODEV;
4869a80d3fSPaul Mackerras 
4969a80d3fSPaul Mackerras 	if (*index >= nvram_size)
5069a80d3fSPaul Mackerras 		return 0;
5169a80d3fSPaul Mackerras 
5269a80d3fSPaul Mackerras 	i = *index;
5369a80d3fSPaul Mackerras 	if (i + count > nvram_size)
5469a80d3fSPaul Mackerras 		count = nvram_size - i;
5569a80d3fSPaul Mackerras 
5669a80d3fSPaul Mackerras 	spin_lock_irqsave(&nvram_lock, flags);
5769a80d3fSPaul Mackerras 
5869a80d3fSPaul Mackerras 	for (; count != 0; count -= len) {
5969a80d3fSPaul Mackerras 		len = count;
6069a80d3fSPaul Mackerras 		if (len > NVRW_CNT)
6169a80d3fSPaul Mackerras 			len = NVRW_CNT;
6269a80d3fSPaul Mackerras 
6369a80d3fSPaul Mackerras 		if ((rtas_call(nvram_fetch, 3, 2, &done, i, __pa(nvram_buf),
6469a80d3fSPaul Mackerras 			       len) != 0) || len != done) {
6569a80d3fSPaul Mackerras 			spin_unlock_irqrestore(&nvram_lock, flags);
6669a80d3fSPaul Mackerras 			return -EIO;
6769a80d3fSPaul Mackerras 		}
6869a80d3fSPaul Mackerras 
6969a80d3fSPaul Mackerras 		memcpy(p, nvram_buf, len);
7069a80d3fSPaul Mackerras 
7169a80d3fSPaul Mackerras 		p += len;
7269a80d3fSPaul Mackerras 		i += len;
7369a80d3fSPaul Mackerras 	}
7469a80d3fSPaul Mackerras 
7569a80d3fSPaul Mackerras 	spin_unlock_irqrestore(&nvram_lock, flags);
7669a80d3fSPaul Mackerras 
7769a80d3fSPaul Mackerras 	*index = i;
7869a80d3fSPaul Mackerras 	return p - buf;
7969a80d3fSPaul Mackerras }
8069a80d3fSPaul Mackerras 
pSeries_nvram_write(char * buf,size_t count,loff_t * index)8169a80d3fSPaul Mackerras static ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index)
8269a80d3fSPaul Mackerras {
8369a80d3fSPaul Mackerras 	unsigned int i;
8469a80d3fSPaul Mackerras 	unsigned long len;
8569a80d3fSPaul Mackerras 	int done;
8669a80d3fSPaul Mackerras 	unsigned long flags;
8769a80d3fSPaul Mackerras 	const char *p = buf;
8869a80d3fSPaul Mackerras 
8969a80d3fSPaul Mackerras 	if (nvram_size == 0 || nvram_store == RTAS_UNKNOWN_SERVICE)
9069a80d3fSPaul Mackerras 		return -ENODEV;
9169a80d3fSPaul Mackerras 
9269a80d3fSPaul Mackerras 	if (*index >= nvram_size)
9369a80d3fSPaul Mackerras 		return 0;
9469a80d3fSPaul Mackerras 
9569a80d3fSPaul Mackerras 	i = *index;
9669a80d3fSPaul Mackerras 	if (i + count > nvram_size)
9769a80d3fSPaul Mackerras 		count = nvram_size - i;
9869a80d3fSPaul Mackerras 
9969a80d3fSPaul Mackerras 	spin_lock_irqsave(&nvram_lock, flags);
10069a80d3fSPaul Mackerras 
10169a80d3fSPaul Mackerras 	for (; count != 0; count -= len) {
10269a80d3fSPaul Mackerras 		len = count;
10369a80d3fSPaul Mackerras 		if (len > NVRW_CNT)
10469a80d3fSPaul Mackerras 			len = NVRW_CNT;
10569a80d3fSPaul Mackerras 
10669a80d3fSPaul Mackerras 		memcpy(nvram_buf, p, len);
10769a80d3fSPaul Mackerras 
10869a80d3fSPaul Mackerras 		if ((rtas_call(nvram_store, 3, 2, &done, i, __pa(nvram_buf),
10969a80d3fSPaul Mackerras 			       len) != 0) || len != done) {
11069a80d3fSPaul Mackerras 			spin_unlock_irqrestore(&nvram_lock, flags);
11169a80d3fSPaul Mackerras 			return -EIO;
11269a80d3fSPaul Mackerras 		}
11369a80d3fSPaul Mackerras 
11469a80d3fSPaul Mackerras 		p += len;
11569a80d3fSPaul Mackerras 		i += len;
11669a80d3fSPaul Mackerras 	}
11769a80d3fSPaul Mackerras 	spin_unlock_irqrestore(&nvram_lock, flags);
11869a80d3fSPaul Mackerras 
11969a80d3fSPaul Mackerras 	*index = i;
12069a80d3fSPaul Mackerras 	return p - buf;
12169a80d3fSPaul Mackerras }
12269a80d3fSPaul Mackerras 
pSeries_nvram_get_size(void)12369a80d3fSPaul Mackerras static ssize_t pSeries_nvram_get_size(void)
12469a80d3fSPaul Mackerras {
12569a80d3fSPaul Mackerras 	return nvram_size ? nvram_size : -ENODEV;
12669a80d3fSPaul Mackerras }
12769a80d3fSPaul Mackerras 
12878989f0aSHari Bathini /* nvram_write_error_log
129edc79a2fSBenjamin Herrenschmidt  *
130edc79a2fSBenjamin Herrenschmidt  * We need to buffer the error logs into nvram to ensure that we have
13178989f0aSHari Bathini  * the failure information to decode.
132edc79a2fSBenjamin Herrenschmidt  */
nvram_write_error_log(char * buff,int length,unsigned int err_type,unsigned int error_log_cnt)1330f4ac132SJim Keniston int nvram_write_error_log(char * buff, int length,
1340f4ac132SJim Keniston                           unsigned int err_type, unsigned int error_log_cnt)
1350f4ac132SJim Keniston {
136a5cf4b08SJim Keniston 	int rc = nvram_write_os_partition(&rtas_log_partition, buff, length,
1370f4ac132SJim Keniston 						err_type, error_log_cnt);
13869020eeaSAruna Balakrishnaiah 	if (!rc) {
139e4a9616cSHari Bathini 		last_unread_rtas_event = ktime_get_real_seconds();
14069020eeaSAruna Balakrishnaiah #ifdef CONFIG_PSTORE
141e4a9616cSHari Bathini 		last_rtas_event = ktime_get_real_seconds();
14269020eeaSAruna Balakrishnaiah #endif
14369020eeaSAruna Balakrishnaiah 	}
14469020eeaSAruna Balakrishnaiah 
145a5cf4b08SJim Keniston 	return rc;
1460f4ac132SJim Keniston }
1470f4ac132SJim Keniston 
14812674610SAruna Balakrishnaiah /* nvram_read_error_log
14912674610SAruna Balakrishnaiah  *
15012674610SAruna Balakrishnaiah  * Reads nvram for error log for at most 'length'
15112674610SAruna Balakrishnaiah  */
nvram_read_error_log(char * buff,int length,unsigned int * err_type,unsigned int * error_log_cnt)15212674610SAruna Balakrishnaiah int nvram_read_error_log(char *buff, int length,
15312674610SAruna Balakrishnaiah 			unsigned int *err_type, unsigned int *error_log_cnt)
15412674610SAruna Balakrishnaiah {
15512674610SAruna Balakrishnaiah 	return nvram_read_partition(&rtas_log_partition, buff, length,
15612674610SAruna Balakrishnaiah 						err_type, error_log_cnt);
15712674610SAruna Balakrishnaiah }
15812674610SAruna Balakrishnaiah 
159edc79a2fSBenjamin Herrenschmidt /* This doesn't actually zero anything, but it sets the event_logged
160edc79a2fSBenjamin Herrenschmidt  * word to tell that this event is safely in syslog.
161edc79a2fSBenjamin Herrenschmidt  */
nvram_clear_error_log(void)162edc79a2fSBenjamin Herrenschmidt int nvram_clear_error_log(void)
163edc79a2fSBenjamin Herrenschmidt {
164edc79a2fSBenjamin Herrenschmidt 	loff_t tmp_index;
165edc79a2fSBenjamin Herrenschmidt 	int clear_word = ERR_FLAG_ALREADY_LOGGED;
166edc79a2fSBenjamin Herrenschmidt 	int rc;
167edc79a2fSBenjamin Herrenschmidt 
1680f4ac132SJim Keniston 	if (rtas_log_partition.index == -1)
169edc79a2fSBenjamin Herrenschmidt 		return -1;
170edc79a2fSBenjamin Herrenschmidt 
1710f4ac132SJim Keniston 	tmp_index = rtas_log_partition.index;
172edc79a2fSBenjamin Herrenschmidt 
173edc79a2fSBenjamin Herrenschmidt 	rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
174edc79a2fSBenjamin Herrenschmidt 	if (rc <= 0) {
175edc79a2fSBenjamin Herrenschmidt 		printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
176edc79a2fSBenjamin Herrenschmidt 		return rc;
177edc79a2fSBenjamin Herrenschmidt 	}
178a5cf4b08SJim Keniston 	last_unread_rtas_event = 0;
179edc79a2fSBenjamin Herrenschmidt 
180edc79a2fSBenjamin Herrenschmidt 	return 0;
181edc79a2fSBenjamin Herrenschmidt }
182edc79a2fSBenjamin Herrenschmidt 
183d7563c94SAruna Balakrishnaiah /*
184d7563c94SAruna Balakrishnaiah  * Are we using the ibm,rtas-log for oops/panic reports?  And if so,
185d7563c94SAruna Balakrishnaiah  * would logging this oops/panic overwrite an RTAS event that rtas_errd
186d7563c94SAruna Balakrishnaiah  * hasn't had a chance to read and process?  Return 1 if so, else 0.
187d7563c94SAruna Balakrishnaiah  *
188d7563c94SAruna Balakrishnaiah  * We assume that if rtas_errd hasn't read the RTAS event in
189d7563c94SAruna Balakrishnaiah  * NVRAM_RTAS_READ_TIMEOUT seconds, it's probably not going to.
190d7563c94SAruna Balakrishnaiah  */
clobbering_unread_rtas_event(void)19178989f0aSHari Bathini int clobbering_unread_rtas_event(void)
192d7563c94SAruna Balakrishnaiah {
193d7563c94SAruna Balakrishnaiah 	return (oops_log_partition.index == rtas_log_partition.index
194d7563c94SAruna Balakrishnaiah 		&& last_unread_rtas_event
195e4a9616cSHari Bathini 		&& ktime_get_real_seconds() - last_unread_rtas_event <=
196d7563c94SAruna Balakrishnaiah 						NVRAM_RTAS_READ_TIMEOUT);
197d7563c94SAruna Balakrishnaiah }
198d7563c94SAruna Balakrishnaiah 
pseries_nvram_init_log_partitions(void)1990f4ac132SJim Keniston static int __init pseries_nvram_init_log_partitions(void)
2000f4ac132SJim Keniston {
201a5cf4b08SJim Keniston 	int rc;
202a5cf4b08SJim Keniston 
20365f36f41SCedric Le Goater 	/* Scan nvram for partitions */
20465f36f41SCedric Le Goater 	nvram_scan_partitions();
20565f36f41SCedric Le Goater 
20678989f0aSHari Bathini 	rc = nvram_init_os_partition(&rtas_log_partition);
207a5cf4b08SJim Keniston 	nvram_init_oops_partition(rc == 0);
2080f4ac132SJim Keniston 	return 0;
2090f4ac132SJim Keniston }
2100f4ac132SJim Keniston machine_arch_initcall(pseries, pseries_nvram_init_log_partitions);
211edc79a2fSBenjamin Herrenschmidt 
pSeries_nvram_init(void)21269a80d3fSPaul Mackerras int __init pSeries_nvram_init(void)
21369a80d3fSPaul Mackerras {
21469a80d3fSPaul Mackerras 	struct device_node *nvram;
215563c5d8aSCedric Le Goater 	const __be32 *nbytes_p;
216954a46e2SJeremy Kerr 	unsigned int proplen;
21769a80d3fSPaul Mackerras 
21869a80d3fSPaul Mackerras 	nvram = of_find_node_by_type(NULL, "nvram");
21969a80d3fSPaul Mackerras 	if (nvram == NULL)
22069a80d3fSPaul Mackerras 		return -ENODEV;
22169a80d3fSPaul Mackerras 
222e2eb6392SStephen Rothwell 	nbytes_p = of_get_property(nvram, "#bytes", &proplen);
223bad5232bSJulia Lawall 	if (nbytes_p == NULL || proplen != sizeof(unsigned int)) {
224bad5232bSJulia Lawall 		of_node_put(nvram);
22569a80d3fSPaul Mackerras 		return -EIO;
226bad5232bSJulia Lawall 	}
22769a80d3fSPaul Mackerras 
228563c5d8aSCedric Le Goater 	nvram_size = be32_to_cpup(nbytes_p);
22969a80d3fSPaul Mackerras 
230*08273c9fSNathan Lynch 	nvram_fetch = rtas_function_token(RTAS_FN_NVRAM_FETCH);
231*08273c9fSNathan Lynch 	nvram_store = rtas_function_token(RTAS_FN_NVRAM_STORE);
23269a80d3fSPaul Mackerras 	printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size);
23369a80d3fSPaul Mackerras 	of_node_put(nvram);
23469a80d3fSPaul Mackerras 
23569a80d3fSPaul Mackerras 	ppc_md.nvram_read	= pSeries_nvram_read;
23669a80d3fSPaul Mackerras 	ppc_md.nvram_write	= pSeries_nvram_write;
23769a80d3fSPaul Mackerras 	ppc_md.nvram_size	= pSeries_nvram_get_size;
23869a80d3fSPaul Mackerras 
23969a80d3fSPaul Mackerras 	return 0;
24069a80d3fSPaul Mackerras }
241a5cf4b08SJim Keniston 
242