|
@@ -36,8 +36,6 @@
|
|
|
|
|
|
#define NVRAM_HEADER_LEN sizeof(struct nvram_header)
|
|
#define NVRAM_HEADER_LEN sizeof(struct nvram_header)
|
|
#define NVRAM_BLOCK_LEN NVRAM_HEADER_LEN
|
|
#define NVRAM_BLOCK_LEN NVRAM_HEADER_LEN
|
|
-#define NVRAM_MAX_REQ 2079
|
|
|
|
-#define NVRAM_MIN_REQ 1055
|
|
|
|
|
|
|
|
/* If change this size, then change the size of NVNAME_LEN */
|
|
/* If change this size, then change the size of NVNAME_LEN */
|
|
struct nvram_header {
|
|
struct nvram_header {
|
|
@@ -54,13 +52,6 @@ struct nvram_partition {
|
|
};
|
|
};
|
|
|
|
|
|
static struct nvram_partition * nvram_part;
|
|
static struct nvram_partition * nvram_part;
|
|
-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;
|
|
|
|
-};
|
|
|
|
|
|
|
|
static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
|
|
static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
|
|
{
|
|
{
|
|
@@ -254,7 +245,7 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
|
|
* @sig: signature of the partition(s) to remove
|
|
* @sig: signature of the partition(s) to remove
|
|
*/
|
|
*/
|
|
|
|
|
|
-static int __init nvram_remove_partition(const char *name, int sig)
|
|
|
|
|
|
+int __init nvram_remove_partition(const char *name, int sig)
|
|
{
|
|
{
|
|
struct nvram_partition *part, *prev, *tmp;
|
|
struct nvram_partition *part, *prev, *tmp;
|
|
int rc;
|
|
int rc;
|
|
@@ -313,8 +304,8 @@ static int __init nvram_remove_partition(const char *name, int sig)
|
|
* you need to query for the actual size yourself after the
|
|
* you need to query for the actual size yourself after the
|
|
* call using nvram_partition_get_size().
|
|
* call using nvram_partition_get_size().
|
|
*/
|
|
*/
|
|
-static loff_t __init nvram_create_partition(const char *name, int sig,
|
|
|
|
- int req_size, int min_size)
|
|
|
|
|
|
+loff_t __init nvram_create_partition(const char *name, int sig,
|
|
|
|
+ int req_size, int min_size)
|
|
{
|
|
{
|
|
struct nvram_partition *part;
|
|
struct nvram_partition *part;
|
|
struct nvram_partition *new_part;
|
|
struct nvram_partition *new_part;
|
|
@@ -417,7 +408,7 @@ static loff_t __init nvram_create_partition(const char *name, int sig,
|
|
* the partition. The same value that is returned by
|
|
* the partition. The same value that is returned by
|
|
* nvram_create_partition().
|
|
* nvram_create_partition().
|
|
*/
|
|
*/
|
|
-static int nvram_get_partition_size(loff_t data_index)
|
|
|
|
|
|
+int nvram_get_partition_size(loff_t data_index)
|
|
{
|
|
{
|
|
struct nvram_partition *part;
|
|
struct nvram_partition *part;
|
|
|
|
|
|
@@ -451,75 +442,7 @@ loff_t nvram_find_partition(const char *name, int sig, int *out_size)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-/* nvram_setup_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 nvram_setup_partition(void)
|
|
|
|
-{
|
|
|
|
- loff_t p;
|
|
|
|
- int size;
|
|
|
|
-
|
|
|
|
- /* For now, we don't do any of this on pmac, until I
|
|
|
|
- * have figured out if it's worth killing some unused stuffs
|
|
|
|
- * in our nvram, as Apple defined partitions use pretty much
|
|
|
|
- * all of the space
|
|
|
|
- */
|
|
|
|
- if (machine_is(powermac))
|
|
|
|
- return -ENOSPC;
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int __init nvram_scan_partitions(void)
|
|
|
|
|
|
+int __init nvram_scan_partitions(void)
|
|
{
|
|
{
|
|
loff_t cur_index = 0;
|
|
loff_t cur_index = 0;
|
|
struct nvram_header phead;
|
|
struct nvram_header phead;
|
|
@@ -529,7 +452,15 @@ static int __init nvram_scan_partitions(void)
|
|
int total_size;
|
|
int total_size;
|
|
int err;
|
|
int err;
|
|
|
|
|
|
- if (ppc_md.nvram_size == NULL)
|
|
|
|
|
|
+ /* Initialize our anchor for the nvram partition list */
|
|
|
|
+ nvram_part = kmalloc(sizeof(struct nvram_partition), GFP_KERNEL);
|
|
|
|
+ if (!nvram_part) {
|
|
|
|
+ printk(KERN_ERR "nvram_init: Failed kmalloc\n");
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ }
|
|
|
|
+ INIT_LIST_HEAD(&nvram_part->partition);
|
|
|
|
+
|
|
|
|
+ if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
total_size = ppc_md.nvram_size();
|
|
total_size = ppc_md.nvram_size();
|
|
|
|
|
|
@@ -582,6 +513,10 @@ static int __init nvram_scan_partitions(void)
|
|
}
|
|
}
|
|
err = 0;
|
|
err = 0;
|
|
|
|
|
|
|
|
+#ifdef DEBUG_NVRAM
|
|
|
|
+ nvram_print_partitions("NVRAM Partitions");
|
|
|
|
+#endif
|
|
|
|
+
|
|
out:
|
|
out:
|
|
kfree(header);
|
|
kfree(header);
|
|
return err;
|
|
return err;
|
|
@@ -589,7 +524,6 @@ static int __init nvram_scan_partitions(void)
|
|
|
|
|
|
static int __init nvram_init(void)
|
|
static int __init nvram_init(void)
|
|
{
|
|
{
|
|
- int error;
|
|
|
|
int rc;
|
|
int rc;
|
|
|
|
|
|
BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
|
|
BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
|
|
@@ -603,29 +537,6 @@ static int __init nvram_init(void)
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
- /* initialize our anchor for the nvram partition list */
|
|
|
|
- nvram_part = kmalloc(sizeof(struct nvram_partition), GFP_KERNEL);
|
|
|
|
- if (!nvram_part) {
|
|
|
|
- printk(KERN_ERR "nvram_init: Failed kmalloc\n");
|
|
|
|
- return -ENOMEM;
|
|
|
|
- }
|
|
|
|
- INIT_LIST_HEAD(&nvram_part->partition);
|
|
|
|
-
|
|
|
|
- /* Get all the NVRAM partitions */
|
|
|
|
- error = nvram_scan_partitions();
|
|
|
|
- if (error) {
|
|
|
|
- printk(KERN_ERR "nvram_init: Failed nvram_scan_partitions\n");
|
|
|
|
- return error;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if(nvram_setup_partition())
|
|
|
|
- printk(KERN_WARNING "nvram_init: Could not find nvram partition"
|
|
|
|
- " for nvram buffered error logging.\n");
|
|
|
|
-
|
|
|
|
-#ifdef DEBUG_NVRAM
|
|
|
|
- nvram_print_partitions("NVRAM Partitions");
|
|
|
|
-#endif
|
|
|
|
-
|
|
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -634,135 +545,6 @@ void __exit nvram_cleanup(void)
|
|
misc_deregister( &nvram_dev );
|
|
misc_deregister( &nvram_dev );
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
-#ifdef CONFIG_PPC_PSERIES
|
|
|
|
-
|
|
|
|
-/* 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;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#endif /* CONFIG_PPC_PSERIES */
|
|
|
|
-
|
|
|
|
module_init(nvram_init);
|
|
module_init(nvram_init);
|
|
module_exit(nvram_cleanup);
|
|
module_exit(nvram_cleanup);
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_LICENSE("GPL");
|