📄 prom.c
字号:
/* * We enter here early on, when the Open Firmware prom is still * handling exceptions and the MMU hash table for us. */unsigned long __initprom_init(int r3, int r4, prom_entry pp){ unsigned long mem; ihandle prom_mmu; unsigned long offset = reloc_offset(); int l; char *p, *d; unsigned long phys; /* Default */ phys = offset + KERNELBASE; /* First get a handle for the stdout device */ RELOC(prom) = pp; RELOC(prom_chosen) = call_prom(RELOC("finddevice"), 1, 1, RELOC("/chosen")); if (RELOC(prom_chosen) == (void *)-1) prom_exit(); if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), RELOC("stdout"), &RELOC(prom_stdout), sizeof(prom_stdout)) <= 0) prom_exit(); /* Get the full OF pathname of the stdout device */ mem = (unsigned long) RELOC(klimit) + offset; p = (char *) mem; memset(p, 0, 256); call_prom(RELOC("instance-to-path"), 3, 1, RELOC(prom_stdout), p, 255); RELOC(of_stdout_device) = PTRUNRELOC(p); mem += strlen(p) + 1; /* Get the boot device and translate it to a full OF pathname. */ p = (char *) mem; l = (int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), RELOC("bootpath"), p, 1<<20); if (l > 0) { p[l] = 0; /* should already be null-terminated */ RELOC(bootpath) = PTRUNRELOC(p); mem += l + 1; d = (char *) mem; *d = 0; call_prom(RELOC("canon"), 3, 1, p, d, 1<<20); RELOC(bootdevice) = PTRUNRELOC(d); mem = ALIGN(mem + strlen(d) + 1); } prom_instantiate_rtas();#ifdef CONFIG_PPC64BRIDGE /* * Find out how much memory we have and allocate a * suitably-sized hash table. */ prom_alloc_htab();#endif mem = check_display(mem); prom_print(RELOC("copying OF device tree...")); mem = copy_device_tree(mem, mem + (1<<20)); prom_print(RELOC("done\n"));#ifdef CONFIG_SMP prom_hold_cpus(mem);#endif RELOC(klimit) = (char *) (mem - offset); /* If we are already running at 0xc0000000, we assume we were loaded by * an OF bootloader which did set a BAT for us. This breaks OF translate * so we force phys to be 0 */ if (offset == 0) phys = 0; else { if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen), RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) { prom_print(RELOC(" no MMU found\n")); } else { int nargs; struct prom_args prom_args; nargs = 4; prom_args.service = RELOC("call-method"); prom_args.nargs = nargs; prom_args.nret = 4; prom_args.args[0] = RELOC("translate"); prom_args.args[1] = prom_mmu; prom_args.args[2] = (void *)(offset + KERNELBASE); prom_args.args[3] = (void *)1; RELOC(prom)(&prom_args); /* We assume the phys. address size is 3 cells */ if (prom_args.args[nargs] != 0) prom_print(RELOC(" (translate failed)\n")); else phys = (unsigned long)prom_args.args[nargs+3]; } }#ifdef CONFIG_BOOTX_TEXT if (RELOC(prom_disp_node) != 0) setup_disp_fake_bi(RELOC(prom_disp_node));#endif /* Use quiesce call to get OF to shut down any devices it's using */ prom_print(RELOC("Calling quiesce ...\n")); call_prom(RELOC("quiesce"), 0, 0);#ifdef CONFIG_BOOTX_TEXT if (RELOC(prom_disp_node) != 0) btext_prepare_BAT();#endif prom_print(RELOC("returning ")); prom_print_hex(phys); prom_print(RELOC(" from prom_init\n")); RELOC(prom_stdout) = 0; return phys;}void phys_call_rtas(int service, int nargs, int nret, ...){ va_list list; union { unsigned long words[16]; double align; } u; unsigned long offset = reloc_offset(); void (*rtas)(void *, unsigned long); int i; u.words[0] = service; u.words[1] = nargs; u.words[2] = nret; va_start(list, nret); for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); rtas = (void (*)(void *, unsigned long)) RELOC(rtas_entry); rtas(&u, RELOC(rtas_data));}static int __initprom_set_color(ihandle ih, int i, int r, int g, int b){ struct prom_args prom_args; unsigned long offset = reloc_offset(); prom_args.service = RELOC("call-method"); prom_args.nargs = 6; prom_args.nret = 1; prom_args.args[0] = RELOC("color!"); prom_args.args[1] = ih; prom_args.args[2] = (void *) i; prom_args.args[3] = (void *) b; prom_args.args[4] = (void *) g; prom_args.args[5] = (void *) r; RELOC(prom)(&prom_args); return (int) prom_args.args[6];}/* * If we have a display that we don't know how to drive, * we will want to try to execute OF's open method for it * later. However, OF will probably fall over if we do that * we've taken over the MMU. * So we check whether we will need to open the display, * and if so, open it now. */static unsigned long __initcheck_display(unsigned long mem){ phandle node; ihandle ih; int i; unsigned long offset = reloc_offset(); char type[16], *path; static unsigned char default_colors[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff, 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff }; RELOC(prom_disp_node) = 0; for (node = 0; prom_next_node(&node); ) { type[0] = 0; call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), type, sizeof(type)); if (strcmp(type, RELOC("display")) != 0) continue; /* It seems OF doesn't null-terminate the path :-( */ path = (char *) mem; memset(path, 0, 256); if ((int) call_prom(RELOC("package-to-path"), 3, 1, node, path, 255) < 0) continue; /* * If this display is the device that OF is using for stdout, * move it to the front of the list. */ mem += strlen(path) + 1; i = RELOC(prom_num_displays)++; if (RELOC(of_stdout_device) != 0 && i > 0 && strcmp(PTRRELOC(RELOC(of_stdout_device)), path) == 0) { for (; i > 0; --i) { RELOC(prom_display_paths[i]) = RELOC(prom_display_paths[i-1]); RELOC(prom_display_nodes[i]) = RELOC(prom_display_nodes[i-1]); } } RELOC(prom_display_paths[i]) = PTRUNRELOC(path); RELOC(prom_display_nodes[i]) = node; if (i == 0) RELOC(prom_disp_node) = node; if (RELOC(prom_num_displays) >= FB_MAX) break; }try_again: /* * Open the first display and set its colormap. */ if (RELOC(prom_num_displays) > 0) { path = PTRRELOC(RELOC(prom_display_paths[0])); prom_print(RELOC("opening display ")); prom_print(path); ih = call_prom(RELOC("open"), 1, 1, path); if (ih == 0 || ih == (ihandle) -1) { prom_print(RELOC("... failed\n")); for (i=1; i<RELOC(prom_num_displays); i++) { RELOC(prom_display_paths[i-1]) = RELOC(prom_display_paths[i]); RELOC(prom_display_nodes[i-1]) = RELOC(prom_display_nodes[i]); } if (--RELOC(prom_num_displays) > 0) RELOC(prom_disp_node) = RELOC(prom_display_nodes[0]); else RELOC(prom_disp_node) = NULL; goto try_again; } else { prom_print(RELOC("... ok\n")); /* * Setup a usable color table when the appropriate * method is available. * Should update this to use set-colors. */ for (i = 0; i < 32; i++) if (prom_set_color(ih, i, RELOC(default_colors)[i*3], RELOC(default_colors)[i*3+1], RELOC(default_colors)[i*3+2]) != 0) break;#ifdef CONFIG_FB for (i = 0; i < LINUX_LOGO_COLORS; i++) if (prom_set_color(ih, i + 32, RELOC(linux_logo_red)[i], RELOC(linux_logo_green)[i], RELOC(linux_logo_blue)[i]) != 0) break;#endif /* CONFIG_FB */ } } return ALIGN(mem);}/* This function will enable the early boot text when doing OF booting. This * way, xmon output should work too */#ifdef CONFIG_BOOTX_TEXTstatic void __initsetup_disp_fake_bi(ihandle dp){ int width = 640, height = 480, depth = 8, pitch; unsigned address; unsigned long offset = reloc_offset(); struct pci_reg_property addrs[8]; int i, naddrs; char name[32]; char *getprop = RELOC("getprop"); prom_print(RELOC("Initializing fake screen: ")); memset(name, 0, sizeof(name)); call_prom(getprop, 4, 1, dp, RELOC("name"), name, sizeof(name)); name[sizeof(name)-1] = 0; prom_print(name); prom_print(RELOC("\n")); call_prom(getprop, 4, 1, dp, RELOC("width"), &width, sizeof(width)); call_prom(getprop, 4, 1, dp, RELOC("height"), &height, sizeof(height)); call_prom(getprop, 4, 1, dp, RELOC("depth"), &depth, sizeof(depth)); pitch = width * ((depth + 7) / 8); call_prom(getprop, 4, 1, dp, RELOC("linebytes"), &pitch, sizeof(pitch)); if (pitch == 1) pitch = 0x1000; /* for strange IBM display */ address = 0; call_prom(getprop, 4, 1, dp, RELOC("address"), &address, sizeof(address)); if (address == 0) { /* look for an assigned address with a size of >= 1MB */ naddrs = (int) call_prom(getprop, 4, 1, dp, RELOC("assigned-addresses"), addrs, sizeof(addrs)); naddrs /= sizeof(struct pci_reg_property); for (i = 0; i < naddrs; ++i) { if (addrs[i].size_lo >= (1 << 20)) { address = addrs[i].addr.a_lo; /* use the BE aperture if possible */ if (addrs[i].size_lo >= (16 << 20)) address += (8 << 20); break; } } if (address == 0) { prom_print(RELOC("Failed to get address\n")); return; } } /* kludge for valkyrie */ if (strcmp(name, RELOC("valkyrie")) == 0) address += 0x1000; btext_setup_display(width, height, depth, pitch, address);}#endifstatic int __initprom_next_node(phandle *nodep){ phandle node; unsigned long offset = reloc_offset(); if ((node = *nodep) != 0 && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) return 1; if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) return 1; for (;;) { if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) return 0; if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) return 1; }}/* * Make a copy of the device tree from the PROM. */static unsigned long __initcopy_device_tree(unsigned long mem_start, unsigned long mem_end){ phandle root; unsigned long new_start; struct device_node **allnextp; unsigned long offset = reloc_offset(); root = call_prom(RELOC("peer"), 1, 1, (phandle)0); if (root == (phandle)0) { prom_print(RELOC("couldn't get device tree root\n")); prom_exit(); } allnextp = &RELOC(allnodes); mem_start = ALIGN(mem_start); new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); *allnextp = 0; return new_start;}static unsigned long __initinspect_node(phandle node, struct device_node *dad, unsigned long mem_start, unsigned long mem_end, struct device_node ***allnextpp){ int l; phandle child; struct device_node *np; struct property *pp, **prev_propp; char *prev_name, *namep; unsigned char *valp; unsigned long offset = reloc_offset(); np = (struct device_node *) mem_start; mem_start += sizeof(struct device_node); memset(np, 0, sizeof(*np)); np->node = node; **allnextpp = PTRUNRELOC(np); *allnextpp = &np->allnext; if (dad != 0) { np->parent = PTRUNRELOC(dad); /* we temporarily use the `next' field as `last_child'. */ if (dad->next == 0) dad->child = PTRUNRELOC(np); else dad->next->sibling = PTRUNRELOC(np); dad->next = np; } /* get and store all properties */ prev_propp = &np->properties; prev_name = RELOC(""); for (;;) { pp = (struct property *) mem_start; namep = (char *) (pp + 1); pp->name = PTRUNRELOC(namep); if ((int) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, namep) <= 0) break; mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); prev_name = namep; valp = (unsigned char *) mem_start; pp->value = PTRUNRELOC(valp); pp->length = (int) call_prom(RELOC("getprop"), 4, 1, node, namep, valp, mem_end - mem_start); if (pp->length < 0) continue;#ifdef MAX_PROPERTY_LENGTH if (pp->length > MAX_PROPERTY_LENGTH) continue; /* ignore this property */#endif mem_start = ALIGN(mem_start + pp->length); *prev_propp = PTRUNRELOC(pp); prev_propp = &pp->next; } if (np->node != NULL) { /* Add a "linux,phandle" property" */ pp = (struct property *) mem_start; *prev_propp = PTRUNRELOC(pp); prev_propp = &pp->next; namep = (char *) (pp + 1); pp->name = PTRUNRELOC(namep); strcpy(namep, RELOC("linux,phandle")); mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1); pp->value = (unsigned char *) PTRUNRELOC(&np->node); pp->length = sizeof(np->node); } *prev_propp = NULL; /* get the node's full name */ l = (int) call_prom(RELOC("package-to-path"), 3, 1, node, (char *) mem_start, mem_end - mem_start); if (l >= 0) { np->full_name = PTRUNRELOC((char *) mem_start); *(char *)(mem_start + l) = 0; mem_start = ALIGN(mem_start + l + 1); } /* do all our children */ child = call_prom(RELOC("child"), 1, 1, node); while (child != (void *)0) { mem_start = inspect_node(child, np, mem_start, mem_end, allnextpp); child = call_prom(RELOC("peer"), 1, 1, child); } return mem_start;}/* * finish_device_tree is called once things are running normally * (i.e. with text and data mapped to the address they were linked at). * It traverses the device tree and fills in the name, type, * {n_}addrs and {n_}intrs fields of each node. */void __initfinish_device_tree(void){ unsigned long mem = (unsigned long) klimit; struct device_node *np; /* All newworld pmac machines and CHRPs now use the interrupt tree */ for (np = allnodes; np != NULL; np = np->allnext) { if (get_property(np, "interrupt-parent", 0)) { use_of_interrupt_tree = 1; break; } } if (_machine == _MACH_Pmac && use_of_interrupt_tree) pmac_newworld = 1;#ifdef CONFIG_BOOTX_TEXT if (boot_infos && pmac_newworld) { prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n"); prom_print(" You should use an Open Firmware bootloader\n"); }#endif /* CONFIG_BOOTX_TEXT */ if (use_of_interrupt_tree) { /* * We want to find out here how many interrupt-controller * nodes there are, and if we are booted from BootX, * we need a pointer to the first (and hopefully only) * such node. But we can't use find_devices here since * np->name has not been set yet. -- paulus */ int n = 0; char *name, *ic; int iclen; for (np = allnodes; np != NULL; np = np->allnext) { ic = get_property(np, "interrupt-controller", &iclen); name = get_property(np, "name", NULL); /* checking iclen makes sure we don't get a false match on /chosen.interrupt_controller */ if ((name != NULL && strcmp(name, "interrupt-controller") == 0) || (ic != NULL && iclen == 0)) { if (n == 0) dflt_interrupt_controller = np; ++n; } } num_interrupt_controllers = n; } mem = finish_node(allnodes, mem, NULL, 1, 1); dev_tree_size = mem - (unsigned long) allnodes; klimit = (char *) mem;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -