📄 prom.c
字号:
/* * BK Id: SCCS/s.prom.c 1.48 12/19/01 10:50:58 paulus *//* * Procedures for interfacing to the Open Firmware PROM on * Power Macintosh computers. * * In particular, we are interested in the device tree * and in using some of its services (exit, write to stdout). * * Paul Mackerras August 1996. * Copyright (C) 1996 Paul Mackerras. */#include <stdarg.h>#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/processor.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 "open_pic.h"#ifdef CONFIG_FB#include <asm/linux_logo.h>#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 4096struct 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;};typedef unsigned long interpret_func(struct device_node *, unsigned long, int, int);static interpret_func interpret_pci_props;static interpret_func interpret_dbdma_props;static interpret_func interpret_isa_props;static interpret_func interpret_macio_props;static interpret_func interpret_root_props;#ifndef FB_MAX /* avoid pulling in all of the fb stuff */#define FB_MAX 8#endifchar *prom_display_paths[FB_MAX] __initdata = { 0, };phandle prom_display_nodes[FB_MAX] __initdata;unsigned int prom_num_displays __initdata = 0;char *of_stdout_device __initdata = 0;ihandle prom_disp_node __initdata = 0;prom_entry prom __initdata = 0;ihandle prom_chosen __initdata = 0;ihandle prom_stdout __initdata = 0;extern char *klimit;char *bootpath;char *bootdevice;unsigned int rtas_data; /* physical pointer */unsigned int rtas_entry; /* physical pointer */unsigned int rtas_size;unsigned int old_rtas;/* Set for a newworld or CHRP machine */int use_of_interrupt_tree;struct device_node *dflt_interrupt_controller;int num_interrupt_controllers;int pmac_newworld;static struct device_node *allnodes;static void *call_prom(const char *service, int nargs, int nret, ...);static void prom_exit(void);static unsigned long copy_device_tree(unsigned long, unsigned long);static unsigned long inspect_node(phandle, struct device_node *, unsigned long, unsigned long, struct device_node ***);static unsigned long finish_node(struct device_node *, unsigned long, interpret_func *, int, int);static unsigned long finish_node_interrupts(struct device_node *, unsigned long);static unsigned long check_display(unsigned long);static int prom_next_node(phandle *);static void *early_get_property(unsigned long, unsigned long, char *);static struct device_node *find_phandle(phandle);#ifdef CONFIG_BOOTX_TEXTstatic void setup_disp_fake_bi(ihandle dp);#endifextern void enter_rtas(void *);void phys_call_rtas(int, int, int, ...);extern char cmd_line[512]; /* XXX */boot_infos_t *boot_infos;unsigned long dev_tree_size;#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))/* Is boot-info compatible ? */#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION)#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2)#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4)/* * Note that prom_init() and anything called from prom_init() must * use the RELOC/PTRRELOC macros to access any static data in * memory, since the kernel may be running at an address that is * different from the address that it was linked at. * (Note that strings count as static variables.) */static void __initprom_exit(){ struct prom_args args; unsigned long offset = reloc_offset(); args.service = "exit"; args.nargs = 0; args.nret = 0; RELOC(prom)(&args); for (;;) /* should never get here */ ;}void __initprom_enter(void){ struct prom_args args; unsigned long offset = reloc_offset(); args.service = RELOC("enter"); args.nargs = 0; args.nret = 0; RELOC(prom)(&args);}static void * __initcall_prom(const char *service, int nargs, int nret, ...){ va_list list; int i; unsigned long offset = reloc_offset(); 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] = 0; RELOC(prom)(&prom_args); return prom_args.args[nargs];}void __initprom_print(const char *msg){ const char *p, *q; unsigned long offset = reloc_offset(); if (RELOC(prom_stdout) == 0) return; for (p = msg; *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n'; ++q) ; if (q > p) call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout), p, q - p); if (*q != 0) { ++q; call_prom(RELOC("write"), 3, 1, RELOC(prom_stdout), RELOC("\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);}unsigned long smp_chrp_cpu_nr __initdata = 0;#ifdef CONFIG_SMP/* * With CHRP SMP we need to use the OF to start the other * processors so 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 high memory, puts some holding pattern * code there and sends the other processors off to there until * smp_boot_cpus tells them to do something. We do that by using * physical address 0x0. 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 */static void __initprom_hold_cpus(unsigned long mem){ extern void __secondary_hold(void); unsigned long i; int cpu; phandle node; unsigned long offset = reloc_offset(); char type[16], *path; unsigned int reg; /* * XXX: hack to make sure we're chrp, assume that if we're * chrp we have a device_type property -- Cort */ node = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); if ( (int)call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"),type, sizeof(type)) <= 0) return; /* copy the holding pattern code to someplace safe (0) */ /* the holding pattern is now within the first 0x100 bytes of the kernel image -- paulus */ memcpy((void *)0, (void *)(KERNELBASE + offset), 0x100); flush_icache_range(0, 0x100); /* look for cpus */ *(unsigned long *)(0x0) = 0; asm volatile("dcbf 0,%0": : "r" (0) : "memory"); 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("cpu")) != 0) continue; path = (char *) mem; memset(path, 0, 256); if ((int) call_prom(RELOC("package-to-path"), 3, 1, node, path, 255) < 0) continue; reg = -1; call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), ®, sizeof(reg)); cpu = RELOC(smp_chrp_cpu_nr)++; RELOC(smp_hw_index)[cpu] = reg; /* XXX: hack - don't start cpu 0, this cpu -- Cort */ if (cpu == 0) continue; prom_print(RELOC("starting cpu ")); prom_print(path); *(ulong *)(0x4) = 0; call_prom(RELOC("start-cpu"), 3, 0, node, __pa(__secondary_hold), cpu); prom_print(RELOC("...")); for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) ; if (*(ulong *)(0x4) == cpu) prom_print(RELOC("ok\n")); else { prom_print(RELOC("failed: ")); prom_print_hex(*(ulong *)0x4); prom_print(RELOC("\n")); } }}#endif /* CONFIG_SMP */void __initbootx_init(unsigned long r4, unsigned long phys){ boot_infos_t *bi = (boot_infos_t *) r4; unsigned long space; unsigned long ptr, x; char *model; unsigned long offset = reloc_offset(); RELOC(boot_infos) = PTRUNRELOC(bi); if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) bi->logicalDisplayBase = 0;#ifdef CONFIG_BOOTX_TEXT btext_init(bi); /* * Test if boot-info is compatible. Done only in config * CONFIG_BOOTX_TEXT since there is nothing much we can do * with an incompatible version, except display a message * and eventually hang the processor... * * I'll try to keep enough of boot-info compatible in the * future to always allow display of this message; */ if (!BOOT_INFO_IS_COMPATIBLE(bi)) { btext_drawstring(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); btext_flushscreen(); }#endif /* CONFIG_BOOTX_TEXT */ /* New BootX enters kernel with MMU off, i/os are not allowed here. This hack will have been done by the boostrap anyway. */ if (bi->version < 4) { /* * XXX If this is an iMac, turn off the USB controller. */ model = (char *) early_get_property (r4 + bi->deviceTreeOffset, 4, RELOC("model")); if (model && (strcmp(model, RELOC("iMac,1")) == 0 || strcmp(model, RELOC("PowerMac1,1")) == 0)) { out_le32((unsigned *)0x80880008, 1); /* XXX */ } } /* Move klimit to enclose device tree, args, ramdisk, etc... */ if (bi->version < 5) { space = bi->deviceTreeOffset + bi->deviceTreeSize; if (bi->ramDisk) space = bi->ramDisk + bi->ramDiskSize; } else space = bi->totalParamsSize; RELOC(klimit) = PTRUNRELOC((char *) bi + space); /* New BootX will have flushed all TLBs and enters kernel with MMU switched OFF, so this should not be useful anymore. */ if (bi->version < 4) { /* * Touch each page to make sure the PTEs for them * are in the hash table - the aim is to try to avoid * getting DSI exceptions while copying the kernel image. */ for (ptr = (KERNELBASE + offset) & PAGE_MASK; ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) x = *(volatile unsigned long *)ptr; }#ifdef CONFIG_BOOTX_TEXT /* * Note that after we call prepare_disp_BAT, we can't do * prom_draw*, flushscreen or clearscreen until we turn the MMU * on, since prepare_disp_BAT sets disp_bi->logicalDisplayBase * to a virtual address. */ btext_prepare_BAT();#endif}#ifdef CONFIG_PPC64BRIDGE/* * 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. * We assume the PTE will fit in the primary PTEG. */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; unsigned long offset = reloc_offset(); /* * 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. */#ifdef CONFIG_POWER4 hsize = 4 << 20; /* POWER4 has no BATs */#else hsize = 2 << 20;#endif /* CONFIG_POWER4 */ htab = (8 << 20); RELOC(Hash) = (void *)(htab + KERNELBASE); RELOC(Hash_size) = hsize; RELOC(_SDR1) = htab + __ilog2(hsize) - 18; /* * Put in PTEs for the first 64MB of RAM */ cacheable_memzero((void *)htab, hsize); for (addr = 0; addr < 0x4000000; addr += 0x1000) make_pte(htab, hsize, addr + KERNELBASE, addr, _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX);}#endif /* CONFIG_PPC64BRIDGE */static void __initprom_instantiate_rtas(void){ ihandle prom_rtas; unsigned int i; struct prom_args prom_args; unsigned long offset = reloc_offset(); prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); if (prom_rtas == (void *) -1) return; RELOC(rtas_size) = 0; call_prom(RELOC("getprop"), 4, 1, prom_rtas, RELOC("rtas-size"), &RELOC(rtas_size), sizeof(rtas_size)); prom_print(RELOC("instantiating rtas")); if (RELOC(rtas_size) == 0) { RELOC(rtas_data) = 0; } else { /* * Ask OF for some space for RTAS. * Actually OF has bugs so we just arbitrarily * use memory at the 6MB point. */ RELOC(rtas_data) = 6 << 20; prom_print(RELOC(" at ")); prom_print_hex(RELOC(rtas_data)); } prom_rtas = call_prom(RELOC("open"), 1, 1, RELOC("/rtas")); prom_print(RELOC("...")); prom_args.service = RELOC("call-method"); prom_args.nargs = 3; prom_args.nret = 2; prom_args.args[0] = RELOC("instantiate-rtas"); prom_args.args[1] = prom_rtas; prom_args.args[2] = (void *) RELOC(rtas_data); RELOC(prom)(&prom_args); i = 0; if (prom_args.args[3] == 0) i = (unsigned int)prom_args.args[4]; RELOC(rtas_entry) = i; if ((RELOC(rtas_entry) == -1) || (RELOC(rtas_entry) == 0)) prom_print(RELOC(" failed\n")); else prom_print(RELOC(" done\n"));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -