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