]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/powerpc/kernel/nvram_64.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / arch / powerpc / kernel / nvram_64.c
index 9cf197f01e943f1ffb95ad441436e5ee6486ded3..bb12b3248f1310ea955f8eb444c81c36463f19c6 100644 (file)
 
 #undef DEBUG_NVRAM
 
-static struct nvram_partition * nvram_part;
-static long nvram_error_log_index = -1;
-static long nvram_error_log_size = 0;
+#define NVRAM_HEADER_LEN       sizeof(struct nvram_header)
+#define NVRAM_BLOCK_LEN                NVRAM_HEADER_LEN
+
+/* If change this size, then change the size of NVNAME_LEN */
+struct nvram_header {
+       unsigned char signature;
+       unsigned char checksum;
+       unsigned short length;
+       /* Terminating null required only for names < 12 chars. */
+       char name[12];
+};
 
-struct err_log_info {
-       int error_type;
-       unsigned int seq_num;
+struct nvram_partition {
+       struct list_head partition;
+       struct nvram_header header;
+       unsigned int index;
 };
 
+static LIST_HEAD(nvram_partitions);
+
 static loff_t dev_nvram_llseek(struct file *file, loff_t offset, int origin)
 {
        int size;
@@ -186,14 +197,12 @@ static struct miscdevice nvram_dev = {
 #ifdef DEBUG_NVRAM
 static void __init nvram_print_partitions(char * label)
 {
-       struct list_head * p;
        struct nvram_partition * tmp_part;
        
        printk(KERN_WARNING "--------%s---------\n", label);
        printk(KERN_WARNING "indx\t\tsig\tchks\tlen\tname\n");
-       list_for_each(p, &nvram_part->partition) {
-               tmp_part = list_entry(p, struct nvram_partition, partition);
-               printk(KERN_WARNING "%4d    \t%02x\t%02x\t%d\t%s\n",
+       list_for_each_entry(tmp_part, &nvram_partitions, partition) {
+               printk(KERN_WARNING "%4d    \t%02x\t%02x\t%d\t%12s\n",
                       tmp_part->index, tmp_part->header.signature,
                       tmp_part->header.checksum, tmp_part->header.length,
                       tmp_part->header.name);
@@ -228,95 +237,113 @@ static unsigned char __init nvram_checksum(struct nvram_header *p)
        return c_sum;
 }
 
-static int __init nvram_remove_os_partition(void)
+/**
+ * nvram_remove_partition - Remove one or more partitions in nvram
+ * @name: name of the partition to remove, or NULL for a
+ *        signature only match
+ * @sig: signature of the partition(s) to remove
+ */
+
+int __init nvram_remove_partition(const char *name, int sig)
 {
-       struct list_head *i;
-       struct list_head *j;
-       struct nvram_partition * part;
-       struct nvram_partition * cur_part;
+       struct nvram_partition *part, *prev, *tmp;
        int rc;
 
-       list_for_each(i, &nvram_part->partition) {
-               part = list_entry(i, struct nvram_partition, partition);
-               if (part->header.signature != NVRAM_SIG_OS)
+       list_for_each_entry(part, &nvram_partitions, partition) {
+               if (part->header.signature != sig)
                        continue;
-               
-               /* Make os partition a free partition */
+               if (name && strncmp(name, part->header.name, 12))
+                       continue;
+
+               /* Make partition a free partition */
                part->header.signature = NVRAM_SIG_FREE;
-               sprintf(part->header.name, "wwwwwwwwwwww");
+               strncpy(part->header.name, "wwwwwwwwwwww", 12);
                part->header.checksum = nvram_checksum(&part->header);
-
-               /* Merge contiguous free partitions backwards */
-               list_for_each_prev(j, &part->partition) {
-                       cur_part = list_entry(j, struct nvram_partition, partition);
-                       if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) {
-                               break;
-                       }
-                       
-                       part->header.length += cur_part->header.length;
-                       part->header.checksum = nvram_checksum(&part->header);
-                       part->index = cur_part->index;
-
-                       list_del(&cur_part->partition);
-                       kfree(cur_part);
-                       j = &part->partition; /* fixup our loop */
-               }
-               
-               /* Merge contiguous free partitions forwards */
-               list_for_each(j, &part->partition) {
-                       cur_part = list_entry(j, struct nvram_partition, partition);
-                       if (cur_part == nvram_part || cur_part->header.signature != NVRAM_SIG_FREE) {
-                               break;
-                       }
-
-                       part->header.length += cur_part->header.length;
-                       part->header.checksum = nvram_checksum(&part->header);
-
-                       list_del(&cur_part->partition);
-                       kfree(cur_part);
-                       j = &part->partition; /* fixup our loop */
-               }
-               
                rc = nvram_write_header(part);
                if (rc <= 0) {
-                       printk(KERN_ERR "nvram_remove_os_partition: nvram_write failed (%d)\n", rc);
+                       printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
                        return rc;
                }
+       }
 
+       /* Merge contiguous ones */
+       prev = NULL;
+       list_for_each_entry_safe(part, tmp, &nvram_partitions, partition) {
+               if (part->header.signature != NVRAM_SIG_FREE) {
+                       prev = NULL;
+                       continue;
+               }
+               if (prev) {
+                       prev->header.length += part->header.length;
+                       prev->header.checksum = nvram_checksum(&part->header);
+                       rc = nvram_write_header(part);
+                       if (rc <= 0) {
+                               printk(KERN_ERR "nvram_remove_partition: nvram_write failed (%d)\n", rc);
+                               return rc;
+                       }
+                       list_del(&part->partition);
+                       kfree(part);
+               } else
+                       prev = part;
        }
        
        return 0;
 }
 
-/* nvram_create_os_partition
+/**
+ * nvram_create_partition - Create a partition in nvram
+ * @name: name of the partition to create
+ * @sig: signature of the partition to create
+ * @req_size: size of data to allocate in bytes
+ * @min_size: minimum acceptable size (0 means req_size)
  *
- * Create a OS linux partition to buffer error logs.
- * Will create a partition starting at the first free
- * space found if space has enough room.
+ * Returns a negative error code or a positive nvram index
+ * of the beginning of the data area of the newly created
+ * partition. If you provided a min_size smaller than req_size
+ * you need to query for the actual size yourself after the
+ * call using nvram_partition_get_size().
  */
-static int __init nvram_create_os_partition(void)
+loff_t __init nvram_create_partition(const char *name, int sig,
+                                    int req_size, int min_size)
 {
        struct nvram_partition *part;
        struct nvram_partition *new_part;
        struct nvram_partition *free_part = NULL;
-       int seq_init[2] = { 0, 0 };
+       static char nv_init_vals[16];
        loff_t tmp_index;
        long size = 0;
        int rc;
-       
+
+       /* Convert sizes from bytes to blocks */
+       req_size = _ALIGN_UP(req_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
+       min_size = _ALIGN_UP(min_size, NVRAM_BLOCK_LEN) / NVRAM_BLOCK_LEN;
+
+       /* If no minimum size specified, make it the same as the
+        * requested size
+        */
+       if (min_size == 0)
+               min_size = req_size;
+       if (min_size > req_size)
+               return -EINVAL;
+
+       /* Now add one block to each for the header */
+       req_size += 1;
+       min_size += 1;
+
        /* Find a free partition that will give us the maximum needed size 
           If can't find one that will give us the minimum size needed */
-       list_for_each_entry(part, &nvram_part->partition, partition) {
+       list_for_each_entry(part, &nvram_partitions, partition) {
                if (part->header.signature != NVRAM_SIG_FREE)
                        continue;
 
-               if (part->header.length >= NVRAM_MAX_REQ) {
-                       size = NVRAM_MAX_REQ;
+               if (part->header.length >= req_size) {
+                       size = req_size;
                        free_part = part;
                        break;
                }
-               if (!size && part->header.length >= NVRAM_MIN_REQ) {
-                       size = NVRAM_MIN_REQ;
+               if (part->header.length > size &&
+                   part->header.length >= min_size) {
+                       size = part->header.length;
                        free_part = part;
                }
        }
@@ -326,136 +353,95 @@ static int __init nvram_create_os_partition(void)
        /* Create our OS partition */
        new_part = kmalloc(sizeof(*new_part), GFP_KERNEL);
        if (!new_part) {
-               printk(KERN_ERR "nvram_create_os_partition: kmalloc failed\n");
+               pr_err("nvram_create_os_partition: kmalloc failed\n");
                return -ENOMEM;
        }
 
        new_part->index = free_part->index;
-       new_part->header.signature = NVRAM_SIG_OS;
+       new_part->header.signature = sig;
        new_part->header.length = size;
-       strcpy(new_part->header.name, "ppc64,linux");
+       strncpy(new_part->header.name, name, 12);
        new_part->header.checksum = nvram_checksum(&new_part->header);
 
        rc = nvram_write_header(new_part);
        if (rc <= 0) {
-               printk(KERN_ERR "nvram_create_os_partition: nvram_write_header "
-                               "failed (%d)\n", rc);
-               return rc;
-       }
-
-       /* make sure and initialize to zero the sequence number and the error
-          type logged */
-       tmp_index = new_part->index + NVRAM_HEADER_LEN;
-       rc = ppc_md.nvram_write((char *)&seq_init, sizeof(seq_init), &tmp_index);
-       if (rc <= 0) {
-               printk(KERN_ERR "nvram_create_os_partition: nvram_write "
+               pr_err("nvram_create_os_partition: nvram_write_header "
                       "failed (%d)\n", rc);
                return rc;
        }
-       
-       nvram_error_log_index = new_part->index + NVRAM_HEADER_LEN;
-       nvram_error_log_size = ((part->header.length - 1) *
-                               NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
-       
        list_add_tail(&new_part->partition, &free_part->partition);
 
-       if (free_part->header.length <= size) {
+       /* Adjust or remove the partition we stole the space from */
+       if (free_part->header.length > size) {
+               free_part->index += size * NVRAM_BLOCK_LEN;
+               free_part->header.length -= size;
+               free_part->header.checksum = nvram_checksum(&free_part->header);
+               rc = nvram_write_header(free_part);
+               if (rc <= 0) {
+                       pr_err("nvram_create_os_partition: nvram_write_header "
+                              "failed (%d)\n", rc);
+                       return rc;
+               }
+       } else {
                list_del(&free_part->partition);
                kfree(free_part);
-               return 0;
        } 
 
-       /* Adjust the partition we stole the space from */
-       free_part->index += size * NVRAM_BLOCK_LEN;
-       free_part->header.length -= size;
-       free_part->header.checksum = nvram_checksum(&free_part->header);
-       
-       rc = nvram_write_header(free_part);
-       if (rc <= 0) {
-               printk(KERN_ERR "nvram_create_os_partition: nvram_write_header "
-                      "failed (%d)\n", rc);
-               return rc;
+       /* Clear the new partition */
+       for (tmp_index = new_part->index + NVRAM_HEADER_LEN;
+            tmp_index <  ((size - 1) * NVRAM_BLOCK_LEN);
+            tmp_index += NVRAM_BLOCK_LEN) {
+               rc = ppc_md.nvram_write(nv_init_vals, NVRAM_BLOCK_LEN, &tmp_index);
+               if (rc <= 0) {
+                       pr_err("nvram_create_partition: nvram_write failed (%d)\n", rc);
+                       return rc;
+               }
        }
-
-       return 0;
+       
+       return new_part->index + NVRAM_HEADER_LEN;
 }
 
-
-/* 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).
+/**
+ * nvram_get_partition_size - Get the data size of an nvram partition
+ * @data_index: This is the offset of the start of the data of
+ *              the partition. The same value that is returned by
+ *              nvram_create_partition().
  */
-static int __init nvram_setup_partition(void)
+int nvram_get_partition_size(loff_t data_index)
 {
-       struct list_head * p;
-       struct nvram_partition * part;
-       int rc;
-
-       /* 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;
-
-       /* see if we have an OS partition that meets our needs.
-          will try getting the max we need.  If not we'll delete
-          partitions and try again. */
-       list_for_each(p, &nvram_part->partition) {
-               part = list_entry(p, struct nvram_partition, partition);
-               if (part->header.signature != NVRAM_SIG_OS)
-                       continue;
+       struct nvram_partition *part;
+       
+       list_for_each_entry(part, &nvram_partitions, partition) {
+               if (part->index + NVRAM_HEADER_LEN == data_index)
+                       return (part->header.length - 1) * NVRAM_BLOCK_LEN;
+       }
+       return -1;
+}
 
-               if (strcmp(part->header.name, "ppc64,linux"))
-                       continue;
 
-               if (part->header.length >= NVRAM_MIN_REQ) {
-                       /* found our partition */
-                       nvram_error_log_index = part->index + NVRAM_HEADER_LEN;
-                       nvram_error_log_size = ((part->header.length - 1) *
-                                               NVRAM_BLOCK_LEN) - sizeof(struct err_log_info);
-                       return 0;
+/**
+ * nvram_find_partition - Find an nvram partition by signature and name
+ * @name: Name of the partition or NULL for any name
+ * @sig: Signature to test against
+ * @out_size: if non-NULL, returns the size of the data part of the partition
+ */
+loff_t nvram_find_partition(const char *name, int sig, int *out_size)
+{
+       struct nvram_partition *p;
+
+       list_for_each_entry(p, &nvram_partitions, partition) {
+               if (p->header.signature == sig &&
+                   (!name || !strncmp(p->header.name, name, 12))) {
+                       if (out_size)
+                               *out_size = (p->header.length - 1) *
+                                       NVRAM_BLOCK_LEN;
+                       return p->index + NVRAM_HEADER_LEN;
                }
        }
-       
-       /* try creating a partition with the free space we have */
-       rc = nvram_create_os_partition();
-       if (!rc) {
-               return 0;
-       }
-               
-       /* need to free up some space */
-       rc = nvram_remove_os_partition();
-       if (rc) {
-               return rc;
-       }
-       
-       /* create a partition in this new space */
-       rc = nvram_create_os_partition();
-       if (rc) {
-               printk(KERN_ERR "nvram_create_os_partition: Could not find a "
-                      "NVRAM partition large enough\n");
-               return rc;
-       }
-       
        return 0;
 }
 
-
-static int __init nvram_scan_partitions(void)
+int __init nvram_scan_partitions(void)
 {
        loff_t cur_index = 0;
        struct nvram_header phead;
@@ -465,7 +451,7 @@ static int __init nvram_scan_partitions(void)
        int total_size;
        int err;
 
-       if (ppc_md.nvram_size == NULL)
+       if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
                return -ENODEV;
        total_size = ppc_md.nvram_size();
        
@@ -512,12 +498,16 @@ static int __init nvram_scan_partitions(void)
                
                memcpy(&tmp_part->header, &phead, NVRAM_HEADER_LEN);
                tmp_part->index = cur_index;
-               list_add_tail(&tmp_part->partition, &nvram_part->partition);
+               list_add_tail(&tmp_part->partition, &nvram_partitions);
                
                cur_index += phead.length * NVRAM_BLOCK_LEN;
        }
        err = 0;
 
+#ifdef DEBUG_NVRAM
+       nvram_print_partitions("NVRAM Partitions");
+#endif
+
  out:
        kfree(header);
        return err;
@@ -525,9 +515,10 @@ static int __init nvram_scan_partitions(void)
 
 static int __init nvram_init(void)
 {
-       int error;
        int rc;
        
+       BUILD_BUG_ON(NVRAM_BLOCK_LEN != 16);
+
        if (ppc_md.nvram_size == NULL || ppc_md.nvram_size() <= 0)
                return  -ENODEV;
 
@@ -537,29 +528,6 @@ static int __init nvram_init(void)
                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;
 }
 
@@ -568,135 +536,6 @@ void __exit nvram_cleanup(void)
         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_exit(nvram_cleanup);
 MODULE_LICENSE("GPL");