blob: 74841c57a6f8255dbcf1320292bbe82cf3d6f643 [file] [log] [blame]
/**
** 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"
#include "cpustate.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, nwindows
.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
*/
nwindows:
.word 0
/*
* 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 CPU number
! XXX: not all CPUs should have MXCC
set 0x1c00f00, %g2
ldda [%g2] ASI_CONTROL, %g2
srl %g3, 24, %g7
subcc %g7, 8, %g7
! Only the first CPU clears memory
bnz clear_done
nop
! 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
! 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, uniprocessor
ba first_cpu
nop
ss10:
! Ok, this is SS-10/20 or SS-600MP
tst %g7
bz first_cpu
nop
! Clear softints used for SMP CPU startup
set PHYS_SS10_INTR0 + 0x04, %g1
sll %g7, 12, %g2
add %g1, %g2, %g2
set 0xffffffff, %g1
sta %g1, [%g2] ASI_M_CTL ! clear softints
add %g2, 4, %g2
sta %g0, [%g2] ASI_M_CTL ! clear softints
! 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
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
set nwindows, %g2 ! store nwindows
st %g3, [%g2]
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 up a default context */
set __context, %g1
ld [%g1], %g1
SAVE_CPU_GENERAL_STATE(entry)
SAVE_CPU_WINDOW_STATE(entry)
/* Set up local stack pointer */
set _estack - 0x40, %sp
/* And for the main context */
add %sp, -0x260, %g2
st %g2, [%g1 + 0x48]
call __switch_context
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"