/*++ 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