powerpc/nvram: Generalize code for OS partitions in NVRAM
[deliverable/linux.git] / arch / powerpc / platforms / pseries / nvram.c
CommitLineData
1da177e4
LT
1/*
2 * c 2001 PPC 64 Team, IBM Corp
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * /dev/nvram driver for PPC64
10 *
11 * This perhaps should live in drivers/char
12 */
13
14
15#include <linux/types.h>
16#include <linux/errno.h>
17#include <linux/init.h>
1da177e4
LT
18#include <linux/spinlock.h>
19#include <asm/uaccess.h>
20#include <asm/nvram.h>
21#include <asm/rtas.h>
22#include <asm/prom.h>
23#include <asm/machdep.h>
24
4e7c77a3
BH
25/* Max bytes to read/write in one go */
26#define NVRW_CNT 0x20
27
1da177e4
LT
28static unsigned int nvram_size;
29static int nvram_fetch, nvram_store;
30static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */
31static DEFINE_SPINLOCK(nvram_lock);
32
edc79a2f
BH
33struct err_log_info {
34 int error_type;
35 unsigned int seq_num;
36};
edc79a2f 37
0f4ac132
JK
38struct nvram_os_partition {
39 const char *name;
40 int req_size; /* desired size, in bytes */
41 int min_size; /* minimum acceptable size (0 means req_size) */
42 long size; /* size of data portion of partition */
43 long index; /* offset of data portion of partition */
44};
45
46static struct nvram_os_partition rtas_log_partition = {
47 .name = "ibm,rtas-log",
48 .req_size = 2079,
49 .min_size = 1055,
50 .index = -1
51};
52
53static const char *pseries_nvram_os_partitions[] = {
54 "ibm,rtas-log",
55 NULL
56};
9a866b87 57
1da177e4
LT
58static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index)
59{
60 unsigned int i;
61 unsigned long len;
62 int done;
63 unsigned long flags;
64 char *p = buf;
65
66
67 if (nvram_size == 0 || nvram_fetch == RTAS_UNKNOWN_SERVICE)
68 return -ENODEV;
69
70 if (*index >= nvram_size)
71 return 0;
72
73 i = *index;
74 if (i + count > nvram_size)
75 count = nvram_size - i;
76
77 spin_lock_irqsave(&nvram_lock, flags);
78
79 for (; count != 0; count -= len) {
80 len = count;
81 if (len > NVRW_CNT)
82 len = NVRW_CNT;
83
84 if ((rtas_call(nvram_fetch, 3, 2, &done, i, __pa(nvram_buf),
85 len) != 0) || len != done) {
86 spin_unlock_irqrestore(&nvram_lock, flags);
87 return -EIO;
88 }
89
90 memcpy(p, nvram_buf, len);
91
92 p += len;
93 i += len;
94 }
95
96 spin_unlock_irqrestore(&nvram_lock, flags);
97
98 *index = i;
99 return p - buf;
100}
101
102static ssize_t pSeries_nvram_write(char *buf, size_t count, loff_t *index)
103{
104 unsigned int i;
105 unsigned long len;
106 int done;
107 unsigned long flags;
108 const char *p = buf;
109
110 if (nvram_size == 0 || nvram_store == RTAS_UNKNOWN_SERVICE)
111 return -ENODEV;
112
113 if (*index >= nvram_size)
114 return 0;
115
116 i = *index;
117 if (i + count > nvram_size)
118 count = nvram_size - i;
119
120 spin_lock_irqsave(&nvram_lock, flags);
121
122 for (; count != 0; count -= len) {
123 len = count;
124 if (len > NVRW_CNT)
125 len = NVRW_CNT;
126
127 memcpy(nvram_buf, p, len);
128
129 if ((rtas_call(nvram_store, 3, 2, &done, i, __pa(nvram_buf),
130 len) != 0) || len != done) {
131 spin_unlock_irqrestore(&nvram_lock, flags);
132 return -EIO;
133 }
134
135 p += len;
136 i += len;
137 }
138 spin_unlock_irqrestore(&nvram_lock, flags);
139
140 *index = i;
141 return p - buf;
142}
143
144static ssize_t pSeries_nvram_get_size(void)
145{
146 return nvram_size ? nvram_size : -ENODEV;
147}
148
edc79a2f 149
0f4ac132 150/* nvram_write_os_partition, nvram_write_error_log
edc79a2f
BH
151 *
152 * We need to buffer the error logs into nvram to ensure that we have
153 * the failure information to decode. If we have a severe error there
154 * is no way to guarantee that the OS or the machine is in a state to
155 * get back to user land and write the error to disk. For example if
156 * the SCSI device driver causes a Machine Check by writing to a bad
157 * IO address, there is no way of guaranteeing that the device driver
158 * is in any state that is would also be able to write the error data
159 * captured to disk, thus we buffer it in NVRAM for analysis on the
160 * next boot.
161 *
162 * In NVRAM the partition containing the error log buffer will looks like:
163 * Header (in bytes):
164 * +-----------+----------+--------+------------+------------------+
165 * | signature | checksum | length | name | data |
166 * |0 |1 |2 3|4 15|16 length-1|
167 * +-----------+----------+--------+------------+------------------+
168 *
169 * The 'data' section would look like (in bytes):
170 * +--------------+------------+-----------------------------------+
171 * | event_logged | sequence # | error log |
0f4ac132 172 * |0 3|4 7|8 error_log_size-1|
edc79a2f
BH
173 * +--------------+------------+-----------------------------------+
174 *
175 * event_logged: 0 if event has not been logged to syslog, 1 if it has
176 * sequence #: The unique sequence # for each event. (until it wraps)
177 * error log: The error log from event_scan
178 */
0f4ac132
JK
179int nvram_write_os_partition(struct nvram_os_partition *part, char * buff,
180 int length, unsigned int err_type, unsigned int error_log_cnt)
edc79a2f
BH
181{
182 int rc;
183 loff_t tmp_index;
184 struct err_log_info info;
185
0f4ac132 186 if (part->index == -1) {
edc79a2f
BH
187 return -ESPIPE;
188 }
189
0f4ac132
JK
190 if (length > part->size) {
191 length = part->size;
edc79a2f
BH
192 }
193
194 info.error_type = err_type;
195 info.seq_num = error_log_cnt;
196
0f4ac132 197 tmp_index = part->index;
edc79a2f
BH
198
199 rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index);
200 if (rc <= 0) {
0f4ac132 201 pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
edc79a2f
BH
202 return rc;
203 }
204
205 rc = ppc_md.nvram_write(buff, length, &tmp_index);
206 if (rc <= 0) {
0f4ac132 207 pr_err("%s: Failed nvram_write (%d)\n", __FUNCTION__, rc);
edc79a2f
BH
208 return rc;
209 }
210
211 return 0;
212}
213
0f4ac132
JK
214int nvram_write_error_log(char * buff, int length,
215 unsigned int err_type, unsigned int error_log_cnt)
216{
217 return nvram_write_os_partition(&rtas_log_partition, buff, length,
218 err_type, error_log_cnt);
219}
220
edc79a2f
BH
221/* nvram_read_error_log
222 *
223 * Reads nvram for error log for at most 'length'
224 */
225int nvram_read_error_log(char * buff, int length,
226 unsigned int * err_type, unsigned int * error_log_cnt)
227{
228 int rc;
229 loff_t tmp_index;
230 struct err_log_info info;
231
0f4ac132 232 if (rtas_log_partition.index == -1)
edc79a2f
BH
233 return -1;
234
0f4ac132
JK
235 if (length > rtas_log_partition.size)
236 length = rtas_log_partition.size;
edc79a2f 237
0f4ac132 238 tmp_index = rtas_log_partition.index;
edc79a2f
BH
239
240 rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index);
241 if (rc <= 0) {
242 printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
243 return rc;
244 }
245
246 rc = ppc_md.nvram_read(buff, length, &tmp_index);
247 if (rc <= 0) {
248 printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc);
249 return rc;
250 }
251
252 *error_log_cnt = info.seq_num;
253 *err_type = info.error_type;
254
255 return 0;
256}
257
258/* This doesn't actually zero anything, but it sets the event_logged
259 * word to tell that this event is safely in syslog.
260 */
261int nvram_clear_error_log(void)
262{
263 loff_t tmp_index;
264 int clear_word = ERR_FLAG_ALREADY_LOGGED;
265 int rc;
266
0f4ac132 267 if (rtas_log_partition.index == -1)
edc79a2f
BH
268 return -1;
269
0f4ac132 270 tmp_index = rtas_log_partition.index;
edc79a2f
BH
271
272 rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index);
273 if (rc <= 0) {
274 printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc);
275 return rc;
276 }
277
278 return 0;
279}
280
0f4ac132 281/* pseries_nvram_init_os_partition
edc79a2f 282 *
0f4ac132 283 * This sets up a partition with an "OS" signature.
edc79a2f
BH
284 *
285 * The general strategy is the following:
0f4ac132
JK
286 * 1.) If a partition with the indicated name already exists...
287 * - If it's large enough, use it.
288 * - Otherwise, recycle it and keep going.
289 * 2.) Search for a free partition that is large enough.
290 * 3.) If there's not a free partition large enough, recycle any obsolete
291 * OS partitions and try again.
292 * 4.) Will first try getting a chunk that will satisfy the requested size.
293 * 5.) If a chunk of the requested size cannot be allocated, then try finding
294 * a chunk that will satisfy the minum needed.
295 *
296 * Returns 0 on success, else -1.
edc79a2f 297 */
0f4ac132
JK
298static int __init pseries_nvram_init_os_partition(struct nvram_os_partition
299 *part)
edc79a2f
BH
300{
301 loff_t p;
302 int size;
303
304 /* Scan nvram for partitions */
305 nvram_scan_partitions();
306
0f4ac132
JK
307 /* Look for ours */
308 p = nvram_find_partition(part->name, NVRAM_SIG_OS, &size);
edc79a2f
BH
309
310 /* Found one but too small, remove it */
0f4ac132
JK
311 if (p && size < part->min_size) {
312 pr_info("nvram: Found too small %s partition,"
313 " removing it...\n", part->name);
314 nvram_remove_partition(part->name, NVRAM_SIG_OS, NULL);
edc79a2f
BH
315 p = 0;
316 }
317
318 /* Create one if we didn't find */
319 if (!p) {
0f4ac132
JK
320 p = nvram_create_partition(part->name, NVRAM_SIG_OS,
321 part->req_size, part->min_size);
edc79a2f 322 if (p == -ENOSPC) {
0f4ac132
JK
323 pr_info("nvram: No room to create %s partition, "
324 "deleting any obsolete OS partitions...\n",
325 part->name);
326 nvram_remove_partition(NULL, NVRAM_SIG_OS,
327 pseries_nvram_os_partitions);
328 p = nvram_create_partition(part->name, NVRAM_SIG_OS,
329 part->req_size, part->min_size);
edc79a2f
BH
330 }
331 }
332
333 if (p <= 0) {
0f4ac132
JK
334 pr_err("nvram: Failed to find or create %s"
335 " partition, err %d\n", part->name, (int)p);
336 return -1;
edc79a2f
BH
337 }
338
0f4ac132
JK
339 part->index = p;
340 part->size = nvram_get_partition_size(p) - sizeof(struct err_log_info);
edc79a2f
BH
341
342 return 0;
343}
0f4ac132
JK
344
345static int __init pseries_nvram_init_log_partitions(void)
346{
347 (void) pseries_nvram_init_os_partition(&rtas_log_partition);
348 return 0;
349}
350machine_arch_initcall(pseries, pseries_nvram_init_log_partitions);
edc79a2f 351
1da177e4
LT
352int __init pSeries_nvram_init(void)
353{
354 struct device_node *nvram;
954a46e2
JK
355 const unsigned int *nbytes_p;
356 unsigned int proplen;
1da177e4
LT
357
358 nvram = of_find_node_by_type(NULL, "nvram");
359 if (nvram == NULL)
360 return -ENODEV;
361
e2eb6392 362 nbytes_p = of_get_property(nvram, "#bytes", &proplen);
bad5232b
JL
363 if (nbytes_p == NULL || proplen != sizeof(unsigned int)) {
364 of_node_put(nvram);
1da177e4 365 return -EIO;
bad5232b 366 }
1da177e4
LT
367
368 nvram_size = *nbytes_p;
369
370 nvram_fetch = rtas_token("nvram-fetch");
371 nvram_store = rtas_token("nvram-store");
372 printk(KERN_INFO "PPC64 nvram contains %d bytes\n", nvram_size);
373 of_node_put(nvram);
374
375 ppc_md.nvram_read = pSeries_nvram_read;
376 ppc_md.nvram_write = pSeries_nvram_write;
377 ppc_md.nvram_size = pSeries_nvram_get_size;
378
379 return 0;
380}
This page took 0.481918 seconds and 5 git commands to generate.