📄 prom_init.c
字号:
static int __init prom_setprop(phandle node, const char *nodename, const char *pname, void *value, size_t valuelen){ char cmd[256], *p; if (!(OF_WORKAROUNDS & OF_WA_LONGTRAIL)) return call_prom("setprop", 4, 1, node, ADDR(pname), (u32)(unsigned long) value, (u32) valuelen); /* gah... setprop doesn't work on longtrail, have to use interpret */ p = cmd; add_string(&p, "dev"); add_string(&p, nodename); add_string(&p, tohex((u32)(unsigned long) value)); add_string(&p, tohex(valuelen)); add_string(&p, tohex(ADDR(pname))); add_string(&p, tohex(strlen(RELOC(pname)))); add_string(&p, "property"); *p = 0; return call_prom("interpret", 1, 1, (u32)(unsigned long) cmd);}/* We can't use the standard versions because of RELOC headaches. */#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ || ('a' <= (c) && (c) <= 'f') \ || ('A' <= (c) && (c) <= 'F'))#define isdigit(c) ('0' <= (c) && (c) <= '9')#define islower(c) ('a' <= (c) && (c) <= 'z')#define toupper(c) (islower(c) ? ((c) - 'a' + 'A') : (c))unsigned long prom_strtoul(const char *cp, const char **endp){ unsigned long result = 0, base = 10, value; if (*cp == '0') { base = 8; cp++; if (toupper(*cp) == 'X') { cp++; base = 16; } } while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' : toupper(*cp) - 'A' + 10) < base) { result = result * base + value; cp++; } if (endp) *endp = cp; return result;}unsigned long prom_memparse(const char *ptr, const char **retptr){ unsigned long ret = prom_strtoul(ptr, retptr); int shift = 0; /* * We can't use a switch here because GCC *may* generate a * jump table which won't work, because we're not running at * the address we're linked at. */ if ('G' == **retptr || 'g' == **retptr) shift = 30; if ('M' == **retptr || 'm' == **retptr) shift = 20; if ('K' == **retptr || 'k' == **retptr) shift = 10; if (shift) { ret <<= shift; (*retptr)++; } return ret;}/* * Early parsing of the command line passed to the kernel, used for * "mem=x" and the options that affect the iommu */static void __init early_cmdline_parse(void){ struct prom_t *_prom = &RELOC(prom);#ifdef CONFIG_PPC64 const char *opt;#endif char *p; int l = 0; RELOC(prom_cmd_line[0]) = 0; p = RELOC(prom_cmd_line); if ((long)_prom->chosen > 0) l = prom_getprop(_prom->chosen, "bootargs", p, COMMAND_LINE_SIZE-1);#ifdef CONFIG_CMDLINE if (l <= 0 || p[0] == '\0') /* dbl check */ strlcpy(RELOC(prom_cmd_line), RELOC(CONFIG_CMDLINE), sizeof(prom_cmd_line));#endif /* CONFIG_CMDLINE */ prom_printf("command line: %s\n", RELOC(prom_cmd_line));#ifdef CONFIG_PPC64 opt = strstr(RELOC(prom_cmd_line), RELOC("iommu=")); if (opt) { prom_printf("iommu opt is: %s\n", opt); opt += 6; while (*opt && *opt == ' ') opt++; if (!strncmp(opt, RELOC("off"), 3)) RELOC(prom_iommu_off) = 1; else if (!strncmp(opt, RELOC("force"), 5)) RELOC(prom_iommu_force_on) = 1; }#endif}#ifdef CONFIG_PPC_PSERIES/* * There are two methods for telling firmware what our capabilities are. * Newer machines have an "ibm,client-architecture-support" method on the * root node. For older machines, we have to call the "process-elf-header" * method in the /packages/elf-loader node, passing it a fake 32-bit * ELF header containing a couple of PT_NOTE sections that contain * structures that contain various information. *//* * New method - extensible architecture description vector. * * Because the description vector contains a mix of byte and word * values, we declare it as an unsigned char array, and use this * macro to put word values in. */#define W(x) ((x) >> 24) & 0xff, ((x) >> 16) & 0xff, \ ((x) >> 8) & 0xff, (x) & 0xff/* Option vector bits - generic bits in byte 1 */#define OV_IGNORE 0x80 /* ignore this vector */#define OV_CESSATION_POLICY 0x40 /* halt if unsupported option present*//* Option vector 1: processor architectures supported */#define OV1_PPC_2_00 0x80 /* set if we support PowerPC 2.00 */#define OV1_PPC_2_01 0x40 /* set if we support PowerPC 2.01 */#define OV1_PPC_2_02 0x20 /* set if we support PowerPC 2.02 */#define OV1_PPC_2_03 0x10 /* set if we support PowerPC 2.03 */#define OV1_PPC_2_04 0x08 /* set if we support PowerPC 2.04 */#define OV1_PPC_2_05 0x04 /* set if we support PowerPC 2.05 *//* Option vector 2: Open Firmware options supported */#define OV2_REAL_MODE 0x20 /* set if we want OF in real mode *//* Option vector 3: processor options supported */#define OV3_FP 0x80 /* floating point */#define OV3_VMX 0x40 /* VMX/Altivec */#define OV3_DFP 0x20 /* decimal FP *//* Option vector 5: PAPR/OF options supported */#define OV5_LPAR 0x80 /* logical partitioning supported */#define OV5_SPLPAR 0x40 /* shared-processor LPAR supported *//* ibm,dynamic-reconfiguration-memory property supported */#define OV5_DRCONF_MEMORY 0x20#define OV5_LARGE_PAGES 0x10 /* large pages supported */#define OV5_DONATE_DEDICATE_CPU 0x02 /* donate dedicated CPU support *//* PCIe/MSI support. Without MSI full PCIe is not supported */#ifdef CONFIG_PCI_MSI#define OV5_MSI 0x01 /* PCIe/MSI support */#else#define OV5_MSI 0x00#endif /* CONFIG_PCI_MSI *//* * The architecture vector has an array of PVR mask/value pairs, * followed by # option vectors - 1, followed by the option vectors. */static unsigned char ibm_architecture_vec[] = { W(0xfffe0000), W(0x003a0000), /* POWER5/POWER5+ */ W(0xffff0000), W(0x003e0000), /* POWER6 */ W(0xffffffff), W(0x0f000002), /* all 2.05-compliant */ W(0xfffffffe), W(0x0f000001), /* all 2.04-compliant and earlier */ 5 - 1, /* 5 option vectors */ /* option vector 1: processor architectures supported */ 3 - 2, /* length */ 0, /* don't ignore, don't halt */ OV1_PPC_2_00 | OV1_PPC_2_01 | OV1_PPC_2_02 | OV1_PPC_2_03 | OV1_PPC_2_04 | OV1_PPC_2_05, /* option vector 2: Open Firmware options supported */ 34 - 2, /* length */ OV2_REAL_MODE, 0, 0, W(0xffffffff), /* real_base */ W(0xffffffff), /* real_size */ W(0xffffffff), /* virt_base */ W(0xffffffff), /* virt_size */ W(0xffffffff), /* load_base */ W(64), /* 128MB min RMA */ W(0xffffffff), /* full client load */ 0, /* min RMA percentage of total RAM */ 48, /* max log_2(hash table size) */ /* option vector 3: processor options supported */ 3 - 2, /* length */ 0, /* don't ignore, don't halt */ OV3_FP | OV3_VMX | OV3_DFP, /* option vector 4: IBM PAPR implementation */ 2 - 2, /* length */ 0, /* don't halt */ /* option vector 5: PAPR/OF options */ 3 - 2, /* length */ 0, /* don't ignore, don't halt */ OV5_LPAR | OV5_SPLPAR | OV5_LARGE_PAGES | OV5_DRCONF_MEMORY | OV5_DONATE_DEDICATE_CPU | OV5_MSI,};/* Old method - ELF header with PT_NOTE sections */static struct fake_elf { Elf32_Ehdr elfhdr; Elf32_Phdr phdr[2]; struct chrpnote { u32 namesz; u32 descsz; u32 type; char name[8]; /* "PowerPC" */ struct chrpdesc { u32 real_mode; u32 real_base; u32 real_size; u32 virt_base; u32 virt_size; u32 load_base; } chrpdesc; } chrpnote; struct rpanote { u32 namesz; u32 descsz; u32 type; char name[24]; /* "IBM,RPA-Client-Config" */ struct rpadesc { u32 lpar_affinity; u32 min_rmo_size; u32 min_rmo_percent; u32 max_pft_size; u32 splpar; u32 min_load; u32 new_mem_def; u32 ignore_me; } rpadesc; } rpanote;} fake_elf = { .elfhdr = { .e_ident = { 0x7f, 'E', 'L', 'F', ELFCLASS32, ELFDATA2MSB, EV_CURRENT }, .e_type = ET_EXEC, /* yeah right */ .e_machine = EM_PPC, .e_version = EV_CURRENT, .e_phoff = offsetof(struct fake_elf, phdr), .e_phentsize = sizeof(Elf32_Phdr), .e_phnum = 2 }, .phdr = { [0] = { .p_type = PT_NOTE, .p_offset = offsetof(struct fake_elf, chrpnote), .p_filesz = sizeof(struct chrpnote) }, [1] = { .p_type = PT_NOTE, .p_offset = offsetof(struct fake_elf, rpanote), .p_filesz = sizeof(struct rpanote) } }, .chrpnote = { .namesz = sizeof("PowerPC"), .descsz = sizeof(struct chrpdesc), .type = 0x1275, .name = "PowerPC", .chrpdesc = { .real_mode = ~0U, /* ~0 means "don't care" */ .real_base = ~0U, .real_size = ~0U, .virt_base = ~0U, .virt_size = ~0U, .load_base = ~0U }, }, .rpanote = { .namesz = sizeof("IBM,RPA-Client-Config"), .descsz = sizeof(struct rpadesc), .type = 0x12759999, .name = "IBM,RPA-Client-Config", .rpadesc = { .lpar_affinity = 0, .min_rmo_size = 64, /* in megabytes */ .min_rmo_percent = 0, .max_pft_size = 48, /* 2^48 bytes max PFT size */ .splpar = 1, .min_load = ~0U, .new_mem_def = 0 } }};static void __init prom_send_capabilities(void){ ihandle elfloader, root; prom_arg_t ret; root = call_prom("open", 1, 1, ADDR("/")); if (root != 0) { /* try calling the ibm,client-architecture-support method */ if (call_prom_ret("call-method", 3, 2, &ret, ADDR("ibm,client-architecture-support"), root, ADDR(ibm_architecture_vec)) == 0) { /* the call exists... */ if (ret) prom_printf("WARNING: ibm,client-architecture" "-support call FAILED!\n"); call_prom("close", 1, 0, root); return; } call_prom("close", 1, 0, root); } /* no ibm,client-architecture-support call, try the old way */ elfloader = call_prom("open", 1, 1, ADDR("/packages/elf-loader")); if (elfloader == 0) { prom_printf("couldn't open /packages/elf-loader\n"); return; } call_prom("call-method", 3, 1, ADDR("process-elf-header"), elfloader, ADDR(&fake_elf)); call_prom("close", 1, 0, elfloader);}#endif/* * Memory allocation strategy... our layout is normally: * * at 14Mb or more we have vmlinux, then a gap and initrd. In some * rare cases, initrd might end up being before the kernel though. * We assume this won't override the final kernel at 0, we have no * provision to handle that in this version, but it should hopefully * never happen. * * alloc_top is set to the top of RMO, eventually shrink down if the * TCEs overlap * * alloc_bottom is set to the top of kernel/initrd * * from there, allocations are done this way : rtas is allocated * topmost, and the device-tree is allocated from the bottom. We try * to grow the device-tree allocation as we progress. If we can't, * then we fail, we don't currently have a facility to restart * elsewhere, but that shouldn't be necessary. * * Note that calls to reserve_mem have to be done explicitly, memory * allocated with either alloc_up or alloc_down isn't automatically * reserved. *//* * Allocates memory in the RMO upward from the kernel/initrd * * When align is 0, this is a special case, it means to allocate in place * at the current location of alloc_bottom or fail (that is basically * extending the previous allocation). Used for the device-tree flattening */static unsigned long __init alloc_up(unsigned long size, unsigned long align){ unsigned long base = RELOC(alloc_bottom); unsigned long addr = 0; if (align) base = _ALIGN_UP(base, align); prom_debug("alloc_up(%x, %x)\n", size, align); if (RELOC(ram_top) == 0) prom_panic("alloc_up() called with mem not initialized\n"); if (align) base = _ALIGN_UP(RELOC(alloc_bottom), align); else base = RELOC(alloc_bottom); for(; (base + size) <= RELOC(alloc_top); base = _ALIGN_UP(base + 0x100000, align)) { prom_debug(" trying: 0x%x\n\r", base); addr = (unsigned long)prom_claim(base, size, 0); if (addr != PROM_ERROR && addr != 0) break; addr = 0; if (align == 0) break; } if (addr == 0) return 0; RELOC(alloc_bottom) = addr; prom_debug(" -> %x\n", addr); prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom)); prom_debug(" alloc_top : %x\n", RELOC(alloc_top)); 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;}/* * Allocates memory downward, either from top of RMO, or if highmem * is set, from the top of RAM. Note that this one doesn't handle * failures. It does claim memory if highmem is not set. */static unsigned long __init alloc_down(unsigned long size, unsigned long align, int highmem){ unsigned long base, addr = 0; prom_debug("alloc_down(%x, %x, %s)\n", size, align, highmem ? RELOC("(high)") : RELOC("(low)")); if (RELOC(ram_top) == 0) prom_panic("alloc_down() called with mem not initialized\n"); if (highmem) { /* Carve out storage for the TCE table. */ addr = _ALIGN_DOWN(RELOC(alloc_top_high) - size, align); if (addr <= RELOC(alloc_bottom)) return 0; /* Will we bump into the RMO ? If yes, check out that we * didn't overlap existing allocations there, if we did, * we are dead, we must be the first in town ! */ if (addr < RELOC(rmo_top)) { /* Good, we are first */ if (RELOC(alloc_top) == RELOC(rmo_top)) RELOC(alloc_top) = RELOC(rmo_top) = addr; else return 0; } RELOC(alloc_top_high) = addr; goto bail; } base = _ALIGN_DOWN(RELOC(alloc_top) - size, align); for (; base > RELOC(alloc_bottom); base = _ALIGN_DOWN(base - 0x100000, align)) { prom_debug(" trying: 0x%x\n\r", base); addr = (unsigned long)prom_claim(base, size, 0); if (addr != PROM_ERROR && addr != 0) break; addr = 0; } if (addr == 0) return 0; RELOC(alloc_top) = addr; bail: prom_debug(" -> %x\n", addr); prom_debug(" alloc_bottom : %x\n", RELOC(alloc_bottom)); prom_debug(" alloc_top : %x\n", RELOC(alloc_top));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -