Merge ../powerpc-merge
[sfrench/cifs-2.6.git] / arch / powerpc / kernel / prom.c
index 977ee3adaf2d23eed404ba0bffc4b926c5d48895..d63cd562d9d5e0bbccbba98ac6fd118f8ff515f0 100644 (file)
@@ -491,7 +491,12 @@ void __init finish_device_tree(void)
        size = 16;
        finish_node(allnodes, &size, 1);
        size -= 16;
-       end = start = (unsigned long) __va(lmb_alloc(size, 128));
+
+       if (0 == size)
+               end = start = 0;
+       else
+               end = start = (unsigned long)__va(lmb_alloc(size, 128));
+
        finish_node(allnodes, &end, 0);
        BUG_ON(end != start + size);
 
@@ -811,8 +816,6 @@ void __init unflatten_device_tree(void)
 {
        unsigned long start, mem, size;
        struct device_node **allnextp = &allnodes;
-       char *p = NULL;
-       int l = 0;
 
        DBG(" -> unflatten_device_tree()\n");
 
@@ -826,10 +829,6 @@ void __init unflatten_device_tree(void)
 
        /* Allocate memory for the expanded device tree */
        mem = lmb_alloc(size + 4, __alignof__(struct device_node));
-       if (!mem) {
-               DBG("Couldn't allocate memory with lmb_alloc()!\n");
-               panic("Couldn't allocate memory with lmb_alloc()!\n");
-       }
        mem = (unsigned long) __va(mem);
 
        ((u32 *)mem)[size / 4] = 0xdeadbeef;
@@ -852,19 +851,6 @@ void __init unflatten_device_tree(void)
        if (of_chosen == NULL)
                of_chosen = of_find_node_by_path("/chosen@0");
 
-       /* Retreive command line */
-       if (of_chosen != NULL) {
-               p = (char *)get_property(of_chosen, "bootargs", &l);
-               if (p != NULL && l > 0)
-                       strlcpy(cmd_line, p, min(l, COMMAND_LINE_SIZE));
-       }
-#ifdef CONFIG_CMDLINE
-       if (l == 0 || (l == 1 && (*p) == 0))
-               strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
-#endif /* CONFIG_CMDLINE */
-
-       DBG("Command line is: %s\n", cmd_line);
-
        DBG(" <- unflatten_device_tree()\n");
 }
 
@@ -935,6 +921,8 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
 {
        u32 *prop;
        unsigned long *lprop;
+       unsigned long l;
+       char *p;
 
        DBG("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
 
@@ -972,7 +960,7 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
 #endif
 
 #ifdef CONFIG_PPC_RTAS
-       /* To help early debugging via the front panel, we retreive a minimal
+       /* To help early debugging via the front panel, we retrieve a minimal
         * set of RTAS infos now if available
         */
        {
@@ -999,6 +987,41 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
                crashk_res.end = crashk_res.start + *lprop - 1;
 #endif
 
+       /* Retreive command line */
+       p = of_get_flat_dt_prop(node, "bootargs", &l);
+       if (p != NULL && l > 0)
+               strlcpy(cmd_line, p, min((int)l, COMMAND_LINE_SIZE));
+
+#ifdef CONFIG_CMDLINE
+       if (l == 0 || (l == 1 && (*p) == 0))
+               strlcpy(cmd_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+#endif /* CONFIG_CMDLINE */
+
+       DBG("Command line is: %s\n", cmd_line);
+
+       if (strstr(cmd_line, "mem=")) {
+               char *p, *q;
+               unsigned long maxmem = 0;
+
+               for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) {
+                       q = p + 4;
+                       if (p > cmd_line && p[-1] != ' ')
+                               continue;
+                       maxmem = simple_strtoul(q, &q, 0);
+                       if (*q == 'k' || *q == 'K') {
+                               maxmem <<= 10;
+                               ++q;
+                       } else if (*q == 'm' || *q == 'M') {
+                               maxmem <<= 20;
+                               ++q;
+                       } else if (*q == 'g' || *q == 'G') {
+                               maxmem <<= 30;
+                               ++q;
+                       }
+               }
+               memory_limit = maxmem;
+       }
+
        /* break now */
        return 1;
 }
@@ -1100,17 +1123,37 @@ static int __init early_init_dt_scan_memory(unsigned long node,
 
 static void __init early_reserve_mem(void)
 {
-       unsigned long base, size;
-       unsigned long *reserve_map;
+       u64 base, size;
+       u64 *reserve_map;
 
-       reserve_map = (unsigned long *)(((unsigned long)initial_boot_params) +
+       reserve_map = (u64 *)(((unsigned long)initial_boot_params) +
                                        initial_boot_params->off_mem_rsvmap);
+#ifdef CONFIG_PPC32
+       /* 
+        * Handle the case where we might be booting from an old kexec
+        * image that setup the mem_rsvmap as pairs of 32-bit values
+        */
+       if (*reserve_map > 0xffffffffull) {
+               u32 base_32, size_32;
+               u32 *reserve_map_32 = (u32 *)reserve_map;
+
+               while (1) {
+                       base_32 = *(reserve_map_32++);
+                       size_32 = *(reserve_map_32++);
+                       if (size_32 == 0)
+                               break;
+                       DBG("reserving: %x -> %x\n", base_32, size_32);
+                       lmb_reserve(base_32, size_32);
+               }
+               return;
+       }
+#endif
        while (1) {
                base = *(reserve_map++);
                size = *(reserve_map++);
                if (size == 0)
                        break;
-               DBG("reserving: %lx -> %lx\n", base, size);
+               DBG("reserving: %llx -> %llx\n", base, size);
                lmb_reserve(base, size);
        }
 
@@ -1378,8 +1421,8 @@ struct device_node *of_find_node_by_name(struct device_node *from,
 
        read_lock(&devtree_lock);
        np = from ? from->allnext : allnodes;
-       for (; np != 0; np = np->allnext)
-               if (np->name != 0 && strcasecmp(np->name, name) == 0
+       for (; np != NULL; np = np->allnext)
+               if (np->name != NULL && strcasecmp(np->name, name) == 0
                    && of_node_get(np))
                        break;
        if (from)
@@ -1607,6 +1650,11 @@ static void of_node_release(struct kref *kref)
                kfree(prop->value);
                kfree(prop);
                prop = next;
+
+               if (!prop) {
+                       prop = node->deadprops;
+                       node->deadprops = NULL;
+               }
        }
        kfree(node->intrs);
        kfree(node->full_name);
@@ -1754,22 +1802,32 @@ static int __init prom_reconfig_setup(void)
 __initcall(prom_reconfig_setup);
 #endif
 
-/*
- * Find a property with a given name for a given node
- * and return the value.
- */
-unsigned char *get_property(struct device_node *np, const char *name,
-                           int *lenp)
+struct property *of_find_property(struct device_node *np, const char *name,
+                                 int *lenp)
 {
        struct property *pp;
 
+       read_lock(&devtree_lock);
        for (pp = np->properties; pp != 0; pp = pp->next)
                if (strcmp(pp->name, name) == 0) {
                        if (lenp != 0)
                                *lenp = pp->length;
-                       return pp->value;
+                       break;
                }
-       return NULL;
+       read_unlock(&devtree_lock);
+
+       return pp;
+}
+
+/*
+ * Find a property with a given name for a given node
+ * and return the value.
+ */
+unsigned char *get_property(struct device_node *np, const char *name,
+                           int *lenp)
+{
+       struct property *pp = of_find_property(np,name,lenp);
+       return pp ? pp->value : NULL;
 }
 EXPORT_SYMBOL(get_property);
 
@@ -1803,4 +1861,109 @@ int prom_add_property(struct device_node* np, struct property* prop)
        return 0;
 }
 
+/*
+ * Remove a property from a node.  Note that we don't actually
+ * remove it, since we have given out who-knows-how-many pointers
+ * to the data using get-property.  Instead we just move the property
+ * to the "dead properties" list, so it won't be found any more.
+ */
+int prom_remove_property(struct device_node *np, struct property *prop)
+{
+       struct property **next;
+       int found = 0;
+
+       write_lock(&devtree_lock);
+       next = &np->properties;
+       while (*next) {
+               if (*next == prop) {
+                       /* found the node */
+                       *next = prop->next;
+                       prop->next = np->deadprops;
+                       np->deadprops = prop;
+                       found = 1;
+                       break;
+               }
+               next = &(*next)->next;
+       }
+       write_unlock(&devtree_lock);
+
+       if (!found)
+               return -ENODEV;
 
+#ifdef CONFIG_PROC_DEVICETREE
+       /* try to remove the proc node as well */
+       if (np->pde)
+               proc_device_tree_remove_prop(np->pde, prop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+       return 0;
+}
+
+/*
+ * Update a property in a node.  Note that we don't actually
+ * remove it, since we have given out who-knows-how-many pointers
+ * to the data using get-property.  Instead we just move the property
+ * to the "dead properties" list, and add the new property to the
+ * property list
+ */
+int prom_update_property(struct device_node *np,
+                        struct property *newprop,
+                        struct property *oldprop)
+{
+       struct property **next;
+       int found = 0;
+
+       write_lock(&devtree_lock);
+       next = &np->properties;
+       while (*next) {
+               if (*next == oldprop) {
+                       /* found the node */
+                       newprop->next = oldprop->next;
+                       *next = newprop;
+                       oldprop->next = np->deadprops;
+                       np->deadprops = oldprop;
+                       found = 1;
+                       break;
+               }
+               next = &(*next)->next;
+       }
+       write_unlock(&devtree_lock);
+
+       if (!found)
+               return -ENODEV;
+
+#ifdef CONFIG_PROC_DEVICETREE
+       /* try to add to proc as well if it was initialized */
+       if (np->pde)
+               proc_device_tree_update_prop(np->pde, newprop, oldprop);
+#endif /* CONFIG_PROC_DEVICETREE */
+
+       return 0;
+}
+
+#ifdef CONFIG_KEXEC
+/* We may have allocated the flat device tree inside the crash kernel region
+ * in prom_init. If so we need to move it out into regular memory. */
+void kdump_move_device_tree(void)
+{
+       unsigned long start, end;
+       struct boot_param_header *new;
+
+       start = __pa((unsigned long)initial_boot_params);
+       end = start + initial_boot_params->totalsize;
+
+       if (end < crashk_res.start || start > crashk_res.end)
+               return;
+
+       new = (struct boot_param_header*)
+               __va(lmb_alloc(initial_boot_params->totalsize, PAGE_SIZE));
+
+       memcpy(new, initial_boot_params, initial_boot_params->totalsize);
+
+       initial_boot_params = new;
+
+       DBG("Flat device tree blob moved to %p\n", initial_boot_params);
+
+       /* XXX should we unreserve the old DT? */
+}
+#endif /* CONFIG_KEXEC */