580 lines
11 KiB
ArmAsm
580 lines
11 KiB
ArmAsm
/*++
|
||
|
||
Copyright (c) 1993 Digital Equipment Corporation
|
||
|
||
|
||
Module Name:
|
||
|
||
jenassem.s
|
||
|
||
|
||
Abstract:
|
||
|
||
This contains assembler code routines for the Alpha PCs.
|
||
|
||
The first section contains functions that need to explicitly
|
||
generate Alpha macroinstructions (e.g., mb, PALcode calls).
|
||
These could be asm() calls within the C code, but our compiler does
|
||
not now handle asm()'s and may not for some time.
|
||
|
||
The second section has linkages for "Fw" calls.
|
||
|
||
The last section has stubs for functions that should be defined
|
||
elsewhere, but are not. When the real code appears in the
|
||
Alpha build tree, these stub routines should be deleted.
|
||
|
||
Most of the "Fw" call section is directly patterned after
|
||
\nt\private\ntos\fw\mips\fwtrap.s, written by Lluis Abello of
|
||
Microsoft.
|
||
|
||
|
||
Author:
|
||
|
||
John DeRosa [DEC] 21-May-1992
|
||
|
||
|
||
Environment:
|
||
|
||
Executes in kernel mode.
|
||
|
||
Revision History:
|
||
|
||
|
||
--*/
|
||
|
||
|
||
#include "ksalpha.h"
|
||
#include "selfmap.h"
|
||
#include "machdef.h"
|
||
|
||
//
|
||
// Static data
|
||
//
|
||
|
||
.align 4
|
||
RegisterTable:
|
||
.space RegisterTableSize
|
||
|
||
|
||
/*++
|
||
|
||
VOID
|
||
FwStallExecution (
|
||
IN ULONG MicroSeconds
|
||
)
|
||
|
||
|
||
Routine Description:
|
||
|
||
This stalls for at least the requested number of microseconds.
|
||
Current timing on a Jensen indicates that this is pessimistic
|
||
by a factor of 1.2.
|
||
|
||
Arguments:
|
||
|
||
Microseconds (a0) - The number of microseconds to stall.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
LEAF_ENTRY( FwStallExecution)
|
||
|
||
beq a0, 20f // exit if zero delay requested
|
||
|
||
// lda t0, 20000(zero) // force small delays to 20 milliseconds
|
||
// subl a0, 5, t1
|
||
// cmoveq t1, t0, a0
|
||
|
||
10: bsr t3, 100f // call 1 microsecond delay subroutine
|
||
|
||
subl a0, 1, a0 // decrement requested microseconds
|
||
zap a0, 0xf0, a0 // unsigned long a0
|
||
|
||
bgt a0, 10b
|
||
|
||
20: ret zero, (ra)
|
||
|
||
//
|
||
// 1 microsecond delay subroutine
|
||
//
|
||
|
||
100: ldl t0, CyclesPerMicrosecond // init 1 microsecond delay
|
||
|
||
rpcc t1 // get entry time rpcc value
|
||
zap t1, 0xf0, t1 // clear <63:32>
|
||
|
||
200: rpcc t2 // get current rpcc value
|
||
zap t2, 0xf0, t2 // clear <63:32>
|
||
|
||
subl t2, t1, t2 // compute unsigned 32b difference
|
||
zap t2, 0xf0, t2
|
||
|
||
subl t0, t2, t2 // (requested delay - delay so far) > 0?
|
||
|
||
bgt t2, 200b
|
||
|
||
ret zero, (t3)
|
||
|
||
.end FwStallExecution
|
||
|
||
|
||
|
||
/*****************************************************************
|
||
|
||
Simple functions to perform PALcode calls and memory barriers.
|
||
|
||
******************************************************************/
|
||
|
||
|
||
LEAF_ENTRY(AlphaInstIMB)
|
||
|
||
callpal imb
|
||
ret zero, (ra)
|
||
|
||
.end AlphaInstIMB
|
||
|
||
|
||
|
||
LEAF_ENTRY(AlphaInstMB)
|
||
|
||
mb
|
||
ret zero, (ra)
|
||
|
||
.end AlphaInstMB
|
||
|
||
|
||
|
||
LEAF_ENTRY(AlphaInstHalt)
|
||
|
||
callpal halt
|
||
ret zero, (ra) # should never return, but...
|
||
|
||
.end AlphaInstHalt
|
||
|
||
|
||
|
||
LEAF_ENTRY(DisableInterrupts)
|
||
|
||
callpal di
|
||
ret zero, (ra)
|
||
|
||
.end DisableInterrupts
|
||
|
||
|
||
|
||
LEAF_ENTRY(RegisterExceptionHandler)
|
||
|
||
lda a0, Monitor # Run monitor on unexpected exceptions
|
||
callpal wrentry
|
||
ret zero, (ra)
|
||
|
||
.end RegisterExceptionHandler
|
||
|
||
NESTED_ENTRY(FwExecute, 0x60, ra)
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the entry point for the Execute service.
|
||
|
||
It behaves in two different ways depending on where it is called from:
|
||
|
||
1) If called from the Firmware, it saves the stack pointer
|
||
in a fixed location and then saves all the saved registers
|
||
in the stack. This is the stack that will be used to restore
|
||
the saved state when returning to the firmware.
|
||
|
||
2) If called from a loaded program, the program to be loaded
|
||
and executed can overwrite the current program and its
|
||
stack. Therefore a temporary stack is set.
|
||
|
||
Arguments:
|
||
|
||
a0 = IN PCHAR Path,
|
||
a1 = IN ULONG Argc,
|
||
a2 = IN PCHAR Argv[],
|
||
a3 = IN PCHAR Envp[]
|
||
|
||
Return Value:
|
||
|
||
ARC_STATUS returned by FwPrivateExecute.
|
||
Always returns to the Firmware.
|
||
|
||
--*/
|
||
|
||
//
|
||
// If the longword is zero then this is the first call from
|
||
// the firmware, and is not a call from an already loaded program.
|
||
//
|
||
|
||
lda t0, FwSavedSp
|
||
ldl t1, (t0)
|
||
beq t1, CallFromFw
|
||
|
||
|
||
//
|
||
// Here when an already loaded program wants to be replaced
|
||
// by another program. Therefore, the current stack and state
|
||
// will be trashed and a new temporary stack needs to be set.
|
||
// (A temporary stack is used to guarantee that there is enough
|
||
// stack space for the necessary calls.)
|
||
//
|
||
|
||
lda t0, FwTemporaryStack
|
||
ldl sp, (t0)
|
||
jsr ra, FwPrivateExecute # this does the dirty work.
|
||
|
||
//
|
||
// The executed program has returned. Its caller is gone.
|
||
// Therefore, restore the initial firmware stack and return
|
||
// to the firmware instead.
|
||
//
|
||
|
||
lda t0, FwSavedSp
|
||
ldl sp, (t0) # restore saved stack
|
||
br zero, RestoreFwState # go restore the state & return
|
||
|
||
|
||
|
||
|
||
CallFromFw:
|
||
|
||
subq sp, 0x60 # make room in the stack
|
||
stl sp, (t0) # save new stack pointer
|
||
|
||
stq ra, (sp) # return address on top of stack
|
||
stq s0, 0x8(sp) # save s registers
|
||
stq s1, 0x10(sp)
|
||
stq s2, 0x18(sp)
|
||
stq s3, 0x20(sp)
|
||
stq s4, 0x28(sp)
|
||
stq s5, 0x30(sp)
|
||
stq fp, 0x38(sp)
|
||
stq gp, 0x40(sp)
|
||
|
||
jsr ra, FwPrivateExecute # go do the work.
|
||
|
||
|
||
RestoreFwState:
|
||
|
||
ldq ra, (sp) # restore return address
|
||
ldq s0, 0x8(sp) # restore s registers
|
||
ldq s1, 0x10(sp)
|
||
ldq s2, 0x18(sp)
|
||
ldq s3, 0x20(sp)
|
||
ldq s4, 0x28(sp)
|
||
ldq s5, 0x30(sp)
|
||
ldq fp, 0x38(sp)
|
||
ldq gp, 0x40(sp)
|
||
addq sp, 0x60 # restore stack pointer
|
||
|
||
ret zero, (ra) # return to firmware control
|
||
|
||
.end FwExecute
|
||
|
||
NESTED_ENTRY(FwInvoke, 0x40, ra)
|
||
|
||
/*++
|
||
ARC_STATUS
|
||
FwInvoke(
|
||
IN ULONG ExecAddr,
|
||
IN ULONG StackAddr,
|
||
IN ULONG Argc,
|
||
IN PCHAR Argv[],
|
||
IN PCHAR Envp[]
|
||
)
|
||
|
||
|
||
Routine Description:
|
||
|
||
This routine invokes a loaded program.
|
||
|
||
Arguments:
|
||
|
||
ExecAddr - Supplies the address of the routine to call.
|
||
|
||
StackAddr - Supplies the address to which the stack pointer is set.
|
||
|
||
Argc, Argv, Envp - Supply the arguments and environment to pass to
|
||
Loaded program.
|
||
|
||
The stack pointer is saved in register s0 so that when the loaded
|
||
program returns, the old stack pointer can be restored.
|
||
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS is returned if the address is valid.
|
||
EFAULT indicates an invalid address.
|
||
|
||
--*/
|
||
|
||
|
||
subq sp, 0x40 # make room on the stack
|
||
stq ra, (sp) # save ra on top of stack
|
||
stq s0, 0x8(sp) # save s0
|
||
and a0, 3, t1 # return EFAULT if unaligned address
|
||
ldiq v0, 0x6
|
||
bne t1, 1f # branch if address alignment error
|
||
|
||
mov a0, t12 # save program address in t12
|
||
|
||
mov sp, s0 # save stack pointer
|
||
mov a1, sp # ..and load new one for program
|
||
mov a2, a0 # argc becomes first argument
|
||
mov a3, a1 # argv becomes second argument
|
||
mov a4, a2 # envp becomes third argument
|
||
|
||
jsr ra, (t12) # call program
|
||
|
||
//
|
||
// here if loaded program returns.
|
||
//
|
||
|
||
mov s0, sp # restore stack pointer
|
||
mov zero, v0 # return ESUCCESS value
|
||
ldq s0, 0x8(sp) # restore things
|
||
ldq ra, (sp)
|
||
1:
|
||
addq sp, 0x40
|
||
ret zero, (ra)
|
||
|
||
.end FwInvoke
|
||
|
||
NESTED_ENTRY(FwMonitor, 50, ra)
|
||
|
||
/*****************************************************************
|
||
|
||
Linkage to the monitor from the jxboot.c boot menu and code in
|
||
bldr\alpha\stubs.c.
|
||
|
||
******************************************************************/
|
||
|
||
|
||
//
|
||
// Move registers into exception frame and call the Monitor.
|
||
// We cannot exactly duplicate what the PALcode creates
|
||
// on an exception.
|
||
//
|
||
// This used to specify the stq addresses as
|
||
// RegisterTable+offset(zero)
|
||
// but the assembler won't generate correct code that way.
|
||
//
|
||
|
||
subq sp, 0x8 # setup t0 with base of register table,
|
||
stq t0, (sp) # and use t1 to store old t0 in it.
|
||
lda t0, RegisterTable
|
||
stq t1, t1RegTable(t0)
|
||
ldq t1, (sp)
|
||
stq t1, t0RegTable(t0)
|
||
addq sp, 0x8
|
||
|
||
stq v0, v0RegTable(t0)
|
||
/* stq t0, RegisterTable+t0RegTable(zero) */
|
||
/* stq t1, RegisterTable+t1RegTable(zero) */
|
||
stq t2, t2RegTable(t0)
|
||
stq t3, t3RegTable(t0)
|
||
stq t4, t4RegTable(t0)
|
||
stq t5, t5RegTable(t0)
|
||
stq t6, t6RegTable(t0)
|
||
stq t7, t7RegTable(t0)
|
||
stq s0, s0RegTable(t0)
|
||
stq s1, s1RegTable(t0)
|
||
stq s2, s2RegTable(t0)
|
||
stq s3, s3RegTable(t0)
|
||
stq s4, s4RegTable(t0)
|
||
stq s5, s5RegTable(t0)
|
||
stq fp, fpRegTable(t0)
|
||
stq a0, a0RegTable(t0)
|
||
stq a1, a1RegTable(t0)
|
||
stq a2, a2RegTable(t0)
|
||
stq a3, a3RegTable(t0)
|
||
stq a4, a4RegTable(t0)
|
||
stq a5, a5RegTable(t0)
|
||
stq t8, t8RegTable(t0)
|
||
stq t9, t9RegTable(t0)
|
||
stq t10, t10RegTable(t0)
|
||
stq t11, t11RegTable(t0)
|
||
stq ra, raRegTable(t0)
|
||
stq t12, t12RegTable(t0)
|
||
.set noat
|
||
stq AT, atRegTable(t0)
|
||
.set at
|
||
stq gp, gpRegTable(t0)
|
||
stq sp, spRegTable(t0)
|
||
stq zero, zeroRegTable(t0)
|
||
stt f0, f0RegTable(t0)
|
||
stt f1, f1RegTable(t0)
|
||
stt f2, f2RegTable(t0)
|
||
stt f3, f3RegTable(t0)
|
||
stt f4, f4RegTable(t0)
|
||
stt f5, f5RegTable(t0)
|
||
stt f6, f6RegTable(t0)
|
||
stt f7, f7RegTable(t0)
|
||
stt f8, f8RegTable(t0)
|
||
stt f9, f9RegTable(t0)
|
||
stt f10, f10RegTable(t0)
|
||
stt f11, f11RegTable(t0)
|
||
stt f12, f12RegTable(t0)
|
||
stt f13, f13RegTable(t0)
|
||
stt f14, f14RegTable(t0)
|
||
stt f15, f15RegTable(t0)
|
||
stt f16, f16RegTable(t0)
|
||
stt f17, f17RegTable(t0)
|
||
stt f18, f18RegTable(t0)
|
||
stt f19, f19RegTable(t0)
|
||
stt f20, f20RegTable(t0)
|
||
stt f21, f21RegTable(t0)
|
||
stt f22, f22RegTable(t0)
|
||
stt f23, f23RegTable(t0)
|
||
stt f24, f24RegTable(t0)
|
||
stt f25, f25RegTable(t0)
|
||
stt f26, f26RegTable(t0)
|
||
stt f27, f27RegTable(t0)
|
||
stt f28, f28RegTable(t0)
|
||
stt f29, f29RegTable(t0)
|
||
stt f30, f30RegTable(t0)
|
||
stt f31, f31RegTable(t0)
|
||
|
||
ldil t1, 0xedbedbed # phony exception type
|
||
stl t1, ResExceptTypeRegTable(t0)
|
||
|
||
# a0 has the CallerSource argument
|
||
# already.
|
||
lda a1, RegisterTable # Frame argument
|
||
jsr ra, Monitor
|
||
|
||
|
||
//
|
||
// On return just restore ra from the RegisterTable.
|
||
// This is coded this way to get around an assembler bug...
|
||
//
|
||
|
||
// ldq ra, RegisterTable+raRegTable
|
||
lda t0, RegisterTable
|
||
ldq ra, raRegTable(t0)
|
||
ret zero, (ra)
|
||
|
||
.end FwMonitor
|
||
|
||
#if 0
|
||
|
||
//
|
||
// This function was used to zero out memory in the selftest.c module.
|
||
// We do not need to do this anymore.
|
||
//
|
||
|
||
/****
|
||
|
||
VOID
|
||
WildZeroMemory(
|
||
IN ULONG StartAddress,
|
||
IN ULONG Size
|
||
)
|
||
Routine Description:
|
||
|
||
This routine zeroes the specified range of memory.
|
||
|
||
At some point this may be changed to a more clever algorithm,
|
||
For now, it simply does store quads.
|
||
|
||
Arguments:
|
||
|
||
a0 - supplies the base physical address of the range of memory
|
||
to zero. It must be a multiple of the data cache line
|
||
size.
|
||
|
||
a1 - supplies length of memory to zero, in bytes. This must
|
||
be a multiple of the data cache line size.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
LEAF_ENTRY(WildZeroMemory)
|
||
|
||
mov a0, t0 # start address
|
||
mov a1, t1 # number of bytes to move
|
||
|
||
1:
|
||
subqv t1, 0x20 # zero a D-cache block = 32 bytes
|
||
stq zero, (t0)
|
||
stq zero, 0x8(t0)
|
||
stq zero, 0x10(t0)
|
||
stq zero, 0x18(t0)
|
||
addqv t0, 0x20 # move to next cache block
|
||
bgt t1, 1b # t1 = 0 when done.
|
||
|
||
ret zero, (ra)
|
||
|
||
.end WildZeroMemory
|
||
|
||
#endif
|
||
|
||
|
||
#ifdef ALPHA_FW_KDHOOKS
|
||
|
||
/*++
|
||
|
||
VOID
|
||
FwRfe(
|
||
VOID
|
||
)
|
||
|
||
Routine Description:
|
||
|
||
This routine executes a return from exception instruction.
|
||
It is used to return after processing a breakpoint.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
LEAF_ENTRY(FwRfe)
|
||
|
||
bis a0, zero, sp # Set the stack pointer to the
|
||
# exception frame pointer.
|
||
lda sp, -0x10(sp) # Adjust for stack empty space
|
||
callpal rfe # This does NOT return.
|
||
|
||
.end FwRfe
|
||
|
||
#endif
|
||
|
||
|
||
/************************************************************
|
||
|
||
Stubs.
|
||
|
||
*************************************************************/
|
||
|
||
#ifndef ALPHA_FW_KDHOOKS
|
||
|
||
//
|
||
// This cannot be defined for kd build.
|
||
//
|
||
|
||
LEAF_ENTRY(DebugPrompt)
|
||
|
||
callpal halt # surprise!
|
||
ret zero, (ra) # should never return, but...
|
||
|
||
.end DebugPrompt
|
||
|
||
#endif
|