📄 prom.c
字号:
/* * $Id: prom.c,v 1.79 1999/10/08 01:56:32 paulus Exp $ * * 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 <asm/init.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/gemini.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/bitops.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. */#define MAX_PROPERTY_LENGTH 1024struct 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, };unsigned int prom_num_displays = 0;char *of_stdout_device = 0;prom_entry prom = 0;ihandle prom_chosen = 0, prom_stdout = 0, prom_disp_node = 0;extern char *klimit;char *bootpath = 0;char *bootdevice = 0;unsigned int rtas_data = 0; /* physical pointer */unsigned int rtas_entry = 0; /* physical pointer */unsigned int rtas_size = 0;unsigned int old_rtas = 0;/* Set for a newworld machine */int use_of_interrupt_tree = 0;int pmac_newworld = 0;static struct device_node *allnodes = 0;#ifdef CONFIG_BOOTX_TEXT#define NO_SCROLLstatic void clearscreen(void);static void flushscreen(void);#ifndef NO_SCROLLstatic void scrollscreen(void);#endifstatic void prepare_disp_BAT(void);static void draw_byte(unsigned char c, long locX, long locY);static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb);static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb);static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb);/* We want those in data, not BSS */static long g_loc_X = 0;static long g_loc_Y = 0;static long g_max_loc_X = 0;static long g_max_loc_Y = 0;unsigned long disp_BAT[2] = {0, 0};#define cmapsz (16*256)static unsigned char vga_font[cmapsz];int bootx_text_mapped = 1;#endif /* CONFIG_BOOTX_TEXT */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 *);#ifdef CONFIG_BOOTX_TEXTstatic void setup_disp_fake_bi(ihandle dp);static void prom_welcome(boot_infos_t* bi, unsigned long phys);#endifextern void enter_rtas(void *);extern unsigned long reloc_offset(void);void phys_call_rtas(int, int, int, ...);extern char cmd_line[512]; /* XXX */boot_infos_t *boot_infos = 0; /* init it so it's in data segment not bss */#ifdef CONFIG_BOOTX_TEXTboot_infos_t *disp_bi = 0;boot_infos_t fake_bi = {0,};#endifunsigned long dev_tree_size;/* * 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 PTRRELOC(x) ((typeof(x))((unsigned long)(x) + offset))#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) - offset))#define RELOC(x) (*PTRRELOC(&(x)))#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)__initstatic voidprom_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 */ ;}__initvoidprom_enter(void){ struct prom_args args; unsigned long offset = reloc_offset(); args.service = RELOC("enter"); args.nargs = 0; args.nret = 0; RELOC(prom)(&args);}__initstatic void *call_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];}__initvoidprom_print(const char *msg){ const char *p, *q; unsigned long offset = reloc_offset(); if (RELOC(prom_stdout) == 0) {#ifdef CONFIG_BOOTX_TEXT if (RELOC(disp_bi) != 0) prom_drawstring(msg);#endif 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); } }}voidprom_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);}voidprom_print_nl(void){ unsigned long offset = reloc_offset(); prom_print(RELOC("\n"));}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 voidprom_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_nl(); } }}#endif /* CONFIG_SMP */voidbootx_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 RELOC(g_loc_X) = 0; RELOC(g_loc_Y) = 0; RELOC(g_max_loc_X) = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; RELOC(g_max_loc_Y) = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; RELOC(disp_bi) = PTRUNRELOC(bi); clearscreen(); /* 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)) prom_print(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n")); prom_welcome(bi, phys); 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 prepare_disp_BAT(); prom_drawstring(RELOC("booting...\n")); flushscreen(); RELOC(bootx_text_mapped) = 1;#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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -