📄 prom_init.c
字号:
prom_debug(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); prom_debug(" rmo_top : %x\n", RELOC(rmo_top)); prom_debug(" ram_top : %x\n", RELOC(ram_top)); return addr;}/* * Parse a "reg" cell */static unsigned long __init prom_next_cell(int s, cell_t **cellp){ cell_t *p = *cellp; unsigned long r = 0; /* Ignore more than 2 cells */ while (s > sizeof(unsigned long) / 4) { p++; s--; } r = *p++;#ifdef CONFIG_PPC64 if (s > 1) { r <<= 32; r |= *(p++); }#endif *cellp = p; return r;}/* * Very dumb function for adding to the memory reserve list, but * we don't need anything smarter at this point * * XXX Eventually check for collisions. They should NEVER happen. * If problems seem to show up, it would be a good start to track * them down. */static void __init reserve_mem(u64 base, u64 size){ u64 top = base + size; unsigned long cnt = RELOC(mem_reserve_cnt); if (size == 0) return; /* We need to always keep one empty entry so that we * have our terminator with "size" set to 0 since we are * dumb and just copy this entire array to the boot params */ base = _ALIGN_DOWN(base, PAGE_SIZE); top = _ALIGN_UP(top, PAGE_SIZE); size = top - base; if (cnt >= (MEM_RESERVE_MAP_SIZE - 1)) prom_panic("Memory reserve map exhausted !\n"); RELOC(mem_reserve_map)[cnt].base = base; RELOC(mem_reserve_map)[cnt].size = size; RELOC(mem_reserve_cnt) = cnt + 1;}/* * Initialize memory allocation mechanism, parse "memory" nodes and * obtain that way the top of memory and RMO to setup out local allocator */static void __init prom_init_mem(void){ phandle node; char *path, type[64]; unsigned int plen; cell_t *p, *endp; struct prom_t *_prom = &RELOC(prom); u32 rac, rsc; /* * We iterate the memory nodes to find * 1) top of RMO (first node) * 2) top of memory */ rac = 2; prom_getprop(_prom->root, "#address-cells", &rac, sizeof(rac)); rsc = 1; prom_getprop(_prom->root, "#size-cells", &rsc, sizeof(rsc)); prom_debug("root_addr_cells: %x\n", (unsigned long) rac); prom_debug("root_size_cells: %x\n", (unsigned long) rsc); prom_debug("scanning memory:\n"); path = RELOC(prom_scratch); for (node = 0; prom_next_node(&node); ) { type[0] = 0; prom_getprop(node, "device_type", type, sizeof(type)); if (type[0] == 0) { /* * CHRP Longtrail machines have no device_type * on the memory node, so check the name instead... */ prom_getprop(node, "name", type, sizeof(type)); } if (strcmp(type, RELOC("memory"))) continue; plen = prom_getprop(node, "reg", RELOC(regbuf), sizeof(regbuf)); if (plen > sizeof(regbuf)) { prom_printf("memory node too large for buffer !\n"); plen = sizeof(regbuf); } p = RELOC(regbuf); endp = p + (plen / sizeof(cell_t));#ifdef DEBUG_PROM memset(path, 0, PROM_SCRATCH_SIZE); call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1); prom_debug(" node %s :\n", path);#endif /* DEBUG_PROM */ while ((endp - p) >= (rac + rsc)) { unsigned long base, size; base = prom_next_cell(rac, &p); size = prom_next_cell(rsc, &p); if (size == 0) continue; prom_debug(" %x %x\n", base, size); if (base == 0 && (RELOC(of_platform) & PLATFORM_LPAR)) RELOC(rmo_top) = size; if ((base + size) > RELOC(ram_top)) RELOC(ram_top) = base + size; } } RELOC(alloc_bottom) = PAGE_ALIGN((unsigned long)&RELOC(_end) + 0x4000); /* Check if we have an initrd after the kernel, if we do move our bottom * point to after it */ if (RELOC(prom_initrd_start)) { if (RELOC(prom_initrd_end) > RELOC(alloc_bottom)) RELOC(alloc_bottom) = PAGE_ALIGN(RELOC(prom_initrd_end)); } /* * Setup our top alloc point, that is top of RMO or top of * segment 0 when running non-LPAR. * Some RS64 machines have buggy firmware where claims up at * 1GB fail. Cap at 768MB as a workaround. * Since 768MB is plenty of room, and we need to cap to something * reasonable on 32-bit, cap at 768MB on all machines. */ if (!RELOC(rmo_top)) RELOC(rmo_top) = RELOC(ram_top); RELOC(rmo_top) = min(0x30000000ul, RELOC(rmo_top)); RELOC(alloc_top) = RELOC(rmo_top); RELOC(alloc_top_high) = RELOC(ram_top); prom_printf("memory layout at init:\n"); prom_printf(" alloc_bottom : %x\n", RELOC(alloc_bottom)); prom_printf(" alloc_top : %x\n", RELOC(alloc_top)); prom_printf(" alloc_top_hi : %x\n", RELOC(alloc_top_high)); prom_printf(" rmo_top : %x\n", RELOC(rmo_top)); prom_printf(" ram_top : %x\n", RELOC(ram_top));}/* * Allocate room for and instantiate RTAS */static void __init prom_instantiate_rtas(void){ phandle rtas_node; ihandle rtas_inst; u32 base, entry = 0; u32 size = 0; prom_debug("prom_instantiate_rtas: start...\n"); rtas_node = call_prom("finddevice", 1, 1, ADDR("/rtas")); prom_debug("rtas_node: %x\n", rtas_node); if (!PHANDLE_VALID(rtas_node)) return; prom_getprop(rtas_node, "rtas-size", &size, sizeof(size)); if (size == 0) return; base = alloc_down(size, PAGE_SIZE, 0); if (base == 0) { prom_printf("RTAS allocation failed !\n"); return; } rtas_inst = call_prom("open", 1, 1, ADDR("/rtas")); if (!IHANDLE_VALID(rtas_inst)) { prom_printf("opening rtas package failed (%x)\n", rtas_inst); return; } prom_printf("instantiating rtas at 0x%x ...", base); if (call_prom_ret("call-method", 3, 2, &entry, ADDR("instantiate-rtas"), rtas_inst, base) != 0 || entry == 0) { prom_printf(" failed\n"); return; } prom_printf(" done\n"); reserve_mem(base, size); prom_setprop(rtas_node, "/rtas", "linux,rtas-base", &base, sizeof(base)); prom_setprop(rtas_node, "/rtas", "linux,rtas-entry", &entry, sizeof(entry)); prom_debug("rtas base = 0x%x\n", base); prom_debug("rtas entry = 0x%x\n", entry); prom_debug("rtas size = 0x%x\n", (long)size); prom_debug("prom_instantiate_rtas: end...\n");}#ifdef CONFIG_PPC64/* * Allocate room for and initialize TCE tables */static void __init prom_initialize_tce_table(void){ phandle node; ihandle phb_node; char compatible[64], type[64], model[64]; char *path = RELOC(prom_scratch); u64 base, align; u32 minalign, minsize; u64 tce_entry, *tce_entryp; u64 local_alloc_top, local_alloc_bottom; u64 i; if (RELOC(prom_iommu_off)) return; prom_debug("starting prom_initialize_tce_table\n"); /* Cache current top of allocs so we reserve a single block */ local_alloc_top = RELOC(alloc_top_high); local_alloc_bottom = local_alloc_top; /* Search all nodes looking for PHBs. */ for (node = 0; prom_next_node(&node); ) { compatible[0] = 0; type[0] = 0; model[0] = 0; prom_getprop(node, "compatible", compatible, sizeof(compatible)); prom_getprop(node, "device_type", type, sizeof(type)); prom_getprop(node, "model", model, sizeof(model)); if ((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) continue; /* Keep the old logic intact to avoid regression. */ if (compatible[0] != 0) { if ((strstr(compatible, RELOC("python")) == NULL) && (strstr(compatible, RELOC("Speedwagon")) == NULL) && (strstr(compatible, RELOC("Winnipeg")) == NULL)) continue; } else if (model[0] != 0) { if ((strstr(model, RELOC("ython")) == NULL) && (strstr(model, RELOC("peedwagon")) == NULL) && (strstr(model, RELOC("innipeg")) == NULL)) continue; } if (prom_getprop(node, "tce-table-minalign", &minalign, sizeof(minalign)) == PROM_ERROR) minalign = 0; if (prom_getprop(node, "tce-table-minsize", &minsize, sizeof(minsize)) == PROM_ERROR) minsize = 4UL << 20; /* * Even though we read what OF wants, we just set the table * size to 4 MB. This is enough to map 2GB of PCI DMA space. * By doing this, we avoid the pitfalls of trying to DMA to * MMIO space and the DMA alias hole. * * On POWER4, firmware sets the TCE region by assuming * each TCE table is 8MB. Using this memory for anything * else will impact performance, so we always allocate 8MB. * Anton */ if (__is_processor(PV_POWER4) || __is_processor(PV_POWER4p)) minsize = 8UL << 20; else minsize = 4UL << 20; /* Align to the greater of the align or size */ align = max(minalign, minsize); base = alloc_down(minsize, align, 1); if (base == 0) prom_panic("ERROR, cannot find space for TCE table.\n"); if (base < local_alloc_bottom) local_alloc_bottom = base; /* It seems OF doesn't null-terminate the path :-( */ memset(path, 0, PROM_SCRATCH_SIZE); /* Call OF to setup the TCE hardware */ if (call_prom("package-to-path", 3, 1, node, path, PROM_SCRATCH_SIZE-1) == PROM_ERROR) { prom_printf("package-to-path failed\n"); } /* Save away the TCE table attributes for later use. */ prom_setprop(node, path, "linux,tce-base", &base, sizeof(base)); prom_setprop(node, path, "linux,tce-size", &minsize, sizeof(minsize)); prom_debug("TCE table: %s\n", path); prom_debug("\tnode = 0x%x\n", node); prom_debug("\tbase = 0x%x\n", base); prom_debug("\tsize = 0x%x\n", minsize); /* Initialize the table to have a one-to-one mapping * over the allocated size. */ tce_entryp = (unsigned long *)base; for (i = 0; i < (minsize >> 3) ;tce_entryp++, i++) { tce_entry = (i << PAGE_SHIFT); tce_entry |= 0x3; *tce_entryp = tce_entry; } prom_printf("opening PHB %s", path); phb_node = call_prom("open", 1, 1, path); if (phb_node == 0) prom_printf("... failed\n"); else prom_printf("... done\n"); call_prom("call-method", 6, 0, ADDR("set-64-bit-addressing"), phb_node, -1, minsize, (u32) base, (u32) (base >> 32)); call_prom("close", 1, 0, phb_node); } reserve_mem(local_alloc_bottom, local_alloc_top - local_alloc_bottom); /* These are only really needed if there is a memory limit in * effect, but we don't know so export them always. */ RELOC(prom_tce_alloc_start) = local_alloc_bottom; RELOC(prom_tce_alloc_end) = local_alloc_top; /* Flag the first invalid entry */ prom_debug("ending prom_initialize_tce_table\n");}#endif/* * With CHRP SMP we need to use the OF to start the other processors. * We can't wait until smp_boot_cpus (the OF is trashed by then) * so we have to put the processors into a holding pattern controlled * by the kernel (not OF) before we destroy the OF. * * This uses a chunk of low memory, puts some holding pattern * code there and sends the other processors off to there until * smp_boot_cpus tells them to do something. The holding pattern * checks that address until its cpu # is there, when it is that * cpu jumps to __secondary_start(). smp_boot_cpus() takes care * of setting those values. * * We also use physical address 0x4 here to tell when a cpu * is in its holding pattern code. * * -- Cort */extern void __secondary_hold(void);extern unsigned long __secondary_hold_spinloop;extern unsigned long __secondary_hold_acknowledge;/* * We want to reference the copy of __secondary_hold_* in the * 0 - 0x100 address range */#define LOW_ADDR(x) (((unsigned long) &(x)) & 0xff)static void __init prom_hold_cpus(void){ unsigned long i; unsigned int reg; phandle node; char type[64]; int cpuid = 0; unsigned int interrupt_server[MAX_CPU_THREADS]; unsigned int cpu_threads, hw_cpu_num; int propsize; struct prom_t *_prom = &RELOC(prom); unsigned long *spinloop = (void *) LOW_ADDR(__secondary_hold_spinloop); unsigned long *acknowledge = (void *) LOW_ADDR(__secondary_hold_acknowledge);#ifdef CONFIG_PPC64 /* __secondary_hold is actually a descriptor, not the text address */ unsigned long secondary_hold = __pa(*PTRRELOC((unsigned long *)__secondary_hold));#else unsigned long secondary_hold = LOW_ADDR(__secondary_hold);#endif prom_debug("prom_hold_cpus: start...\n"); prom_debug(" 1) spinloop = 0x%x\n", (unsigned long)spinloop); prom_debug(" 1) *spinloop = 0x%x\n", *spinloop); prom_debug(" 1) acknowledge = 0x%x\n", (unsigned long)acknowledge); prom_debug(" 1) *acknowledge = 0x%x\n", *acknowledge); prom_debug(" 1) secondary_hold = 0x%x\n", secondary_hold); /* Set the common spinloop variable, so all of the secondary cpus * will block when they are awakened from their OF spinloop. * This must occur for both SMP and non SMP kernels, since OF will * be trashed when we move the kernel. */ *spinloop = 0; /* look for cpus */ for (node = 0; prom_next_node(&node); ) { type[0] = 0; prom_getprop(node, "device_type", type, sizeof(type)); if (strcmp(type, RELOC("cpu")) != 0) continue; /* Skip non-configured cpus. */ if (prom_getprop(node, "status", type, sizeof(type)) > 0) if (strcmp(type, RELOC("okay")) != 0) continue; reg = -1; prom_getprop(node, "reg", ®, sizeof(reg)); prom_debug("\ncpuid = 0x%x\n", cpuid); prom_debug("cpu hw idx = 0x%x\n", reg); /* Init the acknowledge var which will be reset by * the secondary cpu when it awakens from its OF * spinloop. */ *acknowledge = (unsigned long)-1; propsize = prom_getprop(node, "ibm,ppc-interrupt-server#s", &interrupt_server, sizeof(interrupt_server)); if (propsize < 0) { /* no property. old hardware has no SMT */ cpu_threads = 1; interrupt_server[0] = reg; /* fake it with phys id */ } else { /* We have a threaded processor */ cpu_threads = propsize / sizeof(u32); if (cpu_threads > MAX_CPU_THREADS) { prom_printf("SMT: too many threads!\n" "SMT: found %x, max is %x\n", cpu_threads, MAX_CPU_THREADS); cpu_threads = 1; /* ToDo: panic? */ } } hw_cpu_num = interrupt_server[0]; if (hw_cpu_num != _prom->cpu) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -