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