diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries/nvram.c')
-rw-r--r-- | arch/powerpc/platforms/pseries/nvram.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index bc3c7f2abd79..7e828ba29bc3 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c | |||
@@ -22,11 +22,25 @@ | |||
22 | #include <asm/prom.h> | 22 | #include <asm/prom.h> |
23 | #include <asm/machdep.h> | 23 | #include <asm/machdep.h> |
24 | 24 | ||
25 | /* Max bytes to read/write in one go */ | ||
26 | #define NVRW_CNT 0x20 | ||
27 | |||
25 | static unsigned int nvram_size; | 28 | static unsigned int nvram_size; |
26 | static int nvram_fetch, nvram_store; | 29 | static int nvram_fetch, nvram_store; |
27 | static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ | 30 | static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ |
28 | static DEFINE_SPINLOCK(nvram_lock); | 31 | static DEFINE_SPINLOCK(nvram_lock); |
29 | 32 | ||
33 | static long nvram_error_log_index = -1; | ||
34 | static long nvram_error_log_size = 0; | ||
35 | |||
36 | struct err_log_info { | ||
37 | int error_type; | ||
38 | unsigned int seq_num; | ||
39 | }; | ||
40 | #define NVRAM_MAX_REQ 2079 | ||
41 | #define NVRAM_MIN_REQ 1055 | ||
42 | |||
43 | #define NVRAM_LOG_PART_NAME "ibm,rtas-log" | ||
30 | 44 | ||
31 | static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) | 45 | static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) |
32 | { | 46 | { |
@@ -119,6 +133,197 @@ static ssize_t pSeries_nvram_get_size(void) | |||
119 | return nvram_size ? nvram_size : -ENODEV; | 133 | return nvram_size ? nvram_size : -ENODEV; |
120 | } | 134 | } |
121 | 135 | ||
136 | |||
137 | /* nvram_write_error_log | ||
138 | * | ||
139 | * We need to buffer the error logs into nvram to ensure that we have | ||
140 | * the failure information to decode. If we have a severe error there | ||
141 | * is no way to guarantee that the OS or the machine is in a state to | ||
142 | * get back to user land and write the error to disk. For example if | ||
143 | * the SCSI device driver causes a Machine Check by writing to a bad | ||
144 | * IO address, there is no way of guaranteeing that the device driver | ||
145 | * is in any state that is would also be able to write the error data | ||
146 | * captured to disk, thus we buffer it in NVRAM for analysis on the | ||
147 | * next boot. | ||
148 | * | ||
149 | * In NVRAM the partition containing the error log buffer will looks like: | ||
150 | * Header (in bytes): | ||
151 | * +-----------+----------+--------+------------+------------------+ | ||
152 | * | signature | checksum | length | name | data | | ||
153 | * |0 |1 |2 3|4 15|16 length-1| | ||
154 | * +-----------+----------+--------+------------+------------------+ | ||
155 | * | ||
156 | * The 'data' section would look like (in bytes): | ||
157 | * +--------------+------------+-----------------------------------+ | ||
158 | * | event_logged | sequence # | error log | | ||
159 | * |0 3|4 7|8 nvram_error_log_size-1| | ||
160 | * +--------------+------------+-----------------------------------+ | ||
161 | * | ||
162 | * event_logged: 0 if event has not been logged to syslog, 1 if it has | ||
163 | * sequence #: The unique sequence # for each event. (until it wraps) | ||
164 | * error log: The error log from event_scan | ||
165 | */ | ||
166 | int nvram_write_error_log(char * buff, int length, | ||
167 | unsigned int err_type, unsigned int error_log_cnt) | ||
168 | { | ||
169 | int rc; | ||
170 | loff_t tmp_index; | ||
171 | struct err_log_info info; | ||
172 | |||
173 | if (nvram_error_log_index == -1) { | ||
174 | return -ESPIPE; | ||
175 | } | ||
176 | |||
177 | if (length > nvram_error_log_size) { | ||
178 | length = nvram_error_log_size; | ||
179 | } | ||
180 | |||
181 | info.error_type = err_type; | ||
182 | info.seq_num = error_log_cnt; | ||
183 | |||
184 | tmp_index = nvram_error_log_index; | ||
185 | |||
186 | rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); | ||
187 | if (rc <= 0) { | ||
188 | printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); | ||
189 | return rc; | ||
190 | } | ||
191 | |||
192 | rc = ppc_md.nvram_write(buff, length, &tmp_index); | ||
193 | if (rc <= 0) { | ||
194 | printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); | ||
195 | return rc; | ||
196 | } | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | /* nvram_read_error_log | ||
202 | * | ||
203 | * Reads nvram for error log for at most 'length' | ||
204 | */ | ||
205 | int nvram_read_error_log(char * buff, int length, | ||
206 | unsigned int * err_type, unsigned int * error_log_cnt) | ||
207 | { | ||
208 | int rc; | ||
209 | loff_t tmp_index; | ||
210 | struct err_log_info info; | ||
211 | |||
212 | if (nvram_error_log_index == -1) | ||
213 | return -1; | ||
214 | |||
215 | if (length > nvram_error_log_size) | ||
216 | length = nvram_error_log_size; | ||
217 | |||
218 | tmp_index = nvram_error_log_index; | ||
219 | |||
220 | rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index); | ||
221 | if (rc <= 0) { | ||
222 | printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); | ||
223 | return rc; | ||
224 | } | ||
225 | |||
226 | rc = ppc_md.nvram_read(buff, length, &tmp_index); | ||
227 | if (rc <= 0) { | ||
228 | printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); | ||
229 | return rc; | ||
230 | } | ||
231 | |||
232 | *error_log_cnt = info.seq_num; | ||
233 | *err_type = info.error_type; | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* This doesn't actually zero anything, but it sets the event_logged | ||
239 | * word to tell that this event is safely in syslog. | ||
240 | */ | ||
241 | int nvram_clear_error_log(void) | ||
242 | { | ||
243 | loff_t tmp_index; | ||
244 | int clear_word = ERR_FLAG_ALREADY_LOGGED; | ||
245 | int rc; | ||
246 | |||
247 | if (nvram_error_log_index == -1) | ||
248 | return -1; | ||
249 | |||
250 | tmp_index = nvram_error_log_index; | ||
251 | |||
252 | rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index); | ||
253 | if (rc <= 0) { | ||
254 | printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc); | ||
255 | return rc; | ||
256 | } | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | /* pseries_nvram_init_log_partition | ||
262 | * | ||
263 | * This will setup the partition we need for buffering the | ||
264 | * error logs and cleanup partitions if needed. | ||
265 | * | ||
266 | * The general strategy is the following: | ||
267 | * 1.) If there is log partition large enough then use it. | ||
268 | * 2.) If there is none large enough, search | ||
269 | * for a free partition that is large enough. | ||
270 | * 3.) If there is not a free partition large enough remove | ||
271 | * _all_ OS partitions and consolidate the space. | ||
272 | * 4.) Will first try getting a chunk that will satisfy the maximum | ||
273 | * error log size (NVRAM_MAX_REQ). | ||
274 | * 5.) If the max chunk cannot be allocated then try finding a chunk | ||
275 | * that will satisfy the minum needed (NVRAM_MIN_REQ). | ||
276 | */ | ||
277 | static int __init pseries_nvram_init_log_partition(void) | ||
278 | { | ||
279 | loff_t p; | ||
280 | int size; | ||
281 | |||
282 | /* Scan nvram for partitions */ | ||
283 | nvram_scan_partitions(); | ||
284 | |||
285 | /* Lookg for ours */ | ||
286 | p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size); | ||
287 | |||
288 | /* Found one but too small, remove it */ | ||
289 | if (p && size < NVRAM_MIN_REQ) { | ||
290 | pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition" | ||
291 | ",removing it..."); | ||
292 | nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS); | ||
293 | p = 0; | ||
294 | } | ||
295 | |||
296 | /* Create one if we didn't find */ | ||
297 | if (!p) { | ||
298 | p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, | ||
299 | NVRAM_MAX_REQ, NVRAM_MIN_REQ); | ||
300 | /* No room for it, try to get rid of any OS partition | ||
301 | * and try again | ||
302 | */ | ||
303 | if (p == -ENOSPC) { | ||
304 | pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME | ||
305 | " partition, deleting all OS partitions..."); | ||
306 | nvram_remove_partition(NULL, NVRAM_SIG_OS); | ||
307 | p = nvram_create_partition(NVRAM_LOG_PART_NAME, | ||
308 | NVRAM_SIG_OS, NVRAM_MAX_REQ, | ||
309 | NVRAM_MIN_REQ); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | if (p <= 0) { | ||
314 | pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME | ||
315 | " partition, err %d\n", (int)p); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | nvram_error_log_index = p; | ||
320 | nvram_error_log_size = nvram_get_partition_size(p) - | ||
321 | sizeof(struct err_log_info); | ||
322 | |||
323 | return 0; | ||
324 | } | ||
325 | machine_arch_initcall(pseries, pseries_nvram_init_log_partition); | ||
326 | |||
122 | int __init pSeries_nvram_init(void) | 327 | int __init pSeries_nvram_init(void) |
123 | { | 328 | { |
124 | struct device_node *nvram; | 329 | struct device_node *nvram; |