📄 prom_init.c
字号:
/* * Procedures for interfacing to Open Firmware. * * Paul Mackerras August 1996. * Copyright (C) 1996-2005 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/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 <linux/bitops.h>#include <asm/prom.h>#include <asm/rtas.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/pci.h>#include <asm/iommu.h>#include <asm/btext.h>#include <asm/sections.h>#include <asm/machdep.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)/* * Eventually bump that one up */#define DEVTREE_CHUNK_SIZE 0x100000/* * This is the size of the local memory reserve map that gets copied * into the boot params passed to the kernel. That size is totally * flexible as the kernel just reads the list until it encounters an * entry with size 0, so it can be changed without breaking binary * compatibility */#define MEM_RESERVE_MAP_SIZE 8/* * 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. * On ppc32 we compile with -mrelocatable, which means that references * to extern and static variables get relocated automatically. * On ppc64 we have to relocate the references explicitly with * RELOC. (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 must be done within prom_init(). * * ADDR is used in calls to call_prom. The 4th and following * arguments to call_prom should be 32-bit values. * On ppc64, 64 bit values are truncated to 32 bits (and * fortunately don't get interpreted as two arguments). */#ifdef CONFIG_PPC64#define RELOC(x) (*PTRRELOC(&(x)))#define ADDR(x) (u32) add_reloc_offset((unsigned long)(x))#define OF_WORKAROUNDS 0#else#define RELOC(x) (x)#define ADDR(x) (u32) (x)#define OF_WORKAROUNDS of_workaroundsint of_workarounds;#endif#define OF_WA_CLAIM 1 /* do phys/virt claim separately, then map */#define OF_WA_LONGTRAIL 2 /* work around longtrail bugs */#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...)#endiftypedef u32 prom_arg_t;struct prom_args { u32 service; u32 nargs; u32 nret; prom_arg_t args[10];};struct prom_t { ihandle root; phandle chosen; int cpu; ihandle stdout; ihandle mmumap; ihandle memory;};struct mem_map_entry { unsigned long base; unsigned long size;};typedef u32 cell_t;extern void __start(unsigned long r3, unsigned long r4, unsigned long r5);#ifdef CONFIG_PPC64extern int enter_prom(struct prom_args *args, unsigned long entry);#elsestatic inline int enter_prom(struct prom_args *args, unsigned long entry){ return ((int (*)(struct prom_args *))entry)(args);}#endifextern void copy_and_flush(unsigned long dest, unsigned long src, unsigned long size, unsigned long offset);/* prom structure */static struct prom_t __initdata prom;static unsigned long prom_entry __initdata;#define PROM_SCRATCH_SIZE 256static char __initdata of_stdout_device[256];static char __initdata prom_scratch[PROM_SCRATCH_SIZE];static unsigned long __initdata dt_header_start;static unsigned long __initdata dt_struct_start, dt_struct_end;static unsigned long __initdata dt_string_start, dt_string_end;static unsigned long __initdata prom_initrd_start, prom_initrd_end;#ifdef CONFIG_PPC64static int __initdata iommu_force_on;static int __initdata ppc64_iommu_off;static unsigned long __initdata prom_tce_alloc_start;static unsigned long __initdata prom_tce_alloc_end;#endifstatic int __initdata of_platform;static char __initdata prom_cmd_line[COMMAND_LINE_SIZE];static unsigned long __initdata prom_memory_limit;static unsigned long __initdata alloc_top;static unsigned long __initdata alloc_top_high;static unsigned long __initdata alloc_bottom;static unsigned long __initdata rmo_top;static unsigned long __initdata ram_top;static struct mem_map_entry __initdata mem_reserve_map[MEM_RESERVE_MAP_SIZE];static int __initdata mem_reserve_cnt;static cell_t __initdata regbuf[1024];#define MAX_CPU_THREADS 2/* TO GO */#ifdef CONFIG_HMTstruct { unsigned int pir; unsigned int threadid;} hmt_thread_data[NR_CPUS];#endif /* CONFIG_HMT *//* * Error results ... some OF calls will return "-1" on error, some * will return 0, some will return either. To simplify, here are * macros to use with any ihandle or phandle return value to check if * it is valid */#define PROM_ERROR (-1u)#define PHANDLE_VALID(p) ((p) != 0 && (p) != PROM_ERROR)#define IHANDLE_VALID(i) ((i) != 0 && (i) != PROM_ERROR)/* This is the one and *ONLY* place where we actually call open * firmware. */static int __init call_prom(const char *service, int nargs, int nret, ...){ int i; struct prom_args args; va_list list; args.service = ADDR(service); args.nargs = nargs; args.nret = nret; va_start(list, nret); for (i = 0; i < nargs; i++) args.args[i] = va_arg(list, prom_arg_t); va_end(list); for (i = 0; i < nret; i++) args.args[nargs+i] = 0; if (enter_prom(&args, RELOC(prom_entry)) < 0) return PROM_ERROR; return (nret > 0) ? args.args[nargs] : 0;}static int __init call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...){ int i; struct prom_args args; va_list list; args.service = ADDR(service); args.nargs = nargs; args.nret = nret; va_start(list, rets); for (i = 0; i < nargs; i++) args.args[i] = va_arg(list, prom_arg_t); va_end(list); for (i = 0; i < nret; i++) args.args[nargs+i] = 0; if (enter_prom(&args, RELOC(prom_entry)) < 0) return PROM_ERROR; if (rets != NULL) for (i = 1; i < nret; ++i) rets[i-1] = args.args[nargs+i]; return (nret > 0) ? args.args[nargs] : 0;}static void __init prom_print(const char *msg){ const char *p, *q; struct prom_t *_prom = &RELOC(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){ int i, nibbles = sizeof(val)*2; char buf[sizeof(val)*2+1]; struct prom_t *_prom = &RELOC(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, ...){ const char *p, *q, *s; va_list args; unsigned long v; struct prom_t *_prom = &RELOC(prom); va_start(args, format);#ifdef CONFIG_PPC64 format = PTRRELOC(format);#endif for (p = 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 unsigned int __init prom_claim(unsigned long virt, unsigned long size, unsigned long align){ struct prom_t *_prom = &RELOC(prom); if (align == 0 && (OF_WORKAROUNDS & OF_WA_CLAIM)) { /* * Old OF requires we claim physical and virtual separately * and then map explicitly (assuming virtual mode) */ int ret; prom_arg_t result; ret = call_prom_ret("call-method", 5, 2, &result, ADDR("claim"), _prom->memory, align, size, virt); if (ret != 0 || result == -1) return -1; ret = call_prom_ret("call-method", 5, 2, &result, ADDR("claim"), _prom->mmumap, align, size, virt); if (ret != 0) { call_prom("call-method", 4, 1, ADDR("release"), _prom->memory, size, virt); return -1; } /* the 0x12 is M (coherence) + PP == read/write */ call_prom("call-method", 6, 1, ADDR("map"), _prom->mmumap, 0x12, size, virt, virt); return virt; } return call_prom("claim", 3, 1, (prom_arg_t)virt, (prom_arg_t)size, (prom_arg_t)align);}static void __init __attribute__((noreturn)) prom_panic(const char *reason){#ifdef CONFIG_PPC64 reason = PTRRELOC(reason);#endif prom_print(reason); /* ToDo: should put up an SRC here on p/iSeries */ 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 inline prom_getprop(phandle node, const char *pname, void *value, size_t valuelen){ return call_prom("getprop", 4, 1, node, ADDR(pname), (u32)(unsigned long) value, (u32) valuelen);}static int inline prom_getproplen(phandle node, const char *pname){ return call_prom("getproplen", 2, 1, node, ADDR(pname));}static void add_string(char **str, const char *q){ char *p = *str; while (*q) *p++ = *q++; *p++ = ' '; *str = p;}static char *tohex(unsigned int x){ static char digits[] = "0123456789abcdef"; static char result[9]; int i; result[8] = 0; i = 8; do { --i; result[i] = digits[x & 0xf]; x >>= 4; } while (x != 0 && i > 0); return &result[i];}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;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -