prom.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,082 行 · 第 1/4 页
C
2,082 行
unsigned long p = ((unsigned long)initial_boot_params) + initial_boot_params->off_dt_struct; int rc = 0; int depth = -1; do { u32 tag = *((u32 *)p); char *pathp; p += 4; if (tag == OF_DT_END_NODE) { depth --; continue; } if (tag == OF_DT_NOP) continue; if (tag == OF_DT_END) break; if (tag == OF_DT_PROP) { u32 sz = *((u32 *)p); p += 8; if (initial_boot_params->version < 0x10) p = _ALIGN(p, sz >= 8 ? 8 : 4); p += sz; p = _ALIGN(p, 4); continue; } if (tag != OF_DT_BEGIN_NODE) { printk(KERN_WARNING "Invalid tag %x scanning flattened" " device tree !\n", tag); return -EINVAL; } depth++; pathp = (char *)p; p = _ALIGN(p + strlen(pathp) + 1, 4); if ((*pathp) == '/') { char *lp, *np; for (lp = NULL, np = pathp; *np; np++) if ((*np) == '/') lp = np+1; if (lp != NULL) pathp = lp; } rc = it(p, pathp, depth, data); if (rc != 0) break; } while(1); 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 */void* __init of_get_flat_dt_prop(unsigned long node, const char *name, unsigned long *size){ unsigned long p = node; do { u32 tag = *((u32 *)p); u32 sz, noff; const char *nstr; p += 4; if (tag == OF_DT_NOP) continue; if (tag != OF_DT_PROP) return NULL; sz = *((u32 *)p); noff = *((u32 *)(p + 4)); p += 8; if (initial_boot_params->version < 0x10) p = _ALIGN(p, sz >= 8 ? 8 : 4); nstr = find_flat_dt_string(noff); if (nstr == NULL) { printk(KERN_WARNING "Can't find property index" " name !\n"); return NULL; } if (strcmp(name, nstr) == 0) { if (size) *size = sz; return (void *)p; } p += sz; p = _ALIGN(p, 4); } 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){ void *res; *mem = _ALIGN(*mem, align); res = (void *)*mem; *mem += size; return res;}static unsigned long __init unflatten_dt_node(unsigned long mem, unsigned long *p, struct device_node *dad, struct device_node ***allnextpp, unsigned long fpsize){ struct device_node *np; struct property *pp, **prev_pp = NULL; char *pathp; u32 tag; unsigned int l, allocl; int has_name = 0; int new_format = 0; tag = *((u32 *)(*p)); if (tag != OF_DT_BEGIN_NODE) { printk("Weird tag at start of node: %x\n", tag); return mem; } *p += 4; pathp = (char *)*p; l = allocl = strlen(pathp) + 1; *p = _ALIGN(*p + l, 4); /* version 0x10 has a more compact unit name here instead of the full * path. we accumulate the full path size using "fpsize", we'll rebuild * it later. We detect this because the first character of the name is * not '/'. */ if ((*pathp) != '/') { new_format = 1; if (fpsize == 0) { /* root node: special case. fpsize accounts for path * plus terminating zero. root node only has '/', so * fpsize should be 2, but we want to avoid the first * level nodes to have two '/' so we use fpsize 1 here */ fpsize = 1; allocl = 2; } else { /* account for '/' and path size minus terminal 0 * already in 'l' */ fpsize += l; allocl = fpsize; } } np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl, __alignof__(struct device_node)); if (allnextpp) { memset(np, 0, sizeof(*np)); np->full_name = ((char*)np) + sizeof(struct device_node); if (new_format) { char *p = np->full_name; /* rebuild full path for new format */ if (dad && dad->parent) { strcpy(p, dad->full_name);#ifdef DEBUG if ((strlen(p) + l + 1) != allocl) { DBG("%s: p: %d, l: %d, a: %d\n", pathp, (int)strlen(p), l, allocl); }#endif p += strlen(p); } *(p++) = '/'; memcpy(p, pathp, l); } else memcpy(np->full_name, pathp, l); prev_pp = &np->properties; **allnextpp = np; *allnextpp = &np->allnext; if (dad != NULL) { np->parent = dad; /* we temporarily use the next field as `last_child'*/ if (dad->next == 0) dad->child = np; else dad->next->sibling = np; dad->next = np; } kref_init(&np->kref); } while(1) { u32 sz, noff; char *pname; tag = *((u32 *)(*p)); if (tag == OF_DT_NOP) { *p += 4; continue; } if (tag != OF_DT_PROP) break; *p += 4; sz = *((u32 *)(*p)); noff = *((u32 *)((*p) + 4)); *p += 8; if (initial_boot_params->version < 0x10) *p = _ALIGN(*p, sz >= 8 ? 8 : 4); pname = find_flat_dt_string(noff); if (pname == NULL) { printk("Can't find property name in list !\n"); break; } if (strcmp(pname, "name") == 0) has_name = 1; l = strlen(pname) + 1; pp = unflatten_dt_alloc(&mem, sizeof(struct property), __alignof__(struct property)); if (allnextpp) { if (strcmp(pname, "linux,phandle") == 0) { np->node = *((u32 *)*p); if (np->linux_phandle == 0) np->linux_phandle = np->node; } if (strcmp(pname, "ibm,phandle") == 0) np->linux_phandle = *((u32 *)*p); pp->name = pname; pp->length = sz; pp->value = (void *)*p; *prev_pp = pp; prev_pp = &pp->next; } *p = _ALIGN((*p) + sz, 4); } /* with version 0x10 we may not have the name property, recreate * it here from the unit name if absent */ if (!has_name) { char *p = pathp, *ps = pathp, *pa = NULL; int sz; while (*p) { if ((*p) == '@') pa = p; if ((*p) == '/') ps = p + 1; p++; } if (pa < ps) pa = p; sz = (pa - ps) + 1; pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz, __alignof__(struct property)); if (allnextpp) { pp->name = "name"; pp->length = sz; pp->value = (unsigned char *)(pp + 1); *prev_pp = pp; prev_pp = &pp->next; memcpy(pp->value, ps, sz - 1); ((char *)pp->value)[sz - 1] = 0; DBG("fixed up name for %s -> %s\n", pathp, pp->value); } } if (allnextpp) { *prev_pp = NULL; np->name = get_property(np, "name", NULL); np->type = get_property(np, "device_type", NULL); if (!np->name) np->name = "<NULL>"; if (!np->type) np->type = "<NULL>"; } while (tag == OF_DT_BEGIN_NODE) { mem = unflatten_dt_node(mem, p, np, allnextpp, fpsize); tag = *((u32 *)(*p)); } if (tag != OF_DT_END_NODE) { printk("Weird tag at end of node: %x\n", tag); return mem; } *p += 4; return mem;}/** * unflattens the device-tree passed by the firmware, creating the * tree of struct device_node. It also fills the "name" and "type" * pointers of the nodes so the normal device-tree walking functions * can be used (this used to be done by finish_device_tree) */void __init unflatten_device_tree(void){ unsigned long start, mem, size; struct device_node **allnextp = &allnodes; DBG(" -> unflatten_device_tree()\n"); /* First pass, scan for size */ start = ((unsigned long)initial_boot_params) + initial_boot_params->off_dt_struct; size = unflatten_dt_node(0, &start, NULL, NULL, 0); size = (size | 3) + 1; DBG(" size is %lx, allocating...\n", size); /* Allocate memory for the expanded device tree */ mem = lmb_alloc(size + 4, __alignof__(struct device_node)); mem = (unsigned long) __va(mem); ((u32 *)mem)[size / 4] = 0xdeadbeef; DBG(" unflattening %lx...\n", mem); /* Second pass, do actual unflattening */ start = ((unsigned long)initial_boot_params) + initial_boot_params->off_dt_struct; unflatten_dt_node(mem, &start, NULL, &allnextp, 0); if (*((u32 *)start) != OF_DT_END) printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start)); if (((u32 *)mem)[size / 4] != 0xdeadbeef) printk(KERN_WARNING "End of tree marker overwritten: %08x\n", ((u32 *)mem)[size / 4] ); *allnextp = NULL; /* Get pointer to OF "/chosen" node for use everywhere */ of_chosen = of_find_node_by_path("/chosen"); if (of_chosen == NULL) of_chosen = of_find_node_by_path("/chosen@0"); 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; } /* 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]; 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){ static int logical_cpuid = 0; char *type = of_get_flat_dt_prop(node, "device_type", NULL);#ifdef CONFIG_ALTIVEC u32 *prop;#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; /* Get physical cpuid */ intserv = of_get_flat_dt_prop(node, "ibm,ppc-interrupt-server#s", &len); if (intserv) { nthreads = len / sizeof(int); } else { 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) { 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]); }#ifdef CONFIG_ALTIVEC /* Check if we have a VMX and eventually update CPU features */ prop = (u32 *)of_get_flat_dt_prop(node, "ibm,vmx", NULL); if (prop && (*prop) > 0) { cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC; cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC; } /* Same goes for Apple's "altivec" property */ prop = (u32 *)of_get_flat_dt_prop(node, "altivec", NULL); if (prop) { cur_cpu_spec->cpu_features |= CPU_FTR_ALTIVEC; cur_cpu_spec->cpu_user_features |= PPC_FEATURE_HAS_ALTIVEC; }#endif /* CONFIG_ALTIVEC */ check_cpu_pa_features(node);#ifdef CONFIG_PPC_PSERIES
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?