diff options
Diffstat (limited to 'arch/sparc/kernel/head.S')
-rw-r--r-- | arch/sparc/kernel/head.S | 1045 |
1 files changed, 1045 insertions, 0 deletions
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S new file mode 100644 index 000000000..c3a5453e7 --- /dev/null +++ b/arch/sparc/kernel/head.S @@ -0,0 +1,1045 @@ +/* boot.S: The initial boot code for the Sparc port of Linux. + + Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) + + This file has to serve three purposes. + + 1) determine the prom-version and cpu/architecture + 2) print enough useful info before we start to execute + c-code that I can possibly begin to debug things + 3) Hold the vector of trap entry points + + The Sparc offers many challenges to kernel design. Here I will + document those I have come across thus far. Upon bootup the boot + prom loads your a.out image into memory. This memory the prom has + already mapped for you in two places, however as far as I can tell + the virtual address cache is not turned on although the MMU is + translating things. You get loaded at 0x4000 exactly and you are + aliased to 0xf8004000 with the appropriate mmu entries. So, when + you link a boot-loadable object you want to do something like: + + ld -e start -Ttext 4000 -o mykernel myobj1.o myobj2.o .... + + to produce a proper image. + + At boot time you are given (as far as I can tell at this time) + one key to figure out what machine you are one and what devices + are available. The prom when it loads you leaves a pointer to + the 'rom vector' in register %o0 right before it jumps to your + starting address. This is a pointer to a struct that is full of + pointer to functions (ie. printf, halt, reboot), pointers to + linked lists (ie. memory mappings), and pointer to empirical + constants (ie. stdin and stdout magic cookies + rom version). + Starting with this piece of information you can figure out + just about anything you want about the machine you are on. + + Although I don't use it now, if you are on a Multiprocessor and + therefore a v3 or above prom, register %o2 at boot contains a + function pointer you must call before you proceed to invoke the + other cpu's on the machine. I have no idea what kind of magic this + is, give me time. +*/ + +#include <asm/cprefix.h> +#include <asm/head.h> +#include <asm/version.h> +#include <asm/asi.h> +#include <asm/contregs.h> +#include <asm/psr.h> +#include <asm/page.h> + + .data + +/* First thing to go in the data segment is the interrupt stack. */ + + .globl C_LABEL(intstack) + .globl C_LABEL(eintstack) +C_LABEL(intstack): + .skip 4 * PAGE_SIZE ! 16k = 128 128-byte stack frames +C_LABEL(eintstack): + + + +/* + The following are used with the prom_vector node-ops to figure out + the cpu-type +*/ + + .globl C_LABEL(cputyp) + +C_LABEL(cputyp): + .word 1 + +C_LABEL(cputypval): + .asciz "sun4c" + .ascii " " + + .align 4 +/* + * Sun people can't spell worth damn. "compatability" indeed. + * At least we *know* we can't spell, and use a spell-checker. + */ + +/* Uh, actually Linus it is I who cannot spell. Too much murky + * Sparc assembly will do this to ya. + */ +C_LABEL(cputypvar): + .asciz "compatability" + +C_LABEL(cputypvallen) = C_LABEL(cputypvar) - C_LABEL(cputypval) + +/* This hold the prom-interface-version number for either v0 or v2. */ + + .align 4 + .globl C_LABEL(prom_iface_vers) + +C_LABEL(prom_iface_vers): .skip 4 + +/* WARNING: evil messages follow */ + + .align 4 + +sun4_notsup: + .asciz "Sparc-Linux: sun4 support not implemented yet\n\n" + .align 4 + +sun4m_notsup: + .asciz "Sparc-Linux: sun4m support does not exist\n\n" + .align 4 + +sun4d_notsup: + .asciz "Sparc-Linux: sun4d support does not exist\n\n" + .align 4 + +you_lose: + .asciz "You lose..... Thanks for playing...\n" + .align 4 + + + .globl boot_msg + +/* memory descriptor property strings, v2 = yuk yuk yuk */ +/* XXX how to figure out vm mapped by prom? May have to scan magic addresses */ + +mem_prop_physavail: .asciz "available" + + .align 4 +mem_prop_phystot: .asciz "reg" + +/* v2_memory descriptor struct kludged here for assembly, if it ain't broke */ + + .align 4 +v2_mem_struct: .skip 0xff + + .align 4 +v2_printf_physavail: .asciz "Physical Memory Available: 0x%x bytes" + + .align 4 +v2_printf_phystot: .asciz "Physical Memory: 0x%x bytes" + +/* A place to store property strings returned from the prom 'node' funcs */ + + .align 4 +prop_string_buf: .skip 32 + + .align 4 +prop_name: .asciz "name" + + .align 4 +current_node: .skip 4 + + +/* nice little boot message */ + + .align 4 +boot_msg: + .ascii "Booting Sparc-Linux V0.00PRE-ALPHA " + .ascii WHO_COMPILED_ME + .ascii "\r\n" + .align 4 + + .globl boot_msg2 + +boot_msg2: + .asciz "Booting Sparclinux V0.00 PRE-ALPHA on a (SUN4C)\r\n\n" + + .align 4 + +pstring1: + .asciz "Prom Magic Cookie: 0x%x \n" + .align 4 + +pstring2: + .asciz "Interface Version: v%d\n" + .align 4 + +pstring3: + .asciz "Prom Revision: V%d\n\n" + .align 4 + +pstring4: + .ascii "Total Physical Memory: %d bytes\nVM mapped by Prom: %d bytes\n" + .asciz "Available Physical Memory: %d bytes\n" + .align 4 + + + .text + + .globl C_LABEL(msgbuf) +msgbufsize = PAGE_SIZE ! 1 page for msg buffer +C_LABEL(msgbuf) = PAGE_SIZE + + +IE_reg_addr = C_LABEL(msgbuf) + msgbufsize ! this page not used; points to IEreg + + +/* Ok, things start to get interesting. We get linked such that 'start' + is the entry symbol. However, it is real low in kernel address space + and as such a nifty place to place the trap table. We achieve this goal + by just jumping to 'gokernel' for the first trap's entry as the sparc + never receives the zero trap as it is real special (hw reset). + + Each trap entry point is the size of 4 sparc instructions (or 4 bytes + * 4 insns = 16 bytes). There are 128 hardware traps (some undefined + or unimplemented) and 128 software traps (sys-calls, etc.). + + One of the instructions must be a branch. More often than not this + will be to a trap handler entry point because it is completely + impossible to handle any trap in 4 insns. I welcome anyone to + challenge this theory. :-) + + On entry into this table the hardware has loaded the program counter + at which the trap occurred into register %l1 and the next program + counter into %l2, this way we can return from the trap with a simple + + jmp %l1; rett %l2 ! poof... + + after properly servicing the trap. It wouldn't be a bad idea to load + some more information into the local regs since we have technically + 2 or 3 instructions to play with besides the jmp to the 'real' trap + handler (one can even go in the delay slot). For now I am going to put + the %psr (processor status register) and the trap-type value in %l0 + and %l3 respectively. Also, for IRQ's I'll put the level in %l4. + +*/ + + .globl start + .globl _start /* warning, solaris hack */ + .globl C_LABEL(trapbase) +_start: /* danger danger */ +start: +C_LABEL(trapbase): + b gokernel; nop; nop; nop; ! we never get trap #0 it is special + + TRAP_ENTRY(0x1, my_trap_handler) /* Instruction Access Exception */ + TRAP_ENTRY(0x2, my_trap_handler) /* Illegal Instruction */ + TRAP_ENTRY(0x3, my_trap_handler) /* Privileged Instruction */ + TRAP_ENTRY(0x4, my_trap_handler) /* Floating Point Disabled */ + TRAP_ENTRY(0x5, spill_window_entry) /* Window Overflow */ + TRAP_ENTRY(0x6, fill_window_entry) /* Window Underflow */ + TRAP_ENTRY(0x7, my_trap_handler) /* Memory Address Not Aligned */ + TRAP_ENTRY(0x8, my_trap_handler) /* Floating Point Exception */ + TRAP_ENTRY(0x9, my_trap_handler) /* Data Miss Exception */ + TRAP_ENTRY(0xa, my_trap_handler) /* Tagged Instruction Overflow */ + TRAP_ENTRY(0xb, my_trap_handler) /* Watchpoint Detected */ + TRAP_ENTRY(0xc, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xd, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xe, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0xf, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x10, my_trap_handler) /* Undefined... */ + +/* Level'd interrupt entry points, see macro defs above */ + + TRAP_ENTRY_INTERRUPT_SOFT(1, 0x101) /* IRQ Software/SBUS Level 1 */ + TRAP_ENTRY_INTERRUPT(2) /* IRQ SBUS Level 2 */ + TRAP_ENTRY_INTERRUPT(3) /* IRQ SCSI/DMA/SBUS Level 3 */ + TRAP_ENTRY_INTERRUPT_SOFT(4, 0x104) /* IRQ Software Level 4 */ + TRAP_ENTRY_INTERRUPT(5) /* IRQ SBUS/Ethernet Level 5 */ + TRAP_ENTRY_INTERRUPT_SOFT(6, 0x106) /* IRQ Software Level 6 */ + TRAP_ENTRY_INTERRUPT(7) /* IRQ Video/SBUS Level 5 */ + TRAP_ENTRY_INTERRUPT(8) /* IRQ SBUS Level 6 */ + TRAP_ENTRY_INTERRUPT(9) /* IRQ SBUS Level 7 */ + TRAP_ENTRY_INTERRUPT(10) /* IRQ Timer #1 */ + TRAP_ENTRY_INTERRUPT(11) /* IRQ Floppy Intr. */ + TRAP_ENTRY_INTERRUPT(12) /* IRQ Zilog serial chip */ + TRAP_ENTRY_INTERRUPT(13) /* IRQ Audio Intr. */ + TRAP_ENTRY_TIMER /* IRQ Timer #2 (one we use) */ + TRAP_ENTRY_INTERRUPT_NMI(15, linux_trap_nmi) /* Level 15 (nmi) */ + + TRAP_ENTRY(0x20, my_trap_handler) /* General Register Access Error */ + TRAP_ENTRY(0x21, my_trap_handler) /* Instruction Access Error */ + TRAP_ENTRY(0x22, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x23, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x24, my_trap_handler) /* Co-Processor Disabled */ + TRAP_ENTRY(0x25, my_trap_handler) /* Unimplemented FLUSH inst. */ + TRAP_ENTRY(0x26, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x27, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x28, my_trap_handler) /* Co-Processor Exception */ + TRAP_ENTRY(0x29, my_trap_handler) /* Data Access Error */ + TRAP_ENTRY(0x2a, my_trap_handler) /* Division by zero, you lose... */ + TRAP_ENTRY(0x2b, my_trap_handler) /* Data Store Error */ + TRAP_ENTRY(0x2c, my_trap_handler) /* Data Access MMU-Miss */ + TRAP_ENTRY(0x2d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x2e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x2f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x30, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x31, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x32, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x33, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x34, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x35, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x36, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x37, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x38, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x39, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3c, my_trap_handler) /* Instruction Access MMU-Miss */ + TRAP_ENTRY(0x3d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x3f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x40, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x41, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x42, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x43, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x44, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x45, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x46, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x47, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x48, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x49, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4c, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x4f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x50, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x51, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x52, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x53, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x54, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x55, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x56, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x57, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x58, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x59, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5a, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5b, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5c, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5d, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5e, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x5f, my_trap_handler) /* Undefined... */ + TRAP_ENTRY(0x60, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x61, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x62, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x63, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x64, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x65, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x66, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x67, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x68, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x69, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6a, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6b, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6c, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6d, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6e, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x6f, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x70, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x71, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x72, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x73, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x74, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x75, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x76, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x77, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x78, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x79, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7a, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7b, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7c, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7d, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7e, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x7f, my_trap_handler) /* Impl-Dep Exception */ + TRAP_ENTRY(0x80, my_trap_handler) /* SunOS System Call */ + TRAP_ENTRY(0x81, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x82, my_trap_handler) /* Divide by zero trap XXX */ + TRAP_ENTRY(0x83, my_trap_handler) /* Flush Windows Trap XXX */ + TRAP_ENTRY(0x84, my_trap_handler) /* Clean Windows Trap XXX */ + TRAP_ENTRY(0x85, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x86, my_trap_handler) /* Fix Unaligned Access Trap XXX */ + TRAP_ENTRY(0x87, my_trap_handler) /* Integer Overflow Trap XXX */ + TRAP_ENTRY(0x88, my_trap_handler) /* Slowaris System Call */ + TRAP_ENTRY(0x89, my_trap_handler) /* NetBSD System Call */ + TRAP_ENTRY(0x8a, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8b, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8c, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8d, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8e, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x8f, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x90, my_trap_handler) /* SparcLinux System Call */ + TRAP_ENTRY(0x91, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x92, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x93, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x94, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x95, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x96, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x97, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x98, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x99, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9a, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9b, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9c, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9d, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9e, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0x9f, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xa9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xaa, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xab, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xac, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xad, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xae, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xaf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xb9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xba, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbe, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xbf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xc9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xca, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xce, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xcf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xd9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xda, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xde, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xdf, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xe9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xea, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xeb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xec, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xed, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xee, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xef, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf0, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf1, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf2, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf3, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf4, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf5, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf6, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf7, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf8, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xf9, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfa, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfb, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfc, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfd, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xfe, my_trap_handler) /* Software Trap */ + TRAP_ENTRY(0xff, my_trap_handler) /* Software Trap */ + + .skip 4096 + +C_LABEL(msgbufmapped): + .word 1 + + + +/* Cool, here we go. Pick up the romvec pointer in %o0 and stash it in + %g7 and at _prom_vector_p. And also quickly check whether we are on + a v0 or v2 prom. +*/ + +gokernel: or %g0, %o0, %g7 + sethi %hi( C_LABEL(prom_vector_p) ), %g1 + st %o0, [%g1 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later + rd %psr, %l2 + rd %wim, %l3 + rd %tbr, %l4 + or %g0, %o2, %l5 ! could be prom magic value... + +#if 0 /* You think I'm nutz? */ + subcc %l5, 0x0, %g0 ! check for magic SMP pointer + bne nosmp + nop + call %o2 ! call smp prom setup + nop +#endif /* I will be soon... */ + +/* Acquire boot time privileged register values, this will help debugging. + * I figure out and store nwindows later on. + */ + +nosmp: sethi %hi( C_LABEL(boot_psr) ), %l1 + st %l2, [%l1 + %lo( C_LABEL(boot_psr) )] + sethi %hi( C_LABEL(boot_wim) ), %l1 + st %l3, [%l1 + %lo( C_LABEL(boot_wim) )] + sethi %hi( C_LABEL(boot_tbr) ), %l1 + st %l4, [%l1 + %lo( C_LABEL(boot_tbr) )] + sethi %hi( C_LABEL(boot_smp_ptr) ), %l1 + st %l5, [%l1 + %lo( C_LABEL(boot_smp_ptr) )] + + or %g0, %o0, %g7 + sethi %hi( C_LABEL(prom_vector_p) ), %g5 + st %o0, [%g5 + %lo( C_LABEL(prom_vector_p) )] ! we will need it later + + ld [%g7 + 0x4], %o3 + subcc %o3, 0x2, %g0 ! a v2 prom? + be found_v2 + nop + + /* paul@sfe.com.au */ + subcc %o3, 0x3, %g0 ! a v3 prom? + or %g0, 0x3, %o5 + sethi %hi(C_LABEL(prom_iface_vers) ), %g1 + st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )] + be not_v2 + nop + + +/* Old sun4's pass our load address into %o0 instead of the prom + pointer. On sun4's you have to hard code the romvec pointer into + your code. Sun probably still does that because they don't even + trust their own "OpenBoot" specifications. +*/ + + sethi %hi(LOAD_ADDR), %g6 + subcc %o0, %g6, %g0 ! an old sun4? + be no_sun4_here + nop + + sethi %hi( C_LABEL(prom_iface_vers) ), %g1 + st %g0, [%g1 + %lo( C_LABEL(prom_iface_vers) )] + b not_v2 + nop + +found_v2: + or %g0, 0x2, %o5 + sethi %hi( C_LABEL(prom_iface_vers) ), %g1 + st %o5, [%g1 + %lo( C_LABEL(prom_iface_vers) )] + +not_v2: + +/* Get the machine type via the mysterious romvec node operations. + * Here we can find out whether we are on a sun4 sun4c, sun4m, or + * a sun4m. The "nodes" are set up as a bunch of n-ary trees which + * you can traverse to get information about devices and such. The + * information acquisition happens via the node-ops which are defined + * in the linux_openprom.h header file. Of particular interest is the + * 'nextnode(int node)' function as it does the smart thing when + * presented with a value of '0', it gives you the first node in the + * tree. These node integers probably offset into some internal prom + * pointer table the openboot has. It's completely undocumented, so + * I'm not about to go sifting through the prom address space, but may + * do so if I get suspicious enough. :-) + */ + + or %g0, %g7, %l1 + add %l1, 0x1c, %l1 + ld [%l1], %l0 + ld [%l0], %l0 + call %l0 + or %g0, %g0, %o0 ! next_node(0) = first_node + + sethi %hi( C_LABEL(cputypvar) ), %o1 ! first node has cpu-arch + or %o1, %lo( C_LABEL(cputypvar) ), %o1 + sethi %hi( C_LABEL(cputypval) ), %o2 ! information, the string + or %o2, %lo( C_LABEL(cputypval) ), %o2 + ld [%l1], %l0 ! 'compatibility' tells + ld [%l0 + 0xc], %l0 ! that we want 'sun4x' where + call %l0 ! x is one of '', 'c', 'm', + nop ! 'd' or 'e'. %o2 holds pointer + ! to a buf where above string + ! will get stored by the prom. + + sethi %hi( C_LABEL(cputypval) ), %o2 ! better safe than sorry + or %o2, %lo( C_LABEL(cputypval) ), %o2 + ldub [%o2 + 0x4], %o0 + subcc %o0, 'c', %g0 ! we already know we are not + be is_sun4c ! on a plain sun4 because of + nop ! the check for 0x4000 in %o0 + subcc %o0, 'm', %g0 ! at start: + be is_sun4m + nop + b no_sun4d_here ! god bless the person who + nop ! tried to run this on sun4d + +is_sun4m: +is_sun4c: ! OK, this is a sun4c, yippie + or %g0, %g7, %g6 ! load up the promvec offsets + sethi %hi(prom_magic), %g5 ! magic mushroom :> + st %g6, [%g5 + %lo(prom_magic)] + add %g7, 0x4, %g6 + sethi %hi(prom_rom_vers), %g5 + st %g6, [%g5 + %lo(prom_rom_vers)] + add %g7, 0x8, %g6 + sethi %hi(prom_pluginvers), %g5 + st %g6, [%g5 + %lo(prom_pluginvers)] + add %g7, 0xc, %g6 + sethi %hi(prom_revision), %g5 + st %g6, [%g5 + %lo(prom_revision)] + add %g7, 0x10, %g6 + sethi %hi(prom_v0mem_desc), %g5 + st %g6, [%g5 + %lo(prom_v0mem_desc)] + add %g7, 0x1c, %g6 + sethi %hi(prom_nodefuncs), %g5 + st %g6, [%g5 + %lo(prom_nodefuncs)] + add %g7, 0x68, %g6 + sethi %hi(prom_printf), %g5 + st %g6, [%g5 + %lo(prom_printf)] + add %g7, 0x6c, %g6 + sethi %hi(prom_abort), %g5 + st %g6, [%g5 + %lo(prom_abort)] + add %g7, 0x74, %g6 + sethi %hi(prom_halt), %g5 + st %g6, [%g5 + %lo(prom_halt)] + add %g7, 0x78, %g6 + sethi %hi(prom_sync), %g5 + st %g6, [%g5 + %lo(prom_sync)] + add %g7, 0x7c, %g6 + sethi %hi(prom_eval), %g5 + st %g6, [%g5 + %lo(prom_eval)] + add %g7, 0x80, %g6 + sethi %hi(prom_v0bootline), %g6 + st %g6, [%g5 + %lo(prom_v0bootline)] + + +/* That was easy, now lets try to print some message on the screen. + * We don't have to worry about bad address translations when the prom + * addresses our pointers because our pointers are at 0x0-kern_size + * as the prom expects. + */ + +/* paul@sfe.com.au */ +/* V3 doesn't have printf.. And I don't really feel like doing the formatting + * myself.. So we miss out on some messages (for now). + */ + ld [%g7 + 0x4], %o0 + subcc %o3, 0x3, %g0 + be v3_bootmsg + nop + + sethi %hi(boot_msg), %o0 + or %o0, %lo(boot_msg), %o0 + sethi %hi(prom_printf), %o1 + ld [%o1 + %lo(prom_printf)], %o1 + ld [%o1], %o1 + call %o1 ! print boot message #1 + nop + + sethi %hi(pstring1), %o0 + or %o0, %lo(pstring1), %o0 + sethi %hi(prom_printf), %o2 + ld [%o2 + %lo(prom_printf)], %o2 + ld [%o2], %o2 + sethi %hi(prom_magic), %o1 + ld [%o1 + %lo(prom_magic)], %o1 + ld [%o1], %o1 + call %o2 + nop + + sethi %hi(pstring2), %o0 + or %o0, %lo(pstring2), %o0 + sethi %hi(prom_printf), %o2 + ld [%o2 + %lo(prom_printf)], %o2 + ld [%o2], %o2 + sethi %hi( C_LABEL(prom_iface_vers) ), %o1 + ld [%o1 + %lo( C_LABEL(prom_iface_vers) )], %o1 + ld [%o1], %o1 + call %o2 + nop + + b rest_of_boot + nop + +v3_bootmsg: + ld [%g7 + 0x94], %o0 + ld [%o0], %o0 + sethi %hi(boot_msg), %o1 + or %o1, %lo(boot_msg), %o1 + mov BOOT_MSG_LEN, %o2 + ld [%g7 + 0xb8], %o4 + call %o4 + nop + + ld [%g7 + 0x94], %o0 + ld [%o0], %o0 + sethi %hi(boot_msg2), %o1 + or %o1, %lo(boot_msg2), %o1 + mov BOOT_MSG2_LEN, %o2 + ld [%g7 + 0xb8], %o4 + call %o4 + nop + b rest_of_boot + nop + + +no_sun4_here: + ld [%g7 + 0x68], %o1 + set sun4_notsup, %o0 + call %o1 + nop + +rest_of_boot: + or %g0, PAGE_SHIFT, %g5 + + sethi %hi(AC_CONTEXT), %g1 ! kernel context, safe now + ! the only valid context + ! until we call paging_init() + stba %g0, [%g1] ASI_CONTROL + + +/* I make the kernel image sit in memory relative to 0x0 with the text + * starting at 0x4000. Now it looks like the way memory is set in Linux + * on an ix86. + */ + +/* Uh, oh, interrupt time. This crap is real confusing. What I want to do is + * clear all interrupts, map the interrupt enable register which in effect + * enables non-maskable interrupts (or NMI's). Actually we take no interrupts + * until we frob with the %tbr (trap base register) which the prom has set + * to all its routines which allows some sanity during bootup. + */ + + sethi %hi(IE_reg_addr), %l0 + or %l0, %lo(IE_reg_addr), %l0 + + set 0xf4000000, %l3 + sethi %hi(INT_ENABLE_REG_PHYSADR), %l2 + or %l2, %lo(INT_ENABLE_REG_PHYSADR), %l2 + srl %l2, %g5, %l2 + or %l2, %l3, %l1 + +#ifndef CONFIG_SRMMU + sta %l1, [%l0] ASI_PTE +#endif + + or %g0, 0x1, %l1 + stb %l1, [%l0] + + +/* Aieee, now set PC and nPC, enable traps, give ourselves a stack and it's + * show-time! + */ + + sethi %hi(1f), %g1 + or %g1, %lo(1f), %g1 + jmp %g1 + nop + + .align 4 +1: sethi %hi( C_LABEL(cputyp) ), %o0 + st %g4, [%o0 + %lo( C_LABEL(cputyp) )] + + sethi %hi( C_LABEL(pgshift) ), %o0 + st %g5, [%o0 + %lo( C_LABEL(pgshift) )] + + mov 1, %o0 + sll %o0, %g5, %g5 + sethi %hi( C_LABEL(nbpg) ), %o0 + st %g5, [%o0 + %lo( C_LABEL(nbpg) )] + + sub %g5, 1, %g5 + sethi %hi( C_LABEL(pgofset) ), %o0 + st %g5, [%o0 + %lo( C_LABEL(pgofset) )] + + + rd %psr, %g3 + andn %g3, PSR_ET, %g3 + wr %g3, 0x0, %psr ! make sure traps are off + ! before we play around + WRITE_PAUSE ! no guarantees until 3 insns + + + wr %g0, 0x0, %wim ! magical invalid window reg + WRITE_PAUSE ! see above + +/* I keep the timer interrupt on so that BogoMIPS works and the prom + * keeps updating its "jiffies" counter. 100HZ clock on sparcstations. + */ + +/* If gas wasn't so dumb, I could use or'd macros in this next + * write. ;-( like this (PSR_PS | PSR_S | PSR_PIL)... + */ + + sethi %hi(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2 + or %g2, %lo(PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2 + wr %g2, 0x0, %psr + WRITE_PAUSE + + wr %g0, 0x2, %wim ! window 1 invalid + WRITE_PAUSE + + or %g0, 0x1, %g1 + sethi %hi( C_LABEL(current) + THREAD_WIM), %g2 + st %g1, [%g2 + %lo( C_LABEL(current) + THREAD_WIM)] + +/* I want a kernel stack NOW! */ + + set ( C_LABEL(init_user_stack) + 4092 - 96 - 80), %fp + set ( C_LABEL(init_user_stack) + 4092), %sp + +/* now out stack is set up similarly to the way it is on the i386 */ + + rd %psr, %l0 + wr %l0, PSR_ET, %psr + WRITE_PAUSE + +/* + * Maybe the prom zeroes out our BSS section, maybe it doesn't. I certainly + * don't know, do you? + */ + + set C_LABEL(edata) , %o0 + set C_LABEL(end) , %o1 + sub %o1, %o0, %g2 + sethi %hi( C_LABEL(kernel_bss_len) ), %g3 + st %g2, [%g3 + %lo( C_LABEL(kernel_bss_len) )] + sethi %hi( C_LABEL(trapbase) ), %g3 + or %g3, %lo( C_LABEL(trapbase) ), %g3 + sethi %hi( C_LABEL(etext) ), %g4 + or %g4, %lo( C_LABEL(etext) ), %g4 + sub %g4, %g3, %g2 + sethi %hi( C_LABEL(kernel_text_len) ), %g3 + st %g2, [%g3 + %lo( C_LABEL(kernel_text_len) )] + sethi %hi( C_LABEL(etext) ), %g4 + or %g4, %lo( C_LABEL(etext) ), %g4 + sethi %hi( C_LABEL(edata) ), %g3 + or %g3, %lo( C_LABEL(edata) ), %g3 + sub %g3, %g4, %g2 + sethi %hi( C_LABEL(kernel_data_len) ), %g3 + st %g2, [%g3 + %lo( C_LABEL(kernel_data_len) )] + or %g0, %g0, %g1 + +1: + st %g0, [%o0] + add %o0, 0x4, %o0 + subcc %o0, %o1, %g0 + bl 1b + nop + +/* Compute NWINDOWS and stash it away. Now uses %wim trick explained + * in the V8 manual. Ok, this method seems to work, sparc is cool... + */ + + sethi %hi(0xffffffff), %g1 + rd %wim, %g2 ! save current value + or %g1, %lo(0xffffffff), %g1 + wr %g1, 0x0, %wim + rd %wim, %g1 ! get remaining mask + wr %g2, 0x0, %wim ! restore old value + WRITE_PAUSE + + or %g0, 0x0, %g3 + +1: srl %g1, 0x1, %g1 ! shift until highest + subcc %g1, 0x0, %g0 ! bit set + bne 1b + add %g3, 0x1, %g3 + sethi %hi( C_LABEL(nwindows) ), %g4 + st %g3, [%g4 + %lo( C_LABEL(nwindows) )] ! store final value + sub %g3, 0x1, %g3 + sethi %hi( C_LABEL(nwindowsm1) ), %g4 + st %g3, [%g4 + %lo( C_LABEL(nwindowsm1) )] + + +/* Here we go */ + +#ifndef CONFIG_SUN4M + /* paul@sfe.com.au */ + /* Look into traps later :( */ + set C_LABEL(trapbase), %g3 + wr %g3, 0x0, %tbr + WRITE_PAUSE +#endif + + +/* First we call init_prom() to set up romvec, then off to start_kernel() */ +/* XXX put this in arch_init() */ + + sethi %hi( C_LABEL(prom_vector_p) ), %g5 + call C_LABEL(init_prom) + ld [%g5 + %lo( C_LABEL(prom_vector_p) )], %o0 /* delay slot */ + + call C_LABEL(start_kernel) + nop + + call halt_me + nop + +/* There, happy now adrian? */ + +no_sun4d_here: + ld [%g7 + 0x68], %o1 + set sun4d_notsup, %o0 + call %o1 + nop + b halt_me + nop + +halt_me: + ld [%g7 + 0x74], %o0 + call %o0 ! get us out of here... + nop ! apparently solaris is better + + .data + .align 4 + +/* + * Fill up the prom vector, note in particular the kind first element, + * no joke. I don't need all of them in here as the entire prom vector + * gets initialized in c-code so all routines can use it. + */ + + .globl C_LABEL(prom_vector_p) + +C_LABEL(prom_vector_p): .skip 4 +prom_magic: .skip 4 ! magic mushroom, beware... +prom_rom_vers: .skip 4 ! interface version (v0 or v2) +prom_pluginvers: .skip 4 ! XXX help help help ??? +prom_revision: .skip 4 ! PROM revision (ie. 1.4) +prom_halt: .skip 4 ! void halt(void) solaris friend +prom_eval: .skip 4 ! void eval(int len, char* string) +prom_v0bootline: .skip 4 ! boot command line +prom_v0mem_desc: .skip 4 ! V0 memory descriptor list ptr. +prom_nodefuncs: .skip 4 ! Magical Node functions +prom_printf: .skip 4 ! minimal printf() + +/* The prom_abort pointer MUST be mapped in all contexts, because if you + * don't then if a user process is running when you press the abort key + * sequence, all sorts of bad things can happen + */ + +prom_abort: .skip 4 ! L1-A magic cookie + ! must be mapped in ALL contexts + +/* prom_sync is a place where the kernel should place a pointer to a kernel + * function that when called will sync all pending information to the drives + * and then promptly return. If the kernel gets aborted with 'L1-A' one can + * give the 'sync' command to the boot prompt and this magic cookie gets + * executed. Nice feature eh? + */ + +prom_sync: .skip 4 ! hook in prom for sync func + + .align 4 + +/* We calculate the following at boot time, window fills/spills and trap entry + * code uses these to keep track of the register windows. + */ + + .globl C_LABEL(nwindows) + .globl C_LABEL(nwindowsm1) +C_LABEL(nwindows): .skip 4 +C_LABEL(nwindowsm1): .skip 4 + + .align 4 +/* Boot time privileged register values, plus magic %o2 value */ + + .globl C_LABEL(boot_wim) + .globl C_LABEL(boot_psr) + .globl C_LABEL(boot_tbr) + .globl C_LABEL(boot_smp_ptr) +C_LABEL(boot_wim): .skip 4 +C_LABEL(boot_psr): .skip 4 +C_LABEL(boot_tbr): .skip 4 +C_LABEL(boot_smp_ptr): .skip 4 + + + .align 4 +/* Miscellaneous pieces of information saved at kernel startup. */ + .globl C_LABEL(kernel_text_len) + .globl C_LABEL(kernel_data_len) + .globl C_LABEL(kernel_bss_len) +C_LABEL(kernel_text_len): .word 0 +C_LABEL(kernel_data_len): .word 0 +C_LABEL(kernel_bss_len): .word 0 + +/* These are for page alignment/offset information as they change from + machine to machine. +*/ + + .globl C_LABEL(pgshift) + .globl C_LABEL(nbpg) + .globl C_LABEL(pgofset) + + .align 4 +C_LABEL(pgshift): + .word 1 +C_LABEL(nbpg): + .word 1 +C_LABEL(pgofset): + .word 1 + +/* Just to get the kernel through the compiler for now */ + .globl C_LABEL(swapper_pg_dir), C_LABEL(pg0) + .globl C_LABEL(empty_bad_page) + .globl C_LABEL(empty_bad_page_table) + .globl C_LABEL(empty_zero_page) + .globl C_LABEL(floppy_track_buffer) +C_LABEL(floppy_track_buffer): + .fill 512*2*36,1,0 + + .align 4 +C_LABEL(swapper_pg_dir): .skip 0x1000 +C_LABEL(pg0): .skip 0x1000 +C_LABEL(empty_bad_page): .skip 0x1000 +C_LABEL(empty_bad_page_table): .skip 0x1000 +C_LABEL(empty_zero_page): .skip 0x1000 + + .align 4 +diagstr: .asciz "DIAG\n" + .align 4 |