]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - arch/powerpc/kernel/prom.c
[PATCH] powerpc: enable PPC_PTRACE_[GS]ETREGS on ppc32
[karo-tx-linux.git] / arch / powerpc / kernel / prom.c
index 294832a7e0a6598c7eb92093ff60216941f2203f..969f4abcc0be64bc625685facacf0edeb55f7b5f 100644 (file)
@@ -50,6 +50,7 @@
 #include <asm/machdep.h>
 #include <asm/pSeries_reconfig.h>
 #include <asm/pci-bridge.h>
+#include <asm/kexec.h>
 
 #ifdef DEBUG
 #define DBG(fmt...) printk(KERN_ERR fmt)
@@ -62,7 +63,7 @@ static int __initdata dt_root_addr_cells;
 static int __initdata dt_root_size_cells;
 
 #ifdef CONFIG_PPC64
-static int __initdata iommu_is_off;
+int __initdata iommu_is_off;
 int __initdata iommu_force_on;
 unsigned long tce_alloc_start, tce_alloc_end;
 #endif
@@ -383,14 +384,14 @@ static int __devinit finish_node_interrupts(struct device_node *np,
                        /* Apple uses bits in there in a different way, let's
                         * only keep the real sense bit on macs
                         */
-                       if (_machine == PLATFORM_POWERMAC)
+                       if (machine_is(powermac))
                                sense &= 0x1;
                        np->intrs[intrcount].sense = map_mpic_senses[sense];
                }
 
 #ifdef CONFIG_PPC64
                /* We offset irq numbers for the u3 MPIC by 128 in PowerMac */
-               if (_machine == PLATFORM_POWERMAC && ic && ic->parent) {
+               if (machine_is(powermac) && ic && ic->parent) {
                        char *name = get_property(ic->parent, "name", NULL);
                        if (name && !strcmp(name, "u3"))
                                np->intrs[intrcount].line += 128;
@@ -570,6 +571,18 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
        return rc;
 }
 
+unsigned long __init of_get_flat_dt_root(void)
+{
+       unsigned long p = ((unsigned long)initial_boot_params) +
+               initial_boot_params->off_dt_struct;
+
+       while(*((u32 *)p) == OF_DT_NOP)
+               p += 4;
+       BUG_ON (*((u32 *)p) != OF_DT_BEGIN_NODE);
+       p += 4;
+       return _ALIGN(p + strlen((char *)p) + 1, 4);
+}
+
 /**
  * This  function can be used within scan_flattened_dt callback to get
  * access to properties
@@ -612,6 +625,25 @@ void* __init of_get_flat_dt_prop(unsigned long node, const char *name,
        } while(1);
 }
 
+int __init of_flat_dt_is_compatible(unsigned long node, const char *compat)
+{
+       const char* cp;
+       unsigned long cplen, l;
+
+       cp = of_get_flat_dt_prop(node, "compatible", &cplen);
+       if (cp == NULL)
+               return 0;
+       while (cplen > 0) {
+               if (strncasecmp(cp, compat, strlen(compat)) == 0)
+                       return 1;
+               l = strlen(cp) + 1;
+               cp += l;
+               cplen -= l;
+       }
+
+       return 0;
+}
+
 static void *__init unflatten_dt_alloc(unsigned long *mem, unsigned long size,
                                       unsigned long align)
 {
@@ -686,7 +718,7 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
 #ifdef DEBUG
                                if ((strlen(p) + l + 1) != allocl) {
                                        DBG("%s: p: %d, l: %d, a: %d\n",
-                                           pathp, strlen(p), l, allocl);
+                                           pathp, (int)strlen(p), l, allocl);
                                }
 #endif
                                p += strlen(p);
@@ -805,6 +837,42 @@ static unsigned long __init unflatten_dt_node(unsigned long mem,
        return mem;
 }
 
+static int __init early_parse_mem(char *p)
+{
+       if (!p)
+               return 1;
+
+       memory_limit = PAGE_ALIGN(memparse(p, &p));
+       DBG("memory limit = 0x%lx\n", memory_limit);
+
+       return 0;
+}
+early_param("mem", early_parse_mem);
+
+/*
+ * The device tree may be allocated below our memory limit, or inside the
+ * crash kernel region for kdump. If so, move it out now.
+ */
+static void move_device_tree(void)
+{
+       unsigned long start, size;
+       void *p;
+
+       DBG("-> move_device_tree\n");
+
+       start = __pa(initial_boot_params);
+       size = initial_boot_params->totalsize;
+
+       if ((memory_limit && (start + size) > memory_limit) ||
+                       overlaps_crashkernel(start, size)) {
+               p = __va(lmb_alloc_base(size, PAGE_SIZE, lmb.rmo_size));
+               memcpy(p, initial_boot_params, size);
+               initial_boot_params = (struct boot_param_header *)p;
+               DBG("Moved device tree to 0x%p\n", p);
+       }
+
+       DBG("<- move_device_tree\n");
+}
 
 /**
  * unflattens the device-tree passed by the firmware, creating the
@@ -816,8 +884,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");
 
@@ -831,10 +897,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;
@@ -857,51 +919,144 @@ 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));
+       DBG(" <- unflatten_device_tree()\n");
+}
+
+/*
+ * ibm,pa-features is a per-cpu property that contains a string of
+ * attribute descriptors, each of which has a 2 byte header plus up
+ * to 254 bytes worth of processor attribute bits.  First header
+ * byte specifies the number of bytes following the header.
+ * Second header byte is an "attribute-specifier" type, of which
+ * zero is the only currently-defined value.
+ * Implementation:  Pass in the byte and bit offset for the feature
+ * that we are interested in.  The function will return -1 if the
+ * pa-features property is missing, or a 1/0 to indicate if the feature
+ * is supported/not supported.  Note that the bit numbers are
+ * big-endian to match the definition in PAPR.
+ */
+static struct ibm_pa_feature {
+       unsigned long   cpu_features;   /* CPU_FTR_xxx bit */
+       unsigned int    cpu_user_ftrs;  /* PPC_FEATURE_xxx bit */
+       unsigned char   pabyte;         /* byte number in ibm,pa-features */
+       unsigned char   pabit;          /* bit number (big-endian) */
+       unsigned char   invert;         /* if 1, pa bit set => clear feature */
+} ibm_pa_features[] __initdata = {
+       {0, PPC_FEATURE_HAS_MMU,        0, 0, 0},
+       {0, PPC_FEATURE_HAS_FPU,        0, 1, 0},
+       {CPU_FTR_SLB, 0,                0, 2, 0},
+       {CPU_FTR_CTRL, 0,               0, 3, 0},
+       {CPU_FTR_NOEXECUTE, 0,          0, 6, 0},
+       {CPU_FTR_NODSISRALIGN, 0,       1, 1, 1},
+       {CPU_FTR_CI_LARGE_PAGE, 0,      1, 2, 0},
+};
+
+static void __init check_cpu_pa_features(unsigned long node)
+{
+       unsigned char *pa_ftrs;
+       unsigned long len, tablelen, i, bit;
+
+       pa_ftrs = of_get_flat_dt_prop(node, "ibm,pa-features", &tablelen);
+       if (pa_ftrs == NULL)
+               return;
+
+       /* find descriptor with type == 0 */
+       for (;;) {
+               if (tablelen < 3)
+                       return;
+               len = 2 + pa_ftrs[0];
+               if (tablelen < len)
+                       return;         /* descriptor 0 not found */
+               if (pa_ftrs[1] == 0)
+                       break;
+               tablelen -= len;
+               pa_ftrs += len;
        }
-#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);
+       /* loop over bits we know about */
+       for (i = 0; i < ARRAY_SIZE(ibm_pa_features); ++i) {
+               struct ibm_pa_feature *fp = &ibm_pa_features[i];
 
-       DBG(" <- unflatten_device_tree()\n");
+               if (fp->pabyte >= pa_ftrs[0])
+                       continue;
+               bit = (pa_ftrs[2 + fp->pabyte] >> (7 - fp->pabit)) & 1;
+               if (bit ^ fp->invert) {
+                       cur_cpu_spec->cpu_features |= fp->cpu_features;
+                       cur_cpu_spec->cpu_user_features |= fp->cpu_user_ftrs;
+               } else {
+                       cur_cpu_spec->cpu_features &= ~fp->cpu_features;
+                       cur_cpu_spec->cpu_user_features &= ~fp->cpu_user_ftrs;
+               }
+       }
 }
 
-
 static int __init early_init_dt_scan_cpus(unsigned long node,
-                                         const char *uname, int depth, void *data)
+                                         const char *uname, int depth,
+                                         void *data)
 {
+       static int logical_cpuid = 0;
+       char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+#ifdef CONFIG_ALTIVEC
        u32 *prop;
-       unsigned long size;
-       char *type = of_get_flat_dt_prop(node, "device_type", &size);
+#endif
+       u32 *intserv;
+       int i, nthreads;
+       unsigned long len;
+       int found = 0;
 
        /* We are scanning "cpu" nodes only */
        if (type == NULL || strcmp(type, "cpu") != 0)
                return 0;
 
-       boot_cpuid = 0;
-       boot_cpuid_phys = 0;
-       if (initial_boot_params && initial_boot_params->version >= 2) {
-               /* version 2 of the kexec param format adds the phys cpuid
-                * of booted proc.
-                */
-               boot_cpuid_phys = initial_boot_params->boot_cpuid_phys;
+       /* Get physical cpuid */
+       intserv = of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", &len);
+       if (intserv) {
+               nthreads = len / sizeof(int);
        } else {
-               /* Check if it's the boot-cpu, set it's hw index now */
-               if (of_get_flat_dt_prop(node,
+               intserv = of_get_flat_dt_prop(node, "reg", NULL);
+               nthreads = 1;
+       }
+
+       /*
+        * Now see if any of these threads match our boot cpu.
+        * NOTE: This must match the parsing done in smp_setup_cpu_maps.
+        */
+       for (i = 0; i < nthreads; i++) {
+               /*
+                * version 2 of the kexec param format adds the phys cpuid of
+                * booted proc.
+                */
+               if (initial_boot_params && initial_boot_params->version >= 2) {
+                       if (intserv[i] ==
+                                       initial_boot_params->boot_cpuid_phys) {
+                               found = 1;
+                               break;
+                       }
+               } else {
+                       /*
+                        * Check if it's the boot-cpu, set it's hw index now,
+                        * unfortunately this format did not support booting
+                        * off secondary threads.
+                        */
+                       if (of_get_flat_dt_prop(node,
                                        "linux,boot-cpu", NULL) != NULL) {
-                       prop = of_get_flat_dt_prop(node, "reg", NULL);
-                       if (prop != NULL)
-                               boot_cpuid_phys = *prop;
+                               found = 1;
+                               break;
+                       }
                }
+
+#ifdef CONFIG_SMP
+               /* logical cpu id is always 0 on UP kernels */
+               logical_cpuid++;
+#endif
+       }
+
+       if (found) {
+               DBG("boot cpu: logical %d physical %d\n", logical_cpuid,
+                       intserv[i]);
+               boot_cpuid = logical_cpuid;
+               set_hard_smp_processor_id(boot_cpuid, intserv[i]);
        }
-       set_hard_smp_processor_id(0, boot_cpuid_phys);
 
 #ifdef CONFIG_ALTIVEC
        /* Check if we have a VMX and eventually update CPU features */
@@ -919,17 +1074,13 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
        }
 #endif /* CONFIG_ALTIVEC */
 
+       check_cpu_pa_features(node);
+
 #ifdef CONFIG_PPC_PSERIES
-       /*
-        * Check for an SMT capable CPU and set the CPU feature. We do
-        * this by looking at the size of the ibm,ppc-interrupt-server#s
-        * property
-        */
-       prop = (u32 *)of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s",
-                                      &size);
-       cur_cpu_spec->cpu_features &= ~CPU_FTR_SMT;
-       if (prop && ((size / sizeof(u32)) > 1))
+       if (nthreads > 1)
                cur_cpu_spec->cpu_features |= CPU_FTR_SMT;
+       else
+               cur_cpu_spec->cpu_features &= ~CPU_FTR_SMT;
 #endif
 
        return 0;
@@ -938,8 +1089,9 @@ static int __init early_init_dt_scan_cpus(unsigned long node,
 static int __init early_init_dt_scan_chosen(unsigned long node,
                                            const char *uname, int depth, void *data)
 {
-       u32 *prop;
        unsigned long *lprop;
+       unsigned long l;
+       char *p;
 
        DBG("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
 
@@ -947,14 +1099,6 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
            (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
                return 0;
 
-       /* get platform type */
-       prop = (u32 *)of_get_flat_dt_prop(node, "linux,platform", NULL);
-       if (prop == NULL)
-               return 0;
-#ifdef CONFIG_PPC_MULTIPLATFORM
-       _machine = *prop;
-#endif
-
 #ifdef CONFIG_PPC64
        /* check if iommu is forced on or off */
        if (of_get_flat_dt_prop(node, "linux,iommu-off", NULL) != NULL)
@@ -963,6 +1107,7 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
                iommu_force_on = 1;
 #endif
 
+       /* mem=x on the command line is the preferred mechanism */
        lprop = of_get_flat_dt_prop(node, "linux,memory-limit", NULL);
        if (lprop)
                memory_limit = *lprop;
@@ -981,15 +1126,15 @@ static int __init early_init_dt_scan_chosen(unsigned long node,
         * set of RTAS infos now if available
         */
        {
-               u64 *basep, *entryp;
+               u64 *basep, *entryp, *sizep;
 
                basep = of_get_flat_dt_prop(node, "linux,rtas-base", NULL);
                entryp = of_get_flat_dt_prop(node, "linux,rtas-entry", NULL);
-               prop = of_get_flat_dt_prop(node, "linux,rtas-size", NULL);
-               if (basep && entryp && prop) {
+               sizep = of_get_flat_dt_prop(node, "linux,rtas-size", NULL);
+               if (basep && entryp && sizep) {
                        rtas.base = *basep;
                        rtas.entry = *entryp;
-                       rtas.size = *prop;
+                       rtas.size = *sizep;
                }
        }
 #endif /* CONFIG_PPC_RTAS */
@@ -1004,6 +1149,18 @@ 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);
+
        /* break now */
        return 1;
 }
@@ -1110,6 +1267,11 @@ static void __init early_reserve_mem(void)
 
        reserve_map = (u64 *)(((unsigned long)initial_boot_params) +
                                        initial_boot_params->off_mem_rsvmap);
+
+       /* before we do anything, lets reserve the dt blob */
+       lmb_reserve(__pa((unsigned long)initial_boot_params),
+                   initial_boot_params->totalsize);
+
 #ifdef CONFIG_PPC32
        /* 
         * Handle the case where we might be booting from an old kexec
@@ -1124,7 +1286,7 @@ static void __init early_reserve_mem(void)
                        size_32 = *(reserve_map_32++);
                        if (size_32 == 0)
                                break;
-                       DBG("reserving: %lx -> %lx\n", base_32, size_32);
+                       DBG("reserving: %x -> %x\n", base_32, size_32);
                        lmb_reserve(base_32, size_32);
                }
                return;
@@ -1162,18 +1324,26 @@ void __init early_init_devtree(void *params)
        lmb_init();
        of_scan_flat_dt(early_init_dt_scan_root, NULL);
        of_scan_flat_dt(early_init_dt_scan_memory, NULL);
-       lmb_enforce_memory_limit(memory_limit);
-       lmb_analyze();
 
-       DBG("Phys. mem: %lx\n", lmb_phys_mem_size());
+       /* Save command line for /proc/cmdline and then parse parameters */
+       strlcpy(saved_command_line, cmd_line, COMMAND_LINE_SIZE);
+       parse_early_param();
 
        /* Reserve LMB regions used by kernel, initrd, dt, etc... */
        lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START);
-#ifdef CONFIG_CRASH_DUMP
-       lmb_reserve(0, KDUMP_RESERVE_LIMIT);
-#endif
+       reserve_kdump_trampoline();
+       reserve_crashkernel();
        early_reserve_mem();
 
+       lmb_enforce_memory_limit(memory_limit);
+       lmb_analyze();
+
+       DBG("Phys. mem: %lx\n", lmb_phys_mem_size());
+
+       /* We may need to relocate the flat tree, do it now.
+        * FIXME .. and the initrd too? */
+       move_device_tree();
+
        DBG("Scanning CPUs ...\n");
 
        /* Retreive CPU related informations from the flat tree
@@ -1737,7 +1907,7 @@ static int of_finish_dynamic_node(struct device_node *node)
        /* We don't support that function on PowerMac, at least
         * not yet
         */
-       if (_machine == PLATFORM_POWERMAC)
+       if (machine_is(powermac))
                return -ENODEV;
 
        /* fix up new node's linux_phandle field */
@@ -1923,29 +2093,3 @@ int prom_update_property(struct device_node *np,
        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 */