prom.c
字号:
/* * * * Procedures for interfacing to Open Firmware. * * Paul Mackerras August 1996. * Copyright (C) 1996 Paul Mackerras. * * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. * {engebret|bergner}@us.ibm.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#undef DEBUG_PROM#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/types.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <linux/stringify.h>#include <linux/delay.h>#include <linux/initrd.h>#include <asm/prom.h>#include <asm/rtas.h>#include <asm/lmb.h>#include <asm/abs_addr.h>#include <asm/page.h>#include <asm/processor.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/smp.h>#include <asm/system.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/bitops.h>#include <asm/naca.h>#include <asm/pci.h>#include <asm/iommu.h>#include <asm/bootinfo.h>#include <asm/ppcdebug.h>#include <asm/btext.h>#include <asm/sections.h>#include <asm/machdep.h>#include "open_pic.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 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 (1UL * 1024 * 1024)/* * prom_init() is called very early on, before the kernel text * and data have been mapped to KERNELBASE. At this point the code * is running at whatever address it has been loaded at, so * references to extern and static variables must be relocated * explicitly. The procedure reloc_offset() returns the address * we're currently running at minus the address we were linked at. * (Note that strings count as static variables.) * * Because OF may have mapped I/O devices into the area starting at * KERNELBASE, particularly on CHRP machines, we can't safely call * OF once the kernel has been mapped to KERNELBASE. Therefore all * OF calls should be done within prom_init(), and prom_init() * and all routines called within it must be careful to relocate * references as necessary. * * Note that the bss is cleared *after* prom_init runs, so we have * to make sure that any static or extern variables it accesses * are put in the data segment. */#define PROM_BUG() do { \ prom_printf("kernel BUG at %s line 0x%x!\n", \ RELOC(__FILE__), __LINE__); \ __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \} while (0)#ifdef DEBUG_PROM#define prom_debug(x...) prom_printf(x)#else#define prom_debug(x...)#endifstruct pci_reg_property { struct pci_address addr; u32 size_hi; u32 size_lo;};struct isa_reg_property { u32 space; u32 address; u32 size;};struct pci_intr_map { struct pci_address addr; u32 dunno; phandle int_ctrler; u32 intr;};typedef unsigned long interpret_func(struct device_node *, unsigned long, int, int, int);#ifndef FB_MAX /* avoid pulling in all of the fb stuff */#define FB_MAX 8#endif/* prom structure */struct prom_t prom;char *prom_display_paths[FB_MAX] __initdata = { NULL, };phandle prom_display_nodes[FB_MAX] __initdata;unsigned int prom_num_displays = 0;char *of_stdout_device = NULL;static int iommu_force_on;int ppc64_iommu_off;extern struct rtas_t rtas;extern unsigned long klimit;extern struct lmb lmb;#define MAX_PHB (32 * 6) /* 32 drawers * 6 PHBs/drawer */struct of_tce_table of_tce_table[MAX_PHB + 1];char *bootpath = NULL;char *bootdevice = NULL;int boot_cpuid = 0;#define MAX_CPU_THREADS 2struct device_node *allnodes = NULL;/* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. */static rwlock_t devtree_lock = RW_LOCK_UNLOCKED;extern unsigned long reloc_offset(void);extern void enter_prom(struct prom_args *args);extern void copy_and_flush(unsigned long dest, unsigned long src, unsigned long size, unsigned long offset);unsigned long dev_tree_size;unsigned long _get_PIR(void);#ifdef CONFIG_HMTstruct { unsigned int pir; unsigned int threadid;} hmt_thread_data[NR_CPUS];#endif /* CONFIG_HMT */char testString[] = "LINUX\n"; /* * This are used in calls to call_prom. The 4th and following * arguments to call_prom should be 32-bit values. 64 bit values * are truncated to 32 bits (and fortunately don't get interpreted * as two arguments). */#define ADDR(x) (u32) ((unsigned long)(x) - offset)/* This is the one and *ONLY* place where we actually call open * firmware from, since we need to make sure we're running in 32b * mode when we do. We switch back to 64b mode upon return. */#define PROM_ERROR (-1)static int __init call_prom(const char *service, int nargs, int nret, ...){ int i; unsigned long offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); va_list list; _prom->args.service = ADDR(service); _prom->args.nargs = nargs; _prom->args.nret = nret; _prom->args.rets = (prom_arg_t *)&(_prom->args.args[nargs]); va_start(list, nret); for (i=0; i < nargs; i++) _prom->args.args[i] = va_arg(list, prom_arg_t); va_end(list); for (i=0; i < nret ;i++) _prom->args.rets[i] = 0; enter_prom(&_prom->args); return (nret > 0)? _prom->args.rets[0]: 0;}static void __init prom_print(const char *msg){ const char *p, *q; unsigned long offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); 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) break; ++q; call_prom("write", 3, 1, _prom->stdout, ADDR("\r\n"), 2); }}static void __init prom_print_hex(unsigned long val){ unsigned long offset = reloc_offset(); int i, nibbles = sizeof(val)*2; char buf[sizeof(val)*2+1]; struct prom_t *_prom = PTRRELOC(&prom); for (i = nibbles-1; i >= 0; i--) { buf[i] = (val & 0xf) + '0'; if (buf[i] > '9') buf[i] += ('a'-'0'-10); val >>= 4; } buf[nibbles] = '\0'; call_prom("write", 3, 1, _prom->stdout, buf, nibbles);}static void __init prom_printf(const char *format, ...){ unsigned long offset = reloc_offset(); const char *p, *q, *s; va_list args; unsigned long v; struct prom_t *_prom = PTRRELOC(&prom); va_start(args, format); for (p = PTRRELOC(format); *p != 0; p = q) { for (q = p; *q != 0 && *q != '\n' && *q != '%'; ++q) ; if (q > p) call_prom("write", 3, 1, _prom->stdout, p, q - p); if (*q == 0) break; if (*q == '\n') { ++q; call_prom("write", 3, 1, _prom->stdout, ADDR("\r\n"), 2); continue; } ++q; if (*q == 0) break; switch (*q) { case 's': ++q; s = va_arg(args, const char *); prom_print(s); break; case 'x': ++q; v = va_arg(args, unsigned long); prom_print_hex(v); break; } }}static void __init __attribute__((noreturn)) prom_panic(const char *reason){ unsigned long offset = reloc_offset(); prom_print(PTRRELOC(reason)); /* ToDo: should put up an SRC here */ call_prom("exit", 0, 0); for (;;) /* should never get here */ ;}static int __init prom_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; }}static int __init prom_getprop(phandle node, const char *pname, void *value, size_t valuelen){ unsigned long offset = reloc_offset(); return call_prom("getprop", 4, 1, node, ADDR(pname), (u32)(unsigned long) value, (u32) valuelen);}static void __init prom_initialize_naca(void){ phandle node; char type[64]; unsigned long num_cpus = 0; unsigned long offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); struct naca_struct *_naca = RELOC(naca); struct systemcfg *_systemcfg = RELOC(systemcfg); /* NOTE: _naca->debug_switch is already initialized. */ prom_debug("prom_initialize_naca: start...\n"); _naca->pftSize = 0; /* ilog2 of htab size. computed below. */ for (node = 0; prom_next_node(&node); ) { type[0] = 0; prom_getprop(node, "device_type", type, sizeof(type)); if (!strcmp(type, RELOC("cpu"))) { num_cpus += 1; /* We're assuming *all* of the CPUs have the same * d-cache and i-cache sizes... -Peter */ if ( num_cpus == 1 ) { u32 size, lsize; const char *dc, *ic; if (_systemcfg->platform == PLATFORM_POWERMAC){ dc = "d-cache-block-size"; ic = "i-cache-block-size"; } else { dc = "d-cache-line-size"; ic = "i-cache-line-size"; } prom_getprop(node, "d-cache-size", &size, sizeof(size)); prom_getprop(node, dc, &lsize, sizeof(lsize)); _systemcfg->dCacheL1Size = size; _systemcfg->dCacheL1LineSize = lsize; _naca->dCacheL1LogLineSize = __ilog2(lsize); _naca->dCacheL1LinesPerPage = PAGE_SIZE/lsize; prom_getprop(node, "i-cache-size", &size, sizeof(size)); prom_getprop(node, ic, &lsize, sizeof(lsize)); _systemcfg->iCacheL1Size = size; _systemcfg->iCacheL1LineSize = lsize; _naca->iCacheL1LogLineSize = __ilog2(lsize); _naca->iCacheL1LinesPerPage = PAGE_SIZE/lsize; if (_systemcfg->platform == PLATFORM_PSERIES_LPAR) { u32 pft_size[2]; prom_getprop(node, "ibm,pft-size", &pft_size, sizeof(pft_size)); /* pft_size[0] is the NUMA CEC cookie */ _naca->pftSize = pft_size[1]; } } } else if (!strcmp(type, RELOC("serial"))) { phandle isa, pci; struct isa_reg_property reg; union pci_range ranges; if (_systemcfg->platform == PLATFORM_POWERMAC) continue; type[0] = 0; prom_getprop(node, "ibm,aix-loc", type, sizeof(type)); if (strcmp(type, RELOC("S1"))) continue; prom_getprop(node, "reg", ®, sizeof(reg)); isa = call_prom("parent", 1, 1, node); if (!isa) PROM_BUG(); pci = call_prom("parent", 1, 1, isa); if (!pci) PROM_BUG(); prom_getprop(pci, "ranges", &ranges, sizeof(ranges)); if ( _prom->encode_phys_size == 32 ) _naca->serialPortAddr = ranges.pci32.phys+reg.address; else { _naca->serialPortAddr = ((((unsigned long)ranges.pci64.phys_hi) << 32) | (ranges.pci64.phys_lo)) + reg.address; } } } if (_systemcfg->platform == PLATFORM_POWERMAC) _naca->interrupt_controller = IC_OPEN_PIC; else { _naca->interrupt_controller = IC_INVALID; for (node = 0; prom_next_node(&node); ) { type[0] = 0; prom_getprop(node, "name", type, sizeof(type)); if (strcmp(type, RELOC("interrupt-controller"))) continue; prom_getprop(node, "compatible", type, sizeof(type)); if (strstr(type, RELOC("open-pic"))) _naca->interrupt_controller = IC_OPEN_PIC; else if (strstr(type, RELOC("ppc-xicp"))) _naca->interrupt_controller = IC_PPC_XIC; else prom_printf("prom: failed to recognize" " interrupt-controller\n"); break; } } if (_naca->interrupt_controller == IC_INVALID) { prom_printf("prom: failed to find interrupt-controller\n"); PROM_BUG(); } /* We gotta have at least 1 cpu... */ if ( (_systemcfg->processorCount = num_cpus) < 1 ) PROM_BUG(); _systemcfg->physicalMemorySize = lmb_phys_mem_size(); if (_systemcfg->platform == PLATFORM_PSERIES || _systemcfg->platform == PLATFORM_POWERMAC) { unsigned long rnd_mem_size, pteg_count; /* round mem_size up to next power of 2 */ rnd_mem_size = 1UL << __ilog2(_systemcfg->physicalMemorySize); if (rnd_mem_size < _systemcfg->physicalMemorySize) rnd_mem_size <<= 1; /* # pages / 2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -