From 74d51d029818eca9d1aec22dd2895e269c0044b1 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Jul 2010 14:45:24 +1000 Subject: powerpc/nvram: Move things out of asm/nvram.h This moves a bunch of definitions out of asm/nvram.h to the files that use them or just outright remove completely unused stuff. We leave the partition signatures definitions, they will be useful Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/chrp/time.c | 4 ++++ arch/powerpc/platforms/pseries/nvram.c | 2 ++ 2 files changed, 6 insertions(+) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/chrp/time.c b/arch/powerpc/platforms/chrp/time.c index 054dfe5b8e77..f803f4b8ab6f 100644 --- a/arch/powerpc/platforms/chrp/time.c +++ b/arch/powerpc/platforms/chrp/time.c @@ -29,6 +29,10 @@ extern spinlock_t rtc_lock; +#define NVRAM_AS0 0x74 +#define NVRAM_AS1 0x75 +#define NVRAM_DATA 0x77 + static int nvram_as1 = NVRAM_AS1; static int nvram_as0 = NVRAM_AS0; static int nvram_data = NVRAM_DATA; diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index bc3c7f2abd79..f4e4c06284b4 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -27,6 +27,8 @@ static int nvram_fetch, nvram_store; static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ static DEFINE_SPINLOCK(nvram_lock); +/* Max bytes to read/write in one go */ +#define NVRW_CNT 0x20 static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) { -- cgit v1.2.2 From 4e7c77a385efac81d6677a4a761b1b66cd2cb59e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 29 Jul 2010 15:28:20 +1000 Subject: powerpc/nvram: More flexible nvram_create_partition() Replace nvram_create_os_partition() with a variant that takes the partition name, signature and size as arguments. Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/nvram.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index f4e4c06284b4..2a1ef5c25344 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -22,14 +22,14 @@ #include #include +/* Max bytes to read/write in one go */ +#define NVRW_CNT 0x20 + static unsigned int nvram_size; static int nvram_fetch, nvram_store; static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ static DEFINE_SPINLOCK(nvram_lock); -/* Max bytes to read/write in one go */ -#define NVRW_CNT 0x20 - static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) { unsigned int i; -- cgit v1.2.2 From edc79a2f3ee1c74d915f6a0ce3cb22bf468f5ad5 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 2 Aug 2010 11:18:09 +1000 Subject: 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 --- arch/powerpc/platforms/pseries/nvram.c | 200 +++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) (limited to 'arch/powerpc/platforms') 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; static char nvram_buf[NVRW_CNT]; /* assume this is in the first 4GB */ static DEFINE_SPINLOCK(nvram_lock); +static long nvram_error_log_index = -1; +static long nvram_error_log_size = 0; + +struct err_log_info { + int error_type; + unsigned int seq_num; +}; +#define NVRAM_MAX_REQ 2079 +#define NVRAM_MIN_REQ 1055 + static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) { unsigned int i; @@ -121,6 +131,196 @@ static ssize_t pSeries_nvram_get_size(void) return nvram_size ? nvram_size : -ENODEV; } + +/* nvram_write_error_log + * + * We need to buffer the error logs into nvram to ensure that we have + * the failure information to decode. If we have a severe error there + * is no way to guarantee that the OS or the machine is in a state to + * get back to user land and write the error to disk. For example if + * the SCSI device driver causes a Machine Check by writing to a bad + * IO address, there is no way of guaranteeing that the device driver + * is in any state that is would also be able to write the error data + * captured to disk, thus we buffer it in NVRAM for analysis on the + * next boot. + * + * In NVRAM the partition containing the error log buffer will looks like: + * Header (in bytes): + * +-----------+----------+--------+------------+------------------+ + * | signature | checksum | length | name | data | + * |0 |1 |2 3|4 15|16 length-1| + * +-----------+----------+--------+------------+------------------+ + * + * The 'data' section would look like (in bytes): + * +--------------+------------+-----------------------------------+ + * | event_logged | sequence # | error log | + * |0 3|4 7|8 nvram_error_log_size-1| + * +--------------+------------+-----------------------------------+ + * + * event_logged: 0 if event has not been logged to syslog, 1 if it has + * sequence #: The unique sequence # for each event. (until it wraps) + * error log: The error log from event_scan + */ +int nvram_write_error_log(char * buff, int length, + unsigned int err_type, unsigned int error_log_cnt) +{ + int rc; + loff_t tmp_index; + struct err_log_info info; + + if (nvram_error_log_index == -1) { + return -ESPIPE; + } + + if (length > nvram_error_log_size) { + length = nvram_error_log_size; + } + + info.error_type = err_type; + info.seq_num = error_log_cnt; + + tmp_index = nvram_error_log_index; + + rc = ppc_md.nvram_write((char *)&info, sizeof(struct err_log_info), &tmp_index); + if (rc <= 0) { + printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); + return rc; + } + + rc = ppc_md.nvram_write(buff, length, &tmp_index); + if (rc <= 0) { + printk(KERN_ERR "nvram_write_error_log: Failed nvram_write (%d)\n", rc); + return rc; + } + + return 0; +} + +/* nvram_read_error_log + * + * Reads nvram for error log for at most 'length' + */ +int nvram_read_error_log(char * buff, int length, + unsigned int * err_type, unsigned int * error_log_cnt) +{ + int rc; + loff_t tmp_index; + struct err_log_info info; + + if (nvram_error_log_index == -1) + return -1; + + if (length > nvram_error_log_size) + length = nvram_error_log_size; + + tmp_index = nvram_error_log_index; + + rc = ppc_md.nvram_read((char *)&info, sizeof(struct err_log_info), &tmp_index); + if (rc <= 0) { + printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); + return rc; + } + + rc = ppc_md.nvram_read(buff, length, &tmp_index); + if (rc <= 0) { + printk(KERN_ERR "nvram_read_error_log: Failed nvram_read (%d)\n", rc); + return rc; + } + + *error_log_cnt = info.seq_num; + *err_type = info.error_type; + + return 0; +} + +/* This doesn't actually zero anything, but it sets the event_logged + * word to tell that this event is safely in syslog. + */ +int nvram_clear_error_log(void) +{ + loff_t tmp_index; + int clear_word = ERR_FLAG_ALREADY_LOGGED; + int rc; + + if (nvram_error_log_index == -1) + return -1; + + tmp_index = nvram_error_log_index; + + rc = ppc_md.nvram_write((char *)&clear_word, sizeof(int), &tmp_index); + if (rc <= 0) { + printk(KERN_ERR "nvram_clear_error_log: Failed nvram_write (%d)\n", rc); + return rc; + } + + return 0; +} + +/* pseries_nvram_init_log_partition + * + * This will setup the partition we need for buffering the + * error logs and cleanup partitions if needed. + * + * The general strategy is the following: + * 1.) If there is ppc64,linux partition large enough then use it. + * 2.) If there is not a ppc64,linux partition large enough, search + * for a free partition that is large enough. + * 3.) If there is not a free partition large enough remove + * _all_ OS partitions and consolidate the space. + * 4.) Will first try getting a chunk that will satisfy the maximum + * error log size (NVRAM_MAX_REQ). + * 5.) If the max chunk cannot be allocated then try finding a chunk + * that will satisfy the minum needed (NVRAM_MIN_REQ). + */ +static int __init pseries_nvram_init_log_partition(void) +{ + loff_t p; + int size; + + /* Scan nvram for partitions */ + nvram_scan_partitions(); + + /* Lookg for ours */ + p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size); + + /* Found one but too small, remove it */ + if (p && size < NVRAM_MIN_REQ) { + pr_info("nvram: Found too small ppc64,linux partition" + ",removing it..."); + nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS); + p = 0; + } + + /* Create one if we didn't find */ + if (!p) { + p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS, + NVRAM_MAX_REQ, NVRAM_MIN_REQ); + /* No room for it, try to get rid of any OS partition + * and try again + */ + if (p == -ENOSPC) { + pr_info("nvram: No room to create ppc64,linux" + " partition, deleting all OS partitions..."); + nvram_remove_partition(NULL, NVRAM_SIG_OS); + p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS, + NVRAM_MAX_REQ, NVRAM_MIN_REQ); + } + } + + if (p <= 0) { + pr_err("nvram: Failed to find or create ppc64,linux" + " partition, err %d\n", (int)p); + return 0; + } + + nvram_error_log_index = p; + nvram_error_log_size = nvram_get_partition_size(p) - + sizeof(struct err_log_info); + + return 0; +} +machine_arch_initcall(pseries, pseries_nvram_init_log_partition); + int __init pSeries_nvram_init(void) { struct device_node *nvram; -- cgit v1.2.2 From 9a866b870880065364c894823257e6982103d4e4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 2 Aug 2010 10:51:25 +1000 Subject: powerpc/nvram: Rename ppc64,linux partition to ibm,rtas-log I'm not aware of any userspace tool accessing it by its name anyways, it's read back by the kernel itself on the next boot to get back older log entries Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/nvram.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'arch/powerpc/platforms') diff --git a/arch/powerpc/platforms/pseries/nvram.c b/arch/powerpc/platforms/pseries/nvram.c index 55a7141131f1..7e828ba29bc3 100644 --- a/arch/powerpc/platforms/pseries/nvram.c +++ b/arch/powerpc/platforms/pseries/nvram.c @@ -40,6 +40,8 @@ struct err_log_info { #define NVRAM_MAX_REQ 2079 #define NVRAM_MIN_REQ 1055 +#define NVRAM_LOG_PART_NAME "ibm,rtas-log" + static ssize_t pSeries_nvram_read(char *buf, size_t count, loff_t *index) { unsigned int i; @@ -262,8 +264,8 @@ int nvram_clear_error_log(void) * error logs and cleanup partitions if needed. * * The general strategy is the following: - * 1.) If there is ppc64,linux partition large enough then use it. - * 2.) If there is not a ppc64,linux partition large enough, search + * 1.) If there is log partition large enough then use it. + * 2.) If there is none large enough, search * for a free partition that is large enough. * 3.) If there is not a free partition large enough remove * _all_ OS partitions and consolidate the space. @@ -281,34 +283,35 @@ static int __init pseries_nvram_init_log_partition(void) nvram_scan_partitions(); /* Lookg for ours */ - p = nvram_find_partition("ppc64,linux", NVRAM_SIG_OS, &size); + p = nvram_find_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, &size); /* Found one but too small, remove it */ if (p && size < NVRAM_MIN_REQ) { - pr_info("nvram: Found too small ppc64,linux partition" + pr_info("nvram: Found too small "NVRAM_LOG_PART_NAME" partition" ",removing it..."); - nvram_remove_partition("ppc64,linux", NVRAM_SIG_OS); + nvram_remove_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS); p = 0; } /* Create one if we didn't find */ if (!p) { - p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS, + p = nvram_create_partition(NVRAM_LOG_PART_NAME, NVRAM_SIG_OS, NVRAM_MAX_REQ, NVRAM_MIN_REQ); /* No room for it, try to get rid of any OS partition * and try again */ if (p == -ENOSPC) { - pr_info("nvram: No room to create ppc64,linux" + pr_info("nvram: No room to create "NVRAM_LOG_PART_NAME " partition, deleting all OS partitions..."); nvram_remove_partition(NULL, NVRAM_SIG_OS); - p = nvram_create_partition("ppc64,linux", NVRAM_SIG_OS, - NVRAM_MAX_REQ, NVRAM_MIN_REQ); + p = nvram_create_partition(NVRAM_LOG_PART_NAME, + NVRAM_SIG_OS, NVRAM_MAX_REQ, + NVRAM_MIN_REQ); } } if (p <= 0) { - pr_err("nvram: Failed to find or create ppc64,linux" + pr_err("nvram: Failed to find or create "NVRAM_LOG_PART_NAME " partition, err %d\n", (int)p); return 0; } -- cgit v1.2.2