| /** |
| ** Standalone startup code for Linux PROM emulator. |
| ** Copyright 1999 Pete A. Zaitcev |
| ** This code is licensed under GNU General Public License. |
| **/ |
| /* |
| * $Id: head.S,v 1.12 2002/07/23 05:47:09 zaitcev Exp $ |
| */ |
| |
| #define __ASSEMBLY |
| #include "psr.h" |
| #include "asm/asi.h" |
| #include "asm/crs.h" |
| #define NO_QEMU_PROTOS |
| #define NO_OPENBIOS_PROTOS |
| #include "arch/common/fw_cfg.h" |
| |
| #define CFG_ADDR 0x00000510 |
| #define CFG_ASI 0x2d |
| |
| #define PHYS_JJ_INTR0 0x71E00000 /* CPU0 interrupt control registers */ |
| |
| #define PHYS_SS10_INTR0 0xf1400000 |
| |
| #define PHYS_SS2_INTR0 0xf5000000 |
| #define SER_ADDR2 0xf1000004 |
| |
| #define PHYS_SS1000_SBI 0x02800000 |
| #define SER_ADDR1000 0x00200004 |
| |
| #define WRITE_PAUSE nop; nop; nop; /* Have to do this after %wim/%psr chg */ |
| |
| .globl entry, _entry |
| |
| .section ".text", "ax" |
| .align 8 |
| |
| /* Memory map: |
| * |
| * Top +-------------------------+ |
| * | SMP CPU table | |
| * | s + 0x1f00 ... 0x1f0f | |
| * | s + 0x1f0c valid | |
| * | s + 0x1f08 entry | |
| * | s + 0x1f04 ctxtbl | |
| * | s + 0x1f00 ctx | |
| * +-------------------------+ |
| * | Bootstrap | |
| * | MMU L3 tables 8 * 0x100 | |
| * | s + 0xa00 ... 0x11ff | |
| * +-------------------------+ |
| * | Bootstrap | |
| * | MMU L2 tables 2 * 0x100 | |
| * | s + 0x800 ... 0x9ff | |
| * +-------------------------+ |
| * | Bootstrap | |
| * | MMU L1 table 0x400 | |
| * | s + 0x400 ... 0x7ff | |
| * +-------------------------+ |
| * | Bootstrap | |
| * | MMU L0/ctx table 0x400 | |
| * | s + 0x000 ... 0x3ff | |
| * +-------------------------+ |
| * | | |
| * | ROM into RAM | |
| * | | |
| * +-------------------------+ |
| * : : |
| * Bottom |
| */ |
| |
| /* |
| * Entry point |
| * We start execution from here. |
| */ |
| _entry: |
| entry: |
| /* Switch to our main context. |
| * Main context is statically defined in C. |
| */ |
| |
| ! Check signature "QEMU" |
| set CFG_ADDR, %g5 |
| mov FW_CFG_SIGNATURE, %g2 |
| stha %g2, [%g5] CFG_ASI |
| add %g5, 2, %g5 |
| lduba [%g5] CFG_ASI, %g2 |
| cmp %g2, 'Q' |
| bne bad_conf |
| nop |
| lduba [%g5] CFG_ASI, %g2 |
| cmp %g2, 'E' |
| bne bad_conf |
| nop |
| lduba [%g5] CFG_ASI, %g2 |
| cmp %g2, 'M' |
| bne bad_conf |
| nop |
| lduba [%g5] CFG_ASI, %g2 |
| cmp %g2, 'U' |
| bne bad_conf |
| nop |
| |
| ! Get memory size from configuration device |
| ! NB: little endian format |
| mov FW_CFG_RAM_SIZE, %g2 |
| sub %g5, 2, %g5 |
| stha %g2, [%g5] CFG_ASI |
| add %g5, 2, %g5 |
| lduba [%g5] CFG_ASI, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 8, %g3 |
| or %g3, %g4, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 16, %g3 |
| or %g3, %g4, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 24, %g3 |
| or %g3, %g4, %g1 |
| ! %g1 contains end of memory |
| |
| ! Get kernel address from configuration device |
| ! NB: little endian format |
| mov FW_CFG_KERNEL_ADDR, %g2 |
| sub %g5, 2, %g5 |
| stha %g2, [%g5] CFG_ASI |
| add %g5, 2, %g5 |
| lduba [%g5] CFG_ASI, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 8, %g3 |
| or %g3, %g4, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 16, %g3 |
| or %g3, %g4, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 24, %g3 |
| or %g3, %g4, %g4 |
| |
| ! If kernel address is set, don't clear from base of RAM in order to |
| ! leave the kernel image intact |
| mov 0, %g6 |
| cmp %g4, 0 |
| beq clear_mem |
| nop |
| |
| ! Start from 16M |
| set 0x1000000, %g6 |
| |
| clear_mem: |
| sta %g0, [%g6] ASI_M_BYPASS |
| add %g6, 0x4, %g6 |
| cmp %g6, %g1 |
| bl clear_mem |
| nop |
| |
| clear_done: |
| ! Start of private memory in %g6 |
| set 0x2000, %g3 |
| sub %g1, %g3, %g6 |
| |
| ! Check if this is the boot CPU and skip SMP table check if yes |
| ! XXX: not all CPUs should have MXCC |
| set 0x1c00f00, %g2 |
| ldda [%g2] ASI_CONTROL, %g2 |
| srl %g3, 24, %g7 |
| sub %g7, 8, %g7 |
| tst %g7 |
| bz skip_table |
| nop |
| |
| ! Calculate SMP table location |
| set 0x1f0c, %g2 |
| add %g6, %g2, %g2 ! valid? |
| lda [%g2] ASI_M_BYPASS, %g7 |
| sta %g0, [%g2] ASI_M_BYPASS |
| |
| skip_table: |
| ! Get machine ID from configuration device |
| mov FW_CFG_MACHINE_ID, %g2 |
| sub %g5, 2, %g5 |
| stha %g2, [%g5] CFG_ASI |
| add %g5, 2, %g5 |
| lduba [%g5] CFG_ASI, %g4 |
| |
| lduba [%g5] CFG_ASI, %g3 |
| sll %g3, 8, %g3 |
| or %g3, %g4, %g4 |
| mov %g4, %y |
| |
| cmp %g4, 96 |
| bgeu ss1000 |
| cmp %g4, 64 |
| bgeu ss10 |
| cmp %g4, 32 |
| blu ss2 |
| nop |
| |
| ! Ok, this is SS-5 |
| |
| tst %g7 |
| bz first_cpu |
| nop |
| |
| ! Clear softints used for SMP CPU startup |
| set PHYS_JJ_INTR0 + 0x04, %g1 |
| sll %g2, 12, %g2 |
| add %g1, %g2, %g2 |
| set 0xffffffff, %g1 |
| sta %g1, [%g2] ASI_M_BYPASS ! clear softints |
| add %g2, 4, %g2 |
| sta %g0, [%g2] ASI_M_BYPASS ! clear softints |
| |
| load_ctx: |
| ! SMP init, jump to user specified address |
| set 0x1f04, %g5 |
| add %g6, %g5, %g5 ! ctxtbl |
| lda [%g5] ASI_M_BYPASS, %g2 |
| sta %g0, [%g5] ASI_M_BYPASS |
| set AC_M_CTPR, %g1 |
| sta %g2, [%g1] ASI_M_MMUREGS ! set ctx table ptr |
| set 0x1f00, %g5 |
| add %g6, %g5, %g5 ! ctx |
| lda [%g5] ASI_M_BYPASS, %g2 |
| sta %g0, [%g5] ASI_M_BYPASS |
| set AC_M_CXR, %g1 |
| sta %g2, [%g1] ASI_M_MMUREGS ! set context |
| set 0x1f08, %g5 |
| add %g6, %g5, %g5 ! entry |
| lda [%g5] ASI_M_BYPASS, %g2 |
| sta %g0, [%g5] ASI_M_BYPASS |
| set 1, %g1 |
| jmp %g2 ! jump to kernel |
| sta %g1, [%g0] ASI_M_MMUREGS ! enable mmu |
| |
| ss10: |
| ! Ok, this is SS-10 or SS-600MP |
| tst %g7 |
| bz first_cpu |
| nop |
| |
| ! Clear softints used for SMP CPU startup |
| set PHYS_SS10_INTR0 + 0x04, %g1 |
| sll %g2, 12, %g2 |
| add %g1, %g2, %g2 |
| set 0xffffffff, %g1 |
| sta %g1, [%g2] ASI_M_CTL ! clear softints |
| add %g2, 4, %g2 |
| b load_ctx |
| sta %g0, [%g2] ASI_M_CTL ! clear softints |
| |
| ss2: |
| ! Ok, this is SS-2 |
| set ss2_error, %o2 |
| b ss2_ss1000_halt |
| nop |
| |
| ss1000: |
| ! Ok, this is SS-1000 or SS-2000 |
| set ss1000_error, %o2 |
| b ss2_ss1000_halt |
| nop |
| |
| first_cpu: |
| /* Create temporary page tables and map the ROM area to end of |
| RAM. This will be done properly in iommu.c later. */ |
| ! Calculate start of page tables etc. to %g6 |
| set 0x2000, %g4 |
| sub %g1, %g4, %g6 ! start of private memory |
| |
| mov %g6, %g2 ! ctx table at s+0x0 |
| add %g2, 0x400, %g3 ! l1 table at s+0x400 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 0x400, %g2 ! s+0x400 |
| add %g2, 0x400, %g3 ! l2 table for ram (00xxxxxx) at s+0x800 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 0x500, %g3 ! l2 table for rom (ffxxxxxx) at s+0x900 |
| add %g2, 0x3fc, %g2 ! s+0x7fc |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 0x4, %g2 ! s+0x800 |
| #if 0 |
| set 0x40, %g6 |
| set ((7 << 2) | 2), %g3 ! 7 = U: --- S: RWX (main memory) |
| 1: sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 |
| deccc %g6 |
| bne 1b |
| nop |
| #else |
| add %g2, 0x100, %g2 |
| #endif |
| ! s+0x900 |
| add %g2, 0xa00 - 0x900, %g3 ! l3 table for rom at s+0xa00 |
| add %g2, 0x0d0, %g2 ! s+0x9d0 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9d4 |
| add %g2, 0xb00 - 0x9d4, %g3 ! 2nd l3 table for rom at s+0xb00 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9d8 |
| add %g2, 0xc00 - 0x9d8, %g3 ! 3rd l3 table for rom at s+0xc00 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9dc |
| add %g2, 0xd00 - 0x9dc, %g3 ! 4th l3 table for rom at s+0xd00 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9e0 |
| add %g2, 0xe00 - 0x9e0, %g3 ! 5th l3 table for rom at s+0xe00 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9e4 |
| add %g2, 0xf00 - 0x9e4, %g3 ! 6th l3 table for rom at s+0xf00 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9e8 |
| add %g2, 0x1000 - 0x9e8, %g3 ! 7th l3 table for rom at s+0x1000 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 ! s+0x9ec |
| add %g2, 0x1100 - 0x9ec, %g3 ! 8th l3 table for rom at s+0x1100 |
| srl %g3, 0x4, %g3 |
| or %g3, 0x1, %g3 |
| sta %g3, [%g2] ASI_M_BYPASS |
| add %g2, 0xa00-0x9ec, %g2 ! s+0xa00 |
| |
| /* Use end of ram for code, rodata, data, and bss |
| sections. SunOS wants to write to trap table... */ |
| set _end, %g6 |
| set _start, %g4 |
| sub %g6, %g4, %g6 |
| sub %g1, %g6, %g3 |
| set 0x1000, %g5 |
| sub %g3, %g5, %g3 |
| sub %g3, %g5, %g3 ! start of ROM copy |
| mov %g3, %g7 ! save in %g7 |
| srl %g6, 12, %g6 ! # of all pages |
| 1: srl %g3, 0x4, %g4 |
| or %g4, ((7 << 2) | 2), %g4 ! 7 = U: --- S: RWX |
| sta %g4, [%g2] ASI_M_BYPASS |
| add %g2, 4, %g2 |
| add %g3, %g5, %g3 |
| deccc %g6 |
| bne 1b |
| nop |
| |
| mov %g1, %g6 ! %g6 = memory size |
| |
| /* Copy the code, rodata and data sections from ROM. */ |
| sub %g7, 4, %g3 |
| set _start - 4, %g4 ! First address of TEXT - 4 |
| set _bss, %g5 ! Last address of DATA |
| ba 2f |
| nop |
| 1: |
| lda [%g4] ASI_M_KERNELTXT, %g1 |
| sta %g1, [%g3] ASI_M_BYPASS |
| 2: |
| cmp %g4, %g5 |
| add %g3, 0x4, %g3 |
| bl 1b |
| add %g4, 0x4, %g4 |
| |
| set 0x2000, %g3 |
| sub %g6, %g3, %g7 ! ctx table at s+0x0 |
| set AC_M_CTPR, %g2 |
| srl %g7, 4, %g7 |
| sta %g7, [%g2] ASI_M_MMUREGS ! set ctx table ptr |
| set AC_M_CXR, %g2 |
| sta %g0, [%g2] ASI_M_MMUREGS ! context 0 |
| set highmem, %g2 |
| set 1, %g1 |
| jmp %g2 |
| sta %g1, [%g0] ASI_M_MMUREGS ! enable mmu |
| highmem: |
| /* |
| * The code which enables traps is a simplified version of |
| * kernel head.S. |
| * |
| * We know number of windows as 8 so we do not calculate them. |
| * The deadwood is here for any case. |
| */ |
| |
| /* Turn on Supervisor, EnableFloating, and all the PIL bits. |
| * Also puts us in register window zero with traps off. |
| */ |
| set (PSR_PS | PSR_S | PSR_PIL | PSR_EF), %g2 |
| wr %g2, 0x0, %psr |
| WRITE_PAUSE |
| |
| /* Zero out our BSS section. */ |
| set _bss - 4, %o0 ! First address of BSS |
| set _estack - 4, %o1 ! Last address of BSS |
| ba 2f |
| nop |
| 1: |
| st %g0, [%o0] |
| 2: |
| subcc %o0, %o1, %g0 |
| bl 1b |
| add %o0, 0x4, %o0 |
| |
| set trap_table, %g1 |
| wr %g1, 0x0, %tbr |
| |
| set qemu_mem_size, %g1 |
| st %g6, [%g1] |
| |
| set _end, %o0 ! Store va->pa conversion factor |
| set _start, %o2 |
| sub %o0, %o2, %o0 |
| sub %g6, %o0, %o0 |
| set 0x2000, %o1 |
| sub %o0, %o1, %o0 ! start of ROM copy |
| sub %o2, %o0, %o0 ! start of ROM copy |
| set va_shift, %g1 |
| st %o0, [%g1] |
| |
| set qemu_machine_type, %g1 |
| mov %y, %g2 |
| st %g2, [%g1] |
| |
| /* Compute NWINDOWS and stash it away. Now uses %wim trick explained |
| * in the V8 manual. Ok, this method seems to work, Sparc is cool... |
| * No, it doesn't work, have to play the save/readCWP/restore trick. |
| */ |
| |
| wr %g0, 0x0, %wim ! so we do not get a trap |
| WRITE_PAUSE |
| |
| save |
| |
| rd %psr, %g3 |
| |
| restore |
| |
| and %g3, 0x1f, %g3 |
| add %g3, 0x1, %g3 |
| |
| mov 2, %g1 |
| wr %g1, 0x0, %wim ! make window 1 invalid |
| WRITE_PAUSE |
| |
| cmp %g3, 0x7 |
| bne 1f |
| nop |
| |
| /* Adjust our window handling routines to |
| * do things correctly on 7 window Sparcs. |
| */ |
| #define PATCH_INSN(src, dest) \ |
| set src, %g5; \ |
| set dest, %g2; \ |
| ld [%g5], %g4; \ |
| st %g4, [%g2]; |
| |
| /* Patch for window spills... */ |
| PATCH_INSN(spnwin_patch1_7win, spnwin_patch1) |
| PATCH_INSN(spnwin_patch2_7win, spnwin_patch2) |
| |
| /* Patch for window fills... */ |
| PATCH_INSN(fnwin_patch1_7win, fnwin_patch1) |
| PATCH_INSN(fnwin_patch2_7win, fnwin_patch2) |
| |
| 1: |
| /* Finally, turn on traps so that we can call c-code. */ |
| rd %psr, %g3 |
| wr %g3, 0x0, %psr |
| WRITE_PAUSE |
| |
| wr %g3, PSR_ET, %psr |
| WRITE_PAUSE |
| |
| set 0, %fp |
| call __switch_context_nosave |
| nop |
| |
| /* We get here when the main context switches back to |
| * the boot context. |
| * Return to previous bootloader. |
| */ |
| ret |
| nop |
| |
| ss2_ss1000_halt: |
| set SER_ADDR2, %o0 |
| set SER_ADDR1000, %o1 |
| mov 0x05, %o3 /* Reg 5, TXCTRL2 */ |
| stba %o3, [%o0] ASI_M_BYPASS |
| stba %o3, [%o1] ASI_M_CTL |
| mov 0x68, %o3 /* 8 bits, Tx enabled */ |
| stba %o3, [%o0] ASI_M_BYPASS |
| stba %o3, [%o1] ASI_M_CTL |
| add %o0, 2, %o0 |
| add %o1, 2, %o1 |
| |
| 1: lduba [%o2] ASI_M_KERNELTXT, %o3 |
| cmp %o3, 0 |
| be 2f |
| nop |
| stba %o3, [%o0] ASI_M_BYPASS |
| stba %o3, [%o1] ASI_M_CTL |
| b 1b |
| inc %o2 |
| bad_conf: |
| 2: b 2b |
| nop |
| |
| .section .rodata |
| ss2_error: |
| .string "Sun4c machines are not supported by OpenBIOS yet, freezing\r\n" |
| ss1000_error: |
| .string "Sun4d machines are not supported by OpenBIOS yet, freezing\r\n" |