📄 prom_init.c
字号:
/* * Note that prom_init() and anything called from prom_init() * may be running at an address that is different from the address * that it was linked at. References to static data items are * handled by compiling this file with -mrelocatable-lib. */#include <linux/config.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/init.h>#include <linux/version.h>#include <linux/threads.h>#include <linux/spinlock.h>#include <linux/ioport.h>#include <linux/pci.h>#include <linux/slab.h>#include <asm/sections.h>#include <asm/prom.h>#include <asm/page.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/smp.h>#include <asm/bootx.h>#include <asm/system.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/bitops.h>#include <asm/bootinfo.h>#include <asm/btext.h>#include <asm/pci-bridge.h>#include <asm/open_pic.h>#include <asm/cacheflush.h>#ifdef CONFIG_LOGO_LINUX_CLUT224#include <linux/linux_logo.h>extern const struct linux_logo logo_linux_clut224;#endif/* * Properties whose value is longer than this get excluded from our * copy of the device tree. This way we don't waste space storing * things like "driver,AAPL,MacOS,PowerPC" properties. But this value * does need to be big enough to ensure that we don't lose things * like the interrupt-map property on a PCI-PCI bridge. */#define MAX_PROPERTY_LENGTH 4096#ifndef FB_MAX /* avoid pulling in all of the fb stuff */#define FB_MAX 8#endif#define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))struct prom_args { const char *service; int nargs; int nret; void *args[10];};struct pci_address { unsigned a_hi; unsigned a_mid; unsigned a_lo;};struct pci_reg_property { struct pci_address addr; unsigned size_hi; unsigned size_lo;};struct pci_range { struct pci_address addr; unsigned phys; unsigned size_hi; unsigned size_lo;};struct isa_reg_property { unsigned space; unsigned address; unsigned size;};struct pci_intr_map { struct pci_address addr; unsigned dunno; phandle int_ctrler; unsigned intr;};static void prom_exit(void);static void *call_prom(const char *service, int nargs, int nret, ...);static void *call_prom_ret(const char *service, int nargs, int nret, void **rets, ...);static void prom_print_hex(unsigned int v);static int prom_set_color(ihandle ih, int i, int r, int g, int b);static int prom_next_node(phandle *nodep);static unsigned long check_display(unsigned long mem);static void setup_disp_fake_bi(ihandle dp);static unsigned long copy_device_tree(unsigned long mem_start, unsigned long mem_end);static unsigned long inspect_node(phandle node, struct device_node *dad, unsigned long mem_start, unsigned long mem_end, struct device_node ***allnextpp);static void prom_hold_cpus(unsigned long mem);static void prom_instantiate_rtas(void);static void * early_get_property(unsigned long base, unsigned long node, char *prop);prom_entry prom __initdata;ihandle prom_chosen __initdata;ihandle prom_stdout __initdata;char *prom_display_paths[FB_MAX] __initdata;phandle prom_display_nodes[FB_MAX] __initdata;unsigned int prom_num_displays __initdata;char *of_stdout_device __initdata;static ihandle prom_disp_node __initdata;unsigned int rtas_data; /* physical pointer */unsigned int rtas_entry; /* physical pointer */unsigned int rtas_size;unsigned int old_rtas;boot_infos_t *boot_infos;char *bootpath;char *bootdevice;struct device_node *allnodes;extern char *klimit;static void __initprom_exit(void){ struct prom_args args; args.service = "exit"; args.nargs = 0; args.nret = 0; prom(&args); for (;;) /* should never get here */ ;}static void * __initcall_prom(const char *service, int nargs, int nret, ...){ va_list list; int i; struct prom_args prom_args; prom_args.service = service; prom_args.nargs = nargs; prom_args.nret = nret; va_start(list, nret); for (i = 0; i < nargs; ++i) prom_args.args[i] = va_arg(list, void *); va_end(list); for (i = 0; i < nret; ++i) prom_args.args[i + nargs] = NULL; prom(&prom_args); return prom_args.args[nargs];}static void * __initcall_prom_ret(const char *service, int nargs, int nret, void **rets, ...){ va_list list; int i; struct prom_args prom_args; prom_args.service = service; prom_args.nargs = nargs; prom_args.nret = nret; va_start(list, rets); for (i = 0; i < nargs; ++i) prom_args.args[i] = va_arg(list, void *); va_end(list); for (i = 0; i < nret; ++i) prom_args.args[i + nargs] = NULL; prom(&prom_args); for (i = 1; i < nret; ++i) rets[i-1] = prom_args.args[nargs + i]; return prom_args.args[nargs];}void __initprom_print(const char *msg){ const char *p, *q; if (prom_stdout == 0) return; for (p = msg; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n'; ++q) ; if (q > p) call_prom("write", 3, 1, prom_stdout, p, q - p); if (*q != 0) { ++q; call_prom("write", 3, 1, prom_stdout, "\r\n", 2); } }}static void __initprom_print_hex(unsigned int v){ char buf[16]; int i, c; for (i = 0; i < 8; ++i) { c = (v >> ((7-i)*4)) & 0xf; c += (c >= 10)? ('a' - 10): '0'; buf[i] = c; } buf[i] = ' '; buf[i+1] = 0; prom_print(buf);}static int __initprom_set_color(ihandle ih, int i, int r, int g, int b){ struct prom_args prom_args; prom_args.service = "call-method"; prom_args.nargs = 6; prom_args.nret = 1; prom_args.args[0] = "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; prom(&prom_args); return (int) prom_args.args[6];}static int __initprom_next_node(phandle *nodep){ phandle node; if ((node = *nodep) != 0 && (*nodep = call_prom("child", 1, 1, node)) != 0) return 1; if ((*nodep = call_prom("peer", 1, 1, node)) != 0) return 1; for (;;) { if ((node = call_prom("parent", 1, 1, node)) == 0) return 0; if ((*nodep = call_prom("peer", 1, 1, node)) != 0) return 1; }}#ifdef CONFIG_POWER4/* * Set up a hash table with a set of entries in it to map the * first 64MB of RAM. This is used on 64-bit machines since * some of them don't have BATs. */static inline void make_pte(unsigned long htab, unsigned int hsize, unsigned int va, unsigned int pa, int mode){ unsigned int *pteg; unsigned int hash, i, vsid; vsid = ((va >> 28) * 0x111) << 12; hash = ((va ^ vsid) >> 5) & 0x7fff80; pteg = (unsigned int *)(htab + (hash & (hsize - 1))); for (i = 0; i < 8; ++i, pteg += 4) { if ((pteg[1] & 1) == 0) { pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; pteg[3] = pa | mode; break; } }}extern unsigned long _SDR1;extern PTE *Hash;extern unsigned long Hash_size;static void __initprom_alloc_htab(void){ unsigned int hsize; unsigned long htab; unsigned int addr; /* * Because of OF bugs we can't use the "claim" client * interface to allocate memory for the hash table. * This code is only used on 64-bit PPCs, and the only * 64-bit PPCs at the moment are RS/6000s, and their * OF is based at 0xc00000 (the 12M point), so we just * arbitrarily use the 0x800000 - 0xc00000 region for the * hash table. * -- paulus. */ hsize = 4 << 20; /* POWER4 has no BATs */ htab = (8 << 20); call_prom("claim", 3, 1, htab, hsize, 0); Hash = (void *)(htab + KERNELBASE); Hash_size = hsize; _SDR1 = htab + __ilog2(hsize) - 18; /* * Put in PTEs for the first 64MB of RAM */ memset((void *)htab, 0, hsize); for (addr = 0; addr < 0x4000000; addr += 0x1000) make_pte(htab, hsize, addr + KERNELBASE, addr, _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX);#if 0 /* DEBUG stuff mapping the SCC */ make_pte(htab, hsize, 0x80013000, 0x80013000, _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX);#endif}#endif /* CONFIG_POWER4 *//* * 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, j; 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 }; const unsigned char *clut; prom_disp_node = NULL; for (node = NULL; prom_next_node(&node); ) { type[0] = 0; call_prom("getprop", 4, 1, node, "device_type", type, sizeof(type)); if (strcmp(type, "display") != 0) continue; /* It seems OF doesn't null-terminate the path :-( */ path = (char *) mem; memset(path, 0, 256); if ((int) call_prom("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 = prom_num_displays++; if (of_stdout_device != 0 && i > 0 && strcmp(of_stdout_device, path) == 0) { for (; i > 0; --i) { prom_display_paths[i] = prom_display_paths[i-1]; prom_display_nodes[i] = prom_display_nodes[i-1]; } } prom_display_paths[i] = path; prom_display_nodes[i] = node; if (i == 0) prom_disp_node = node; if (prom_num_displays >= FB_MAX) break; } for (j=0; j<prom_num_displays; j++) { path = prom_display_paths[j]; prom_print("opening display "); prom_print(path); ih = call_prom("open", 1, 1, path); if (ih == 0 || ih == (ihandle) -1) { prom_print("... failed\n"); for (i=j+1; i<prom_num_displays; i++) { prom_display_paths[i-1] = prom_display_paths[i]; prom_display_nodes[i-1] = prom_display_nodes[i]; } if (--prom_num_displays > 0) { prom_disp_node = prom_display_nodes[j]; j--; } else prom_disp_node = NULL; continue; } else { prom_print("... ok\n"); /* * Setup a usable color table when the appropriate * method is available. * Should update this to use set-colors. */ clut = default_colors; for (i = 0; i < 32; i++, clut += 3) if (prom_set_color(ih, i, clut[0], clut[1], clut[2]) != 0) break;#ifdef CONFIG_LOGO_LINUX_CLUT224 clut = PTRRELOC(logo_linux_clut224.clut); for (i = 0; i < logo_linux_clut224.clutsize; i++, clut += 3) if (prom_set_color(ih, i + 32, clut[0], clut[1], clut[2]) != 0) break;#endif /* CONFIG_LOGO_LINUX_CLUT224 */ } } return ALIGNUL(mem);}/* This function will enable the early boot text when doing OF booting. This * way, xmon output should work too */static void __initsetup_disp_fake_bi(ihandle dp){#ifdef CONFIG_BOOTX_TEXT int width = 640, height = 480, depth = 8, pitch; unsigned address; struct pci_reg_property addrs[8]; int i, naddrs; char name[32]; char *getprop = "getprop"; prom_print("Initializing fake screen: "); memset(name, 0, sizeof(name)); call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); name[sizeof(name)-1] = 0; prom_print(name); prom_print("\n"); call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); pitch = width * ((depth + 7) / 8); call_prom(getprop, 4, 1, dp, "linebytes", &pitch, sizeof(pitch)); if (pitch == 1) pitch = 0x1000; /* for strange IBM display */ address = 0; call_prom(getprop, 4, 1, dp, "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, "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("Failed to get address\n"); return; } } /* kludge for valkyrie */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -