From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/rtl/alpha/capture.s | 310 +++++ private/ntos/rtl/alpha/chandler.c | 297 +++++ private/ntos/rtl/alpha/chkstk.s | 131 +++ private/ntos/rtl/alpha/context.c | 330 ++++++ private/ntos/rtl/alpha/debugstb.s | 269 +++++ private/ntos/rtl/alpha/exdsptch.c | 2335 +++++++++++++++++++++++++++++++++++++ private/ntos/rtl/alpha/getcalr.c | 189 +++ private/ntos/rtl/alpha/ghandler.c | 436 +++++++ private/ntos/rtl/alpha/largeint.s | 999 ++++++++++++++++ private/ntos/rtl/alpha/localrtl.c | 160 +++ private/ntos/rtl/alpha/localrtl.h | 82 ++ private/ntos/rtl/alpha/longjmp.s | 200 ++++ private/ntos/rtl/alpha/lzntaxp.s | 485 ++++++++ private/ntos/rtl/alpha/mvmem.s | 1920 ++++++++++++++++++++++++++++++ private/ntos/rtl/alpha/ntcurteb.s | 57 + private/ntos/rtl/alpha/ntrtlalp.h | 123 ++ private/ntos/rtl/alpha/setjmp.s | 148 +++ private/ntos/rtl/alpha/tcmpmem.c | 105 ++ private/ntos/rtl/alpha/tfilmem.c | 79 ++ private/ntos/rtl/alpha/tmovmem.c | 129 ++ private/ntos/rtl/alpha/trampoln.s | 524 +++++++++ private/ntos/rtl/alpha/tzermem.c | 79 ++ private/ntos/rtl/alpha/unwindr.c | 871 ++++++++++++++ private/ntos/rtl/alpha/xcptmisc.s | 501 ++++++++ 24 files changed, 10759 insertions(+) create mode 100644 private/ntos/rtl/alpha/capture.s create mode 100644 private/ntos/rtl/alpha/chandler.c create mode 100644 private/ntos/rtl/alpha/chkstk.s create mode 100644 private/ntos/rtl/alpha/context.c create mode 100644 private/ntos/rtl/alpha/debugstb.s create mode 100644 private/ntos/rtl/alpha/exdsptch.c create mode 100644 private/ntos/rtl/alpha/getcalr.c create mode 100644 private/ntos/rtl/alpha/ghandler.c create mode 100644 private/ntos/rtl/alpha/largeint.s create mode 100644 private/ntos/rtl/alpha/localrtl.c create mode 100644 private/ntos/rtl/alpha/localrtl.h create mode 100644 private/ntos/rtl/alpha/longjmp.s create mode 100644 private/ntos/rtl/alpha/lzntaxp.s create mode 100644 private/ntos/rtl/alpha/mvmem.s create mode 100644 private/ntos/rtl/alpha/ntcurteb.s create mode 100644 private/ntos/rtl/alpha/ntrtlalp.h create mode 100644 private/ntos/rtl/alpha/setjmp.s create mode 100644 private/ntos/rtl/alpha/tcmpmem.c create mode 100644 private/ntos/rtl/alpha/tfilmem.c create mode 100644 private/ntos/rtl/alpha/tmovmem.c create mode 100644 private/ntos/rtl/alpha/trampoln.s create mode 100644 private/ntos/rtl/alpha/tzermem.c create mode 100644 private/ntos/rtl/alpha/unwindr.c create mode 100644 private/ntos/rtl/alpha/xcptmisc.s (limited to 'private/ntos/rtl/alpha') diff --git a/private/ntos/rtl/alpha/capture.s b/private/ntos/rtl/alpha/capture.s new file mode 100644 index 000000000..027ced3f5 --- /dev/null +++ b/private/ntos/rtl/alpha/capture.s @@ -0,0 +1,310 @@ +// TITLE("Capture and Restore Context") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// Copyright (c) 1993 Digital Equipment Corporation +// +// Module Name: +// +// capture.s +// +// Abstract: +// +// This module implements the code necessary to capture and restore +// the context of the caller. +// +// Author: +// +// David N. Cutler (davec) 14-Sep-1990 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +// Thomas Van Baak (tvb) 13-May-1992 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + + SBTTL("Capture Context") +//++ +// +// VOID +// RtlCaptureContext ( +// OUT PCONTEXT ContextRecord +// ) +// +// Routine Description: +// +// This function captures the context of the caller in the specified +// context record. +// +// The context record is assumed to be quadword aligned (since all the +// members of the record are themselves quadwords). +// +// N.B. The stored value of registers a0 and ra will be a side effect of +// having made this call. All other registers will be stored as they +// were when the call to this function was made. +// +// Arguments: +// +// ContextRecord (a0) - Supplies the address of a context record. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlCaptureContext) + +// +// Save all the integer registers. +// + + .set noreorder + .set noat + stq v0, CxIntV0(a0) // 0: store integer register v0 + stq t0, CxIntT0(a0) // 1: store integer registers t0 - t7 + stq t1, CxIntT1(a0) // 2: + stq t2, CxIntT2(a0) // 3: + stq t3, CxIntT3(a0) // 4: + stq t4, CxIntT4(a0) // 5: + stq t5, CxIntT5(a0) // 6: + stq t6, CxIntT6(a0) // 7: + stq t7, CxIntT7(a0) // 8: + + stq s0, CxIntS0(a0) // 9: store integer registers s0 - s5 + stq s1, CxIntS1(a0) // 10: + stq s2, CxIntS2(a0) // 11: + stq s3, CxIntS3(a0) // 12: + stq s4, CxIntS4(a0) // 13: + stq s5, CxIntS5(a0) // 14: + stq fp, CxIntFp(a0) // 15: store integer register fp/s6 + + stq a0, CxIntA0(a0) // 16: store integer registers a0 - a5 + stq a1, CxIntA1(a0) // 17: + stq a2, CxIntA2(a0) // 18: + stq a3, CxIntA3(a0) // 19: + stq a4, CxIntA4(a0) // 20: + stq a5, CxIntA5(a0) // 21: + + stq t8, CxIntT8(a0) // 22: store integer registers t8 - t11 + stq t9, CxIntT9(a0) // 23: + stq t10, CxIntT10(a0) // 24: + stq t11, CxIntT11(a0) // 25: + + stq ra, CxIntRa(a0) // 26: store integer register ra + stq t12, CxIntT12(a0) // 27: store integer register t12 + stq AT, CxIntAt(a0) // 28: store integer register at + stq gp, CxIntGp(a0) // 29: store integer register gp + stq sp, CxIntSp(a0) // 30: store integer register sp + stq zero, CxIntZero(a0) // 31: store integer register zero + +// +// Save all the floating registers, and the floating control register. +// + + stt f0, CxFltF0(a0) // store floating registers f0 - f31 + stt f1, CxFltF1(a0) // + stt f2, CxFltF2(a0) // + stt f3, CxFltF3(a0) // + stt f4, CxFltF4(a0) // + stt f5, CxFltF5(a0) // + stt f6, CxFltF6(a0) // + stt f7, CxFltF7(a0) // + stt f8, CxFltF8(a0) // + stt f9, CxFltF9(a0) // + stt f10, CxFltF10(a0) // + stt f11, CxFltF11(a0) // + stt f12, CxFltF12(a0) // + stt f13, CxFltF13(a0) // + stt f14, CxFltF14(a0) // + stt f15, CxFltF15(a0) // + stt f16, CxFltF16(a0) // + stt f17, CxFltF17(a0) // + stt f18, CxFltF18(a0) // + stt f19, CxFltF19(a0) // + stt f20, CxFltF20(a0) // + stt f21, CxFltF21(a0) // + stt f22, CxFltF22(a0) // + stt f23, CxFltF23(a0) // + stt f24, CxFltF24(a0) // + stt f25, CxFltF25(a0) // + stt f26, CxFltF26(a0) // + stt f27, CxFltF27(a0) // + stt f28, CxFltF28(a0) // + stt f29, CxFltF29(a0) // + stt f30, CxFltF30(a0) // + stt f31, CxFltF31(a0) // + +// +// Save control information and set context flags. +// + + mf_fpcr f0, f0, f0 // get floating point control register + stt f0, CxFpcr(a0) // store it + ldt f0, CxFltF0(a0) // restore original f0 value + stq ra, CxFir(a0) // set continuation address + .set at + .set reorder + +// +// If called from user mode set a known fixed PSR value for user mode. If +// called from kernel mode we are able to execute the privileged call pal +// to get the actual PSR. +// + + ldil v0, PSR_USER_MODE // set constant user processor status + bgt sp, 10f // if sp > 0, call came from user mode + + GET_CURRENT_PROCESSOR_STATUS_REGISTER // get contents of PSR in v0 + +10: stl v0, CxPsr(a0) // store processor status + ldil v0, CONTEXT_FULL // set context control flags + stl v0, CxContextFlags(a0) // store it + ret zero, (ra) // return + + .end RtlCaptureContext + + SBTTL("Restore Context") +//++ +// +// VOID +// RtlpRestoreContext ( +// IN PCONTEXT ContextRecord +// ) +// +// Routine Description: +// +// This function restores the context of the caller to the specified +// context. +// +// The context record is assumed to be quadword aligned (since all the +// members of the record are themselves quadwords). +// +// N.B. This is a special routine that is used by RtlUnwind to restore +// context in the current mode. PSR, t0, and t1 are not restored. +// +// Arguments: +// +// ContextRecord (a0) - Supplies the address of a context record. +// +// Return Value: +// +// None. +// +// N.B. There is no return from this routine. +// +//-- + + LEAF_ENTRY(RtlpRestoreContext) + +// +// If the call is from user mode, then use the continue system service to +// continue execution. Otherwise, restore the context directly since the +// current mode is kernel and threads can't be arbitrarily interrupted. +// + + blt ra, 10f // if ra < 0, kernel mode restore + ldil a1, FALSE // set test alert argument false + bsr ra, ZwContinue // continue execution + +// +// Save the address of the context record and continuation address in +// registers t0 and t1. These registers are not restored. +// + +10: mov a0, t0 // save context record address + ldq t1, CxFir(t0) // get continuation address + +// +// Restore floating control and floating registers f0 - f30. +// + + .set noreorder + .set noat + ldt f0, CxFpcr(t0) // get floating point control register + mt_fpcr f0, f0, f0 // load register + + ldt f0, CxFltF0(t0) // restore floating registers f0 - f30 + ldt f1, CxFltF1(t0) // + ldt f2, CxFltF2(t0) // + ldt f3, CxFltF3(t0) // + ldt f4, CxFltF4(t0) // + ldt f5, CxFltF5(t0) // + ldt f6, CxFltF6(t0) // + ldt f7, CxFltF7(t0) // + ldt f8, CxFltF8(t0) // + ldt f9, CxFltF9(t0) // + ldt f10, CxFltF10(t0) // + ldt f11, CxFltF11(t0) // + ldt f12, CxFltF12(t0) // + ldt f13, CxFltF13(t0) // + ldt f14, CxFltF14(t0) // + ldt f15, CxFltF15(t0) // + ldt f16, CxFltF16(t0) // + ldt f17, CxFltF17(t0) // + ldt f18, CxFltF18(t0) // + ldt f19, CxFltF19(t0) // + ldt f20, CxFltF20(t0) // + ldt f21, CxFltF21(t0) // + ldt f22, CxFltF22(t0) // + ldt f23, CxFltF23(t0) // + ldt f24, CxFltF24(t0) // + ldt f25, CxFltF25(t0) // + ldt f26, CxFltF26(t0) // + ldt f27, CxFltF27(t0) // + ldt f28, CxFltF28(t0) // + ldt f29, CxFltF29(t0) // + ldt f30, CxFltF30(t0) // + +// +// Restore integer registers and continue execution. +// N.B. Integer registers t0 and t1 cannot be restored by this function. +// + + ldq v0, CxIntV0(t0) // restore integer register v0 + ldq t2, CxIntT2(t0) // restore integer registers t2 - t7 + ldq t3, CxIntT3(t0) // + ldq t4, CxIntT4(t0) // + ldq t5, CxIntT5(t0) // + ldq t6, CxIntT6(t0) // + ldq t7, CxIntT7(t0) // + + ldq s0, CxIntS0(t0) // restore integer registers s0 - s5 + ldq s1, CxIntS1(t0) // + ldq s2, CxIntS2(t0) // + ldq s3, CxIntS3(t0) // + ldq s4, CxIntS4(t0) // + ldq s5, CxIntS5(t0) // + ldq fp, CxIntFp(t0) // restore integer register fp/s6 + + ldq a0, CxIntA0(t0) // restore integer registers a0 - a5 + ldq a1, CxIntA1(t0) // + ldq a2, CxIntA2(t0) // + ldq a3, CxIntA3(t0) // + ldq a4, CxIntA4(t0) // + ldq a5, CxIntA5(t0) // + + ldq t8, CxIntT8(t0) // restore integer registers t8 - t11 + ldq t9, CxIntT9(t0) // + ldq t10, CxIntT10(t0) // + ldq t11, CxIntT11(t0) // + + ldq ra, CxIntRa(t0) // restore integer register ra + ldq t12, CxIntT12(t0) // restore integer register t12 + ldq AT, CxIntAt(t0) // restore integer register at + ldq gp, CxIntGp(t0) // restore integer register gp + ldq sp, CxIntSp(t0) // restore integer register sp + + jmp zero, (t1) // continue execution + .set at + .set reorder + + .end RtlpRestoreContext diff --git a/private/ntos/rtl/alpha/chandler.c b/private/ntos/rtl/alpha/chandler.c new file mode 100644 index 000000000..07b6e3c6b --- /dev/null +++ b/private/ntos/rtl/alpha/chandler.c @@ -0,0 +1,297 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + chandler.c + +Abstract: + + This module implements the C specific exception handler that provides + structured condition handling for the C language. + +Author: + + David N. Cutler (davec) 11-Sep-1990 + +Environment: + + Any mode. + +Revision History: + + Thomas Van Baak (tvb) 29-Apr-1992 + + Adapted for Alpha AXP. + +--*/ + +#include "nt.h" + +// +// Define procedure prototypes for exception filter and termination handler +// execution routines defined in jmpuwind.s. +// + +LONG +__C_ExecuteExceptionFilter ( + PEXCEPTION_POINTERS ExceptionPointers, + EXCEPTION_FILTER ExceptionFilter, + ULONG EstablisherFrame + ); + +VOID +__C_ExecuteTerminationHandler ( + BOOLEAN AbnormalTermination, + TERMINATION_HANDLER TerminationHandler, + ULONG EstablisherFrame + ); + +EXCEPTION_DISPOSITION +__C_specific_handler ( + IN struct _EXCEPTION_RECORD *ExceptionRecord, + IN void *EstablisherFrame, + IN OUT struct _CONTEXT *ContextRecord, + IN OUT struct _DISPATCHER_CONTEXT *DispatcherContext + ) + +/*++ + +Routine Description: + + This function scans the scope tables associated with the specified + procedure and calls exception and termination handlers as necessary. + + This language specific exception handler function is called on a + per-frame basis and in two different cases: + + First, the IS_DISPATCHING case, it is called by the exception + dispatcher, RtlDispatchException, via the short assembler routine, + __C_ExecuteHandlerForException, when trying to locate exception + filters within the given frame. + + Second, the IS_UNWINDING case, it is called by the frame unwinder, + RtlUnwind, via the short assembler routine, __C_ExecuteHandlerForUnwind, + when unwinding the stack and trying to locate termination handlers + within the given frame. + +Arguments: + + ExceptionRecord - Supplies a pointer to an exception record. + + EstablisherFrame - Supplies a pointer to frame of the establisher function. + + ContextRecord - Supplies a pointer to a context record. + + DispatcherContext - Supplies a pointer to the exception dispatcher or + unwind dispatcher context. + +Return Value: + + If the exception is handled by one of the exception filter routines, then + there is no return from this routine and RtlUnwind is called. Otherwise, + an exception disposition value of continue execution or continue search is + returned. + +--*/ + +{ + + ULONG ControlPc; + EXCEPTION_FILTER ExceptionFilter; + EXCEPTION_POINTERS ExceptionPointers; + PRUNTIME_FUNCTION FunctionEntry; + ULONG Index; + PSCOPE_TABLE ScopeTable; + ULONG TargetPc; + TERMINATION_HANDLER TerminationHandler; + LONG Value; + + // + // Get the address of where control left the establisher, the address of + // the function table entry that describes the function, and the address of + // the scope table. + // + + ControlPc = DispatcherContext->ControlPc; + FunctionEntry = DispatcherContext->FunctionEntry; + ScopeTable = (PSCOPE_TABLE)(FunctionEntry->HandlerData); + + // + // The scope table HandlerAddress is either the address of an exception + // filter or a termination handler. The C compiler wraps the code in the + // exception filter expression or the termination handler clause within + // an internal C function. The value of the scope table JumpTarget field + // is used to distinguish an exception filter function (JumpTarget non zero) + // from a termination handler function (JumpTarget is zero). + // + + // + // If an unwind is not in progress, then scan the scope table and call + // the appropriate exception filter routines. Otherwise, scan the scope + // table and call the appropriate termination handlers using the target + // PC obtained from the context record. + // + + if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) { + + // + // Set up the ExceptionPointers structure that is passed as the argument + // to the exception filter. It is used by the compiler to implement the + // intrinsic functions exception_code() and exception_info(). + // + + ExceptionPointers.ExceptionRecord = ExceptionRecord; + ExceptionPointers.ContextRecord = ContextRecord; + + // + // Scan the scope table and call the appropriate exception filter + // routines. The scope table entries are known to be sorted by + // increasing EndAddress order. Thus a linear scan will naturally + // hit inner scope exception clauses before outer scope clauses. + // + + for (Index = 0; Index < ScopeTable->Count; Index += 1) { + if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) && + (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress) && + (ScopeTable->ScopeRecord[Index].JumpTarget != 0)) { + + // + // Call the exception filter routine. + // + + ExceptionFilter = + (EXCEPTION_FILTER)ScopeTable->ScopeRecord[Index].HandlerAddress; + Value = __C_ExecuteExceptionFilter(&ExceptionPointers, + ExceptionFilter, + (ULONG)EstablisherFrame); + + // + // If the return value is less than zero, then dismiss the + // exception. Otherwise, if the value is greater than zero, + // then unwind to the target exception handler corresponding + // to the exception filter. Otherwise, continue the search for + // an exception filter. + // + + // + // Exception filters will usually return one of the following + // defines, although the decision below is made only by sign: + // + // #define EXCEPTION_EXECUTE_HANDLER 1 + // #define EXCEPTION_CONTINUE_SEARCH 0 + // #define EXCEPTION_CONTINUE_EXECUTION -1 + // + + if (Value < 0) { + return ExceptionContinueExecution; + + } else if (Value > 0) { + + // + // Set the return value for the unwind to the exception + // code so the exception handler clause can retrieve it + // from v0. This is how GetExceptionCode() is implemented + // in exception handler clauses. + // + + RtlUnwind2(EstablisherFrame, + (PVOID)ScopeTable->ScopeRecord[Index].JumpTarget, + ExceptionRecord, + (PVOID)ExceptionRecord->ExceptionCode, + ContextRecord); + } + } + } + + } else { + + // + // Scan the scope table and call the appropriate termination handler + // routines. + // + + TargetPc = (ULONG)ContextRecord->Fir; + for (Index = 0; Index < ScopeTable->Count; Index += 1) { + if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) && + (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress)) { + + // + // If the target PC is within the same scope the control PC + // is within, then this is an uplevel goto out of an inner try + // scope or a long jump back into a try scope. Terminate the + // scan for termination handlers - because any other handlers + // will be outside the scope of both the goto and its label. + // + // N.B. The target PC can be just beyond the end of the scope, + // in which case it is a leave from the scope. + // + + if ((TargetPc >= ScopeTable->ScopeRecord[Index].BeginAddress) && + (TargetPc <= ScopeTable->ScopeRecord[Index].EndAddress)) { + break; + + } else { + + // + // If the scope table entry describes an exception filter + // and the associated exception handler is the target of + // the unwind, then terminate the scan for termination + // handlers. Otherwise, if the scope table entry describes + // a termination handler, then record the address of the + // end of the scope as the new control PC address and call + // the termination handler. + // + // Recording a new control PC is necessary to ensure that + // termination handlers are called only once even when a + // collided unwind occurs. + // + + if (ScopeTable->ScopeRecord[Index].JumpTarget != 0) { + + // + // try/except - exception filter (JumpTarget != 0). + // After the exception filter is called, the exception + // handler clause is executed by the call to unwind + // above. Having reached this point in the scan of the + // scope tables, any other termination handlers will + // be outside the scope of the try/except. + // + + if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) { + break; + } + + } else { + + // + // try/finally - termination handler (JumpTarget == 0). + // + + // + // Unless the termination handler results in a long + // jump, execution will resume at the instruction after + // the exception handler clause. + // + // ## tvb - I'm still suspicious of the +4 below. + + DispatcherContext->ControlPc = + ScopeTable->ScopeRecord[Index].EndAddress + 4; + TerminationHandler = + (TERMINATION_HANDLER)ScopeTable->ScopeRecord[Index].HandlerAddress; + __C_ExecuteTerminationHandler(TRUE, + TerminationHandler, + (ULONG)EstablisherFrame); + } + } + } + } + } + + // + // Continue search for exception filters or termination handlers. + // + + return ExceptionContinueSearch; +} diff --git a/private/ntos/rtl/alpha/chkstk.s b/private/ntos/rtl/alpha/chkstk.s new file mode 100644 index 000000000..749af86a7 --- /dev/null +++ b/private/ntos/rtl/alpha/chkstk.s @@ -0,0 +1,131 @@ +// TITLE("Runtime Stack Checking") +//++ +// +// Copyright (c) 1991 Microsoft Corporation +// Copyright (c) 1992 Digital Equipment Corporation +// +// Module Name: +// +// chkstk.s +// +// Abstract: +// +// This module implements runtime stack checking. +// +// Author: +// +// David N. Cutler (davec) 14-Mar-1991 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +// Thomas Van Baak (tvb) 7-May-1992 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + + SBTTL("Check Stack") +//++ +// +// ULONG +// _RtlCheckStack ( +// IN ULONG Allocation +// ) +// +// Routine Description: +// +// This function provides runtime stack checking for local allocations +// that are more than a page and for storage dynamically allocated with +// the alloca function. Stack checking consists of probing downward in +// the stack a page at a time. If the current stack commitment is exceeded, +// then the system will automatically attempts to expand the stack. If the +// attempt succeeds, then another page is committed. Otherwise, a stack +// overflow exception is raised. It is the responsibility of the caller to +// handle this exception. +// +// N.B. This routine is called using a non-standard calling sequence since +// it is typically called from within the prologue. The allocation size +// argument is in register t12 and it must be preserved. Register t11 +// may contain the callers saved ra and it must be preserved. The choice +// of these registers is hard-coded into the acc C compiler. Register v0 +// may contain a static link pointer (exception handlers) and so it must +// be preserved. Since this function is called from within the prolog, +// the a' registers must be preserved, as well as all the s' registers. +// Registers t8, t9, and t10 are used by this function and are not +// preserved. +// +// The typical calling sequence from the prologue is: +// +// mov ra, t11 // save return address +// ldil t12, SIZE // set requested stack frame size +// bsr ra, _RtlCheckStack // check stack page allocation +// subq sp, t12, sp // allocate stack frame +// mov t11, ra // restore return address +// +// Arguments: +// +// Allocation (t12) - Supplies the size of the allocation on the stack. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(_RtlCheckStack) + + subq sp, t12, t8 // compute requested new stack address + mov v0, t10 // save v0 since the PALcode uses it +#if defined(NTOS_KERNEL_RUNTIME) + +// +// Running on kernel stack - get stack limit from thread object +// + + GET_CURRENT_THREAD // current thread in v0 + ldl t9, ThStackLimit(v0) + +#else +// +// Running on user stack - get stack limit from thread environment block. +// + +10: GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) put TEB address in v0 + + ldl t9, TeStackLimit(v0) // get low limit of user stack address +#endif + +// +// The requested bottom of the stack is in t8. +// The current low limit of the stack is in t9. +// +// If the new stack address is greater than the current stack limit, then the +// pages have already been allocated, and nothing further needs to be done. +// + + mov t10, v0 // restore v0, no further PAL calls + cmpult t8, t9, t10 // t8=t9, so yes + + ldil t10, ~(PAGE_SIZE - 1) // round down new stack address + and t8, t10, t8 // to next page boundary + +// +// Go down one page, touch one quadword in it, and repeat until we reach the +// new stack limit. +// + +30: lda t9, -PAGE_SIZE(t9) // compute next address to check + stq zero, 0(t9) // probe stack address with a write + cmpeq t8, t9, t10 // t8=t9? at the low limit yet? + beq t10, 30b // if eq [false], more pages to probe + +40: ret zero, (ra) // return + + .end _RtlCheckStack diff --git a/private/ntos/rtl/alpha/context.c b/private/ntos/rtl/alpha/context.c new file mode 100644 index 000000000..dfcf0aa1e --- /dev/null +++ b/private/ntos/rtl/alpha/context.c @@ -0,0 +1,330 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + context.c + +Abstract: + + This module implements user-mode callable context manipulation routines. + +Author: + + Mark Lucovsky (markl) 20-Jun-1989 + +Revision History: + + David N. Cutler (davec) 18-Apr-1990 + + Revise for MIPS environment. + + Thomas Van Baak (tvb) 11-May-1992 + + Adapted for Alpha AXP. + +--*/ + +#include +#include +#include + +VOID +RtlInitializeContext( + IN HANDLE Process, + OUT PCONTEXT Context, + IN PVOID Parameter OPTIONAL, + IN PVOID InitialPc OPTIONAL, + IN PVOID InitialSp OPTIONAL + ) + +/*++ + +Routine Description: + + This function initializes a context structure so that it can be used in + a subsequent call to NtCreateThread. + +Arguments: + + Process - Supplies an open handle to the target process. This argument + is ignored by this function. + + Context - Supplies a pointer to a context record that is to be initialized. + + Parameter - Supplies an initial value for register A0. + + InitialPc - Supplies an initial program counter value. + + InitialSp - Supplies an initial stack pointer value. + +Return Value: + + Raises STATUS_BAD_INITIAL_STACK if the value of InitialSp is not properly + aligned. + + Raises STATUS_BAD_INITIAL_PC if the value of InitialPc is not properly + aligned. + +--*/ + +{ + + // + // Check for proper initial stack and PC alignment. + // + + if (((ULONG)InitialSp & 0xF) != 0) { + RtlRaiseStatus(STATUS_BAD_INITIAL_STACK); + } + if (((ULONG)InitialPc & 0x3) != 0) { + RtlRaiseStatus(STATUS_BAD_INITIAL_PC); + } + + // + // Initialize the integer registers to contain their register number + // (they must be initialized and using register numbers instead of 0 + // can be useful for debugging). Integer registers a0, ra, gp, and sp + // are set later. + // + + Context->IntV0 = 0; + Context->IntT0 = 1; + Context->IntT1 = 2; + Context->IntT2 = 3; + Context->IntT3 = 4; + Context->IntT4 = 5; + Context->IntT5 = 6; + Context->IntT6 = 7; + Context->IntT7 = 8; + Context->IntS0 = 9; + Context->IntS1 = 10; + Context->IntS2 = 11; + Context->IntS3 = 12; + Context->IntS4 = 13; + Context->IntS5 = 14; + Context->IntFp = 15; + Context->IntA1 = 17; + Context->IntA2 = 18; + Context->IntA3 = 19; + Context->IntA4 = 20; + Context->IntA5 = 21; + Context->IntT8 = 22; + Context->IntT9 = 23; + Context->IntT10 = 24; + Context->IntT11 = 25; + Context->IntT12 = 27; + Context->IntAt = 28; + + // + // Initialize the floating point registers to contain the integer value + // of their register number (they must be initialized and using register + // numbers instead of 0 can be useful for debugging). + // + + Context->FltF0 = 0; + Context->FltF1 = 1; + Context->FltF2 = 2; + Context->FltF3 = 3; + Context->FltF4 = 4; + Context->FltF5 = 5; + Context->FltF6 = 6; + Context->FltF7 = 7; + Context->FltF8 = 8; + Context->FltF9 = 9; + Context->FltF10 = 10; + Context->FltF11 = 11; + Context->FltF12 = 12; + Context->FltF13 = 13; + Context->FltF14 = 14; + Context->FltF15 = 15; + Context->FltF16 = 16; + Context->FltF17 = 17; + Context->FltF18 = 18; + Context->FltF19 = 19; + Context->FltF20 = 20; + Context->FltF21 = 21; + Context->FltF22 = 22; + Context->FltF23 = 23; + Context->FltF24 = 24; + Context->FltF25 = 25; + Context->FltF26 = 26; + Context->FltF27 = 27; + Context->FltF28 = 28; + Context->FltF29 = 29; + Context->FltF30 = 30; + Context->FltF31 = 0; + + // + // Initialize the control registers. + // + // Gp: will be set in LdrpInitialize at thread startup. + // Ra: some debuggers compare for 1 as a top-of-stack indication. + // + // N.B. ULONG becomes canonical longword with (ULONGLONG)(LONG) cast. + // + + Context->IntGp = 0; + Context->IntSp = (ULONGLONG)(LONG)InitialSp; + Context->IntRa = 1; + Context->Fir = (ULONGLONG)(LONG)InitialPc; + + // + // Set default Alpha floating point control register values. + // + + Context->Fpcr = (ULONGLONG)0; + ((PFPCR)(&Context->Fpcr))->DynamicRoundingMode = ROUND_TO_NEAREST; + Context->SoftFpcr = (ULONGLONG)0; + + Context->Psr = 0; + Context->ContextFlags = CONTEXT_FULL; + + // + // Set the initial context of the thread in a machine specific way. + // + + Context->IntA0 = (ULONGLONG)(LONG)Parameter; +} + +NTSTATUS +RtlRemoteCall( + HANDLE Process, + HANDLE Thread, + PVOID CallSite, + ULONG ArgumentCount, + PULONG Arguments, + BOOLEAN PassContext, + BOOLEAN AlreadySuspended + ) + +/*++ + +Routine Description: + + This function calls a procedure in another thread/process by using + NtGetContext and NtSetContext. Parameters are passed to the target + procedure via the nonvolatile registers (s0 - s5). + +Arguments: + + Process - Supplies an open handle to the target process. + + Thread - Supplies an open handle to the target thread within the target + process. + + CallSite - Supplies the address of the procedure to call in the target + process. + + ArgumentCount - Supplies the number of 32 bit parameters to pass to the + target procedure. + + Arguments - Supplies a pointer to the array of 32 bit parameters to pass. + + PassContext - Supplies a boolean value that determines whether a parameter + is to be passed that points to a context record. This parameter is + ignored on MIPS and Alpha hosts. + + AlreadySuspended - Supplies a boolean value that determines whether the + target thread is already in a suspended or waiting state. + +Return Value: + + Status - Status value. + +--*/ + +{ + + NTSTATUS Status; + CONTEXT Context; + ULONG Index; + ULONGLONG NewSp; + + if ((ArgumentCount > 6) || + (PassContext && (ArgumentCount > 5))) { + return(STATUS_INVALID_PARAMETER); + } + + // + // If necessary, suspend the target thread before getting the thread's + // current state. + // + + if (AlreadySuspended == FALSE) { + Status = NtSuspendThread(Thread, NULL); + if (NT_SUCCESS(Status) == FALSE) { + return(Status); + } + } + + // + // Get the current state of the target thread. + // + + Context.ContextFlags = CONTEXT_FULL; + Status = NtGetContextThread(Thread, &Context); + if (NT_SUCCESS(Status) == FALSE) { + if (AlreadySuspended == FALSE) { + NtResumeThread(Thread, NULL); + } + return(Status); + } + + if (AlreadySuspended) { + Context.IntV0 = STATUS_ALERTED; + } + + // + // Pass the parameters to the other thread via the non-volatile registers + // s0 - s5. The context record is passed on the stack of the target thread. + // + + NewSp = Context.IntSp - sizeof(CONTEXT); + Status = NtWriteVirtualMemory(Process, (PVOID)NewSp, &Context, + sizeof(CONTEXT), NULL); + if (NT_SUCCESS(Status) == FALSE) { + if (AlreadySuspended == FALSE) { + NtResumeThread(Thread, NULL); + } + return(Status); + } + + // + // N.B. Each ULONG argument is converted to canonical form with the + // (ULONGLONG)(LONG) cast as required by the calling standard. + // + // N.B. Given the PULONG Arguments prototype, this function cannot pass + // quadword arguments (including structures such as LARGE_INTEGER). + // + + Context.IntSp = NewSp; + + if (PassContext) { + Context.IntS0 = NewSp; + for (Index = 0; Index < ArgumentCount; Index += 1) { + (&Context.IntS1)[Index] = (ULONGLONG)(LONG)Arguments[Index]; + } + + } else { + for (Index = 0; Index < ArgumentCount; Index += 1) { + (&Context.IntS0)[Index] = (ULONGLONG)(LONG)Arguments[Index]; + } + } + + // + // Set the address of the target code into FIR and set the thread context + // to cause the target procedure to be executed. + // + // N.B. The PVOID CallSite is stored as a canonical longword in order + // for Fir to be a valid 64-bit address. + // + + Context.Fir = (ULONGLONG)(LONG)CallSite; + Status = NtSetContextThread(Thread, &Context); + if (AlreadySuspended == FALSE) { + NtResumeThread(Thread, NULL); + } + return(Status); +} diff --git a/private/ntos/rtl/alpha/debugstb.s b/private/ntos/rtl/alpha/debugstb.s new file mode 100644 index 000000000..5847eb47f --- /dev/null +++ b/private/ntos/rtl/alpha/debugstb.s @@ -0,0 +1,269 @@ +// TITLE("Debug Support Functions") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// +// Module Name: +// +// debug.s +// +// Abstract: +// +// This module implements functions to support debugging NT. Each +// function executes a specific call pal which is interpreted by the +// kernel debugger. +// +// Author: +// +// Steven R. Wood (stevewo) 3-Aug-1989 +// Joe Notarangelo 24-Jun-1992 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +//-- + +#include "ksalpha.h" + +//++ +// +// VOID +// DbgBreakPoint() +// +// Routine Description: +// +// This function executes a breakpoint instruction. Useful for entering +// the debugger under program control. This breakpoint will always go to +// the kernel debugger if one is installed, otherwise it will go to the +// debug subsystem. +// +// Arguments: +// +// None. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(DbgBreakPoint) + + BREAK_DEBUG_STOP // debug stop breakpoint + ret zero, (ra) // return + + .end DbgBreakPoint + +//++ +// +// VOID +// DbgBreakPointWithStatus( +// IN ULONG Status +// ) +// +// Routine Description: +// +// This function executes a breakpoint instruction. Useful for entering +// the debugger under program control. This breakpoint will always go to +// the kernel debugger if one is installed, otherwise it will go to the +// debug subsystem. This function is identical to DbgBreakPoint, except +// that it takes an argument which the debugger can see. +// +// Arguments: +// +// A status code. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(DbgBreakPointWithStatus) + + + ALTERNATE_ENTRY(RtlpBreakWithStatusInstruction) + BREAK_DEBUG_STOP // debug stop breakpoint + ret zero, (ra) // return + + .end DbgBreakPointWithStatus + +//++ +// +// VOID +// DbgUserBreakPoint() +// +// Routine Description: +// +// This function executes a breakpoint instruction. Useful for entering +// the debug subsystem under program control. The kernel debug will ignore +// this breakpoint since it will not find the instruction address in its +// breakpoint table. +// +// Arguments: +// +// None. +// +// Return Value: +// +// None. +// +//-- + LEAF_ENTRY(DbgUserBreakPoint) + + BREAK // issue user breakpoint + ret zero, (ra) // return + + .end DbgUserBreakPoint + +//++ +// +// NTSTATUS +// DebugPrint( +// IN PSTRING Output +// ) +// +// Routine Description: +// +// This function executes a debug print breakpoint. +// +// Arguments: +// +// Output (a0) - Supplies a pointer to the output string descriptor. +// +// Return Value: +// +// Status code. STATUS_SUCCESS if debug print happened. +// STATUS_BREAKPOINT if user typed a Control-C during print. +// STATUS_DEVICE_NOT_CONNECTED if kernel debugger not present. +// +//-- + +#if DEVL + + LEAF_ENTRY(DebugPrint) + + ldwu a1, StrLength(a0) // set length of output string + ldl a0, StrBuffer(a0) // set address of output string + BREAK_DEBUG_PRINT // execute a debug print breakpoint + ret zero, (ra) // return + .end DebugPrint + +#endif + +//++ +// +// ULONG +// DebugPrompt( +// IN PSTRING Output, +// IN PSTRING Input +// ) +// +// Routine Description: +// +// This function executes a debug prompt breakpoint. +// +// Arguments: +// +// Output (a0) - Supplies a pointer to the output string descriptor. +// +// Input (a1) - Supplies a pointer to the input string descriptor. +// +// Return Value: +// +// The length of the input string is returned as the function value. +// +//-- + +#if DEVL + + LEAF_ENTRY(DebugPrompt) + + ldwu a3,StrMaximumLength(a1) // set maximum length of input string + ldl a2,StrBuffer(a1) // set address of input string + ldwu a1,StrLength(a0) // set length of output string + ldl a0,StrBuffer(a0) // set address of output string + BREAK_DEBUG_PROMPT + ret zero, (ra) + + .end DebugPrompt + +#endif + + +//++ +// +// VOID +// DebugLoadImageSymbols( +// IN PSTRING ImagePathName, +// IN PKD_SYMBOLS_INFO SymbolInfo +// ) +// +// Routine Description: +// +// This function calls the kernel debugger to load the symbol +// table for the specified image. +// +// Arguments: +// +// ImagePathName - specifies the fully qualified path name of the image +// file that has been loaded into an NT address space. +// +// SymbolInfo - information captured from header of image file. +// +// Return Value: +// +// None. +// +//-- + +#if DEVL + + LEAF_ENTRY(DebugLoadImageSymbols) + + BREAK_DEBUG_LOAD_SYMBOLS + ret zero, (ra) + + .end DebugLoadImageSymbols + +#endif + +//++ +// +// VOID +// DebugUnLoadImageSymbols( +// IN PSTRING ImagePathName, +// IN PKD_SYMBOLS_INFO SymbolInfo +// ) +// +// Routine Description: +// +// This function calls the kernel debugger to unload the symbol +// table for the specified image. +// +// Arguments: +// +// ImagePathName - specifies the fully qualified path name of the image +// file that has been unloaded from an NT address space. +// +// SymbolInfo - information captured from header of image file. +// +// Return Value: +// +// None. +// +//-- + +#if DEVL + + LEAF_ENTRY(DebugUnLoadImageSymbols) + + BREAK_DEBUG_UNLOAD_SYMBOLS + ret zero, (ra) + + .end DebugUnLoadImageSymbols + +#endif diff --git a/private/ntos/rtl/alpha/exdsptch.c b/private/ntos/rtl/alpha/exdsptch.c new file mode 100644 index 000000000..298073e6a --- /dev/null +++ b/private/ntos/rtl/alpha/exdsptch.c @@ -0,0 +1,2335 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + exdsptch.c + +Abstract: + + This module implements the dispatching of exceptions and the unwinding of + procedure call frames. + +Author: + + David N. Cutler (davec) 11-Sep-1990 + +Environment: + + Any mode. + +Revision History: + + Thomas Van Baak (tvb) 13-May-1992 + + Adapted for Alpha AXP. + +--*/ + +#include "ntrtlp.h" + +// +// Define local macros. +// +// Raise noncontinuable exception with associated exception record. +// + +#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \ + EXCEPTION_RECORD ExceptionRecordn; \ + \ + ExceptionRecordn.ExceptionCode = Status; \ + ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \ + ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \ + ExceptionRecordn.NumberParameters = 0; \ + RtlRaiseException(&ExceptionRecordn); \ + } + +// +// The low 2 bits of ExceptionHandler are flags bits and not part of the +// exception handler address. +// + +#define IS_HANDLER_DEFINED(FunctionEntry) \ + (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0) + + +#if DBG + +// +// Maintain a short history of PC's for malformed function table errors. +// + +#define PC_HISTORY_DEPTH 4 + +// +// Definition of global flag to debug/validate exception handling. +// See ntrtlalp.h for the bit definitions in this flag word. +// + +ULONG RtlDebugFlags = 0; + +#endif + +#define Virtual VirtualFramePointer +#define Real RealFramePointer + +// +// Define private function prototypes. +// + +VOID +RtlpRaiseException ( + IN PEXCEPTION_RECORD ExceptionRecord + ); + +VOID +RtlpRaiseStatus ( + IN NTSTATUS Status + ); + +ULONG +RtlpVirtualUnwind ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PULONG EstablisherFrame + ); + +BOOLEAN +RtlDispatchException ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PCONTEXT ContextRecord + ) + +/*++ + +Routine Description: + + This function attempts to dispatch an exception to a frame based + handler by searching backwards through the stack based call frames. + The search begins with the frame specified in the context record and + continues backward until either a handler is found that handles the + exception, the stack is found to be invalid (i.e., out of limits or + unaligned), or the end of the call hierarchy is reached. + + As each frame is encountered, the PC where control left the corresponding + function is determined and used to lookup exception handler information + in the runtime function table built by the linker. If the respective + routine has an exception handler, then the handler is called. If the + handler does not handle the exception, then the prologue of the routine + is executed backwards to "unwind" the effect of the prologue and then + the next frame is examined. + +Arguments: + + ExceptionRecord - Supplies a pointer to an exception record. + + ContextRecord - Supplies a pointer to a context record. + +Return Value: + + If the exception is handled by one of the frame based handlers, then + a value of TRUE is returned. Otherwise a value of FALSE is returned. + +--*/ + +{ + + CONTEXT ContextRecord1; + ULONG ControlPc; +#if DBG + ULONG ControlPcHistory[PC_HISTORY_DEPTH]; + ULONG ControlPcHistoryIndex = 0; +#endif + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + FRAME_POINTERS EstablisherFrame; + ULONG ExceptionFlags; +#if DBG + LONG FrameDepth = 0; +#endif + PRUNTIME_FUNCTION FunctionEntry; + ULONG HighLimit; + BOOLEAN InFunction; + ULONG LowLimit; + ULONG NestedFrame; + ULONG NextPc; + + // + // Get current stack limits, copy the context record, get the initial + // PC value, capture the exception flags, and set the nested exception + // frame pointer. + // + // The initial PC value is obtained from ExceptionAddress rather than + // from ContextRecord.Fir since some Alpha exceptions are asynchronous. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT)); + ControlPc = (ULONG)ExceptionRecord->ExceptionAddress; +#if DBG + if ((ULONG)ExceptionRecord->ExceptionAddress != (ULONG)ContextRecord->Fir) { + DbgPrint("RtlDispatchException: ExceptionAddress = %lx, Fir = %lx\n", + (ULONG)ExceptionRecord->ExceptionAddress, (ULONG)ContextRecord->Fir); + } +#endif + ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE; + NestedFrame = 0; +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) { + DbgPrint("\nRtlDispatchException(ExceptionRecord = %lx, ContextRecord = %lx)\n", + ExceptionRecord, ContextRecord); + DbgPrint("RtlDispatchException: ControlPc = %lx, ExceptionRecord->ExceptionCode = %lx\n", + ControlPc, ExceptionRecord->ExceptionCode); + } +#endif + + // + // Start with the frame specified by the context record and search + // backwards through the call frame hierarchy attempting to find an + // exception handler that will handle the exception. + // + + do { +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlDispatchException: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n", + FrameDepth, ContextRecord1.IntSp, ControlPc); + FrameDepth -= 1; + } +#endif + + // + // Lookup the function table entry using the point at which control + // left the procedure. + // + + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + + // + // If there is a function table entry for the routine, then virtually + // unwind to the caller of the current routine to obtain the virtual + // frame pointer of the establisher and check if there is an exception + // handler for the frame. + // + + if (FunctionEntry != NULL) { + NextPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + + // + // If the virtual frame pointer is not within the specified stack + // limits or the virtual frame pointer is unaligned, then set the + // stack invalid flag in the exception record and return exception + // not handled. Otherwise, check if the current routine has an + // exception handler. + // + + if ((EstablisherFrame.Virtual < LowLimit) || + (EstablisherFrame.Virtual > HighLimit) || + ((EstablisherFrame.Virtual & 0xF) != 0)) { +#if DBG + DbgPrint("\n****** Warning - stack invalid (exception).\n"); + DbgPrint(" EstablisherFrame.Virtual = %08lx, EstablisherFrame.Real = %08lx\n", + EstablisherFrame.Virtual, EstablisherFrame.Real); + DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n", + LowLimit, HighLimit); + DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n", + NextPc, ControlPc); + DbgPrint(" Now setting EXCEPTION_STACK_INVALID flag.\n"); +#endif + ExceptionFlags |= EXCEPTION_STACK_INVALID; + break; + + } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) { + + ULONG Index; +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlDispatchException: ExceptionHandler = %lx, HandlerData = %lx\n", + FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData); + } +#endif + + // + // The frame has an exception handler. The handler must be + // executed by calling another routine that is written in + // assembler. This is required because up level addressing + // of the handler information is required when a nested + // exception is encountered. + // + + DispatcherContext.ControlPc = ControlPc; + DispatcherContext.FunctionEntry = FunctionEntry; + DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual; + DispatcherContext.ContextRecord = ContextRecord; + ExceptionRecord->ExceptionFlags = ExceptionFlags; + + if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { + Index = RtlpLogExceptionHandler( + ExceptionRecord, + ContextRecord, + ControlPc, + FunctionEntry, + sizeof(RUNTIME_FUNCTION)); + } + +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlDispatchException: calling RtlpExecuteHandlerForException, ControlPc = %lx\n", ControlPc); + } +#endif + Disposition = + RtlpExecuteHandlerForException(ExceptionRecord, + EstablisherFrame.Virtual, + ContextRecord, + &DispatcherContext, + FunctionEntry->ExceptionHandler); +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlDispatchException: RtlpExecuteHandlerForException returned Disposition = %lx\n", Disposition); + } +#endif + + if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { + RtlpLogLastExceptionDisposition(Index, Disposition); + } + + ExceptionFlags |= + (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE); + + // + // If the current scan is within a nested context and the frame + // just examined is the end of the nested region, then clear + // the nested context frame and the nested exception flag in + // the exception flags. + // + + if (NestedFrame == EstablisherFrame.Virtual) { + ExceptionFlags &= (~EXCEPTION_NESTED_CALL); + NestedFrame = 0; + } + + // + // Case on the handler disposition. + // + + switch (Disposition) { + + // + // The disposition is to continue execution. + // + // If the exception is not continuable, then raise the + // exception STATUS_NONCONTINUABLE_EXCEPTION. Otherwise + // return exception handled. + // + + case ExceptionContinueExecution : + if ((ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0) { + RAISE_EXCEPTION(STATUS_NONCONTINUABLE_EXCEPTION, ExceptionRecord); + + } else { +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) { + DbgPrint("RtlDispatchException: returning TRUE\n"); + } +#endif + return TRUE; + } + + // + // The disposition is to continue the search. + // + // Get next frame address and continue the search. + // + + case ExceptionContinueSearch : + break; + + // + // The disposition is nested exception. + // + // Set the nested context frame to the establisher frame + // address and set the nested exception flag in the + // exception flags. + // + + case ExceptionNestedException : + ExceptionFlags |= EXCEPTION_NESTED_CALL; + if (DispatcherContext.EstablisherFrame > NestedFrame) { + NestedFrame = DispatcherContext.EstablisherFrame; + } + + break; + + // + // All other disposition values are invalid. + // + // Raise invalid disposition exception. + // + + default : + RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); + } + } + + } else { + + // + // Set point at which control left the previous routine. + // + + NextPc = (ULONG)ContextRecord1.IntRa - 4; + + // + // If the next control PC is the same as the old control PC, then + // the function table is not correctly formed. + // + + if (NextPc == ControlPc) { +#if DBG + ULONG Count; + DbgPrint("\n****** Warning - malformed function table (exception).\n"); + DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc); + for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) { + if (ControlPcHistoryIndex > 0) { + ControlPcHistoryIndex -= 1; + ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH]; + DbgPrint(", %08lx", ControlPc); + } + } + DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n"); +#endif + break; + } + } + + // + // Set point at which control left the previous routine. + // + +#if DBG + ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc; + ControlPcHistoryIndex += 1; +#endif + ControlPc = NextPc; + + } while ((ULONG)ContextRecord1.IntSp < HighLimit); + + // + // Set final exception flags and return exception not handled. + // + + ExceptionRecord->ExceptionFlags = ExceptionFlags; +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) { + DbgPrint("RtlDispatchException: returning FALSE\n"); + } +#endif + return FALSE; +} + +PRUNTIME_FUNCTION +RtlLookupFunctionEntry ( + IN ULONG ControlPc + ) + +/*++ + +Routine Description: + + This function searches the currently active function tables for an entry + that corresponds to the specified PC value. + +Arguments: + + ControlPc - Supplies the address of an instruction within the specified + function. + +Return Value: + + If there is no entry in the function table for the specified PC, then + NULL is returned. Otherwise, the address of the function table entry + that corresponds to the specified PC is returned. + +--*/ + +{ + + PRUNTIME_FUNCTION FunctionEntry; + PRUNTIME_FUNCTION FunctionTable; + ULONG SizeOfExceptionTable; + LONG High; + PVOID ImageBase; + LONG Low; + LONG Middle; + + // + // Search for the image that includes the specified PC value. + // + + ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase); +#if DBG + if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) { + DbgPrint("RtlLookupFunctionEntry(ControlPc = %lx) ImageBase = %lx\n", + ControlPc, ImageBase); + } +#endif + + // + // If an image is found that includes the specified PC, then locate the + // function table for the image. + // + + if (ImageBase != NULL) { + FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData( + ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, + &SizeOfExceptionTable); +#if DBG + if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) { + DbgPrint("RtlLookupFunctionEntry: FunctionTable = %lx, SizeOfExceptionTable = %lx\n", + FunctionTable, SizeOfExceptionTable); + } +#endif + + // + // If a function table is located, then search the function table + // for a function table entry for the specified PC. + // + + if (FunctionTable != NULL) { + + // + // Initialize search indicies. + // + + Low = 0; + High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1; + + // + // Perform binary search on the function table for a function table + // entry that subsumes the specified PC. + // + + while (High >= Low) { + + // + // Compute next probe index and test entry. If the specified PC + // is greater than of equal to the beginning address and less + // than the ending address of the function table entry, then + // return the address of the function table entry. Otherwise, + // continue the search. + // + + Middle = (Low + High) >> 1; + FunctionEntry = &FunctionTable[Middle]; + if (ControlPc < FunctionEntry->BeginAddress) { + High = Middle - 1; + + } else if (ControlPc >= FunctionEntry->EndAddress) { + Low = Middle + 1; + + } else { +#if DBG + if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) { + DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress); + DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress); + DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress); + DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler); + DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData); + } +#endif + // + // The capability exists for more than one function entry + // to map to the same function. This permits a function to + // have (within reason) discontiguous code segment(s). If + // PrologEndAddress is out of range, it is re-interpreted + // as a pointer to the primary function table entry for + // that function. + // + + if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) || + (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) { + FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress; +#if DBG + if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) { + DbgPrint("RtlLookupFunctionEntry: ** indirect function entry **\n"); + DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress); + DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress); + DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress); + DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler); + DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData); + } +#endif + } +#if DBG + if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) { + DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = %lx\n", + FunctionEntry); + } +#endif + return FunctionEntry; + } + } + } + } + + // + // A function table entry for the specified PC was not found. + // + +#if DBG + if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) { + DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = NULL\n"); + } +#endif + return NULL; +} + +VOID +RtlRaiseException ( + IN PEXCEPTION_RECORD ExceptionRecord + ) + +/*++ + +Routine Description: + + This function raises a software exception by building a context record + and calling the raise exception system service. + + N.B. This routine is a shell routine that simply calls another routine + to do the real work. The reason this is done is to avoid a problem + in try/finally scopes where the last statement in the scope is a + call to raise an exception. + +Arguments: + + ExceptionRecord - Supplies a pointer to an exception record. + +Return Value: + + None. + +--*/ + +{ + +#if DBG + if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) { + DbgPrint("RtlRaiseException(ExceptionRecord = %lx) Status = %lx\n", + ExceptionRecord, ExceptionRecord->ExceptionCode); + } +#endif + RtlpRaiseException(ExceptionRecord); + return; +} + +VOID +RtlpRaiseException ( + IN PEXCEPTION_RECORD ExceptionRecord + ) + +/*++ + +Routine Description: + + This function raises a software exception by building a context record + and calling the raise exception system service. + +Arguments: + + ExceptionRecord - Supplies a pointer to an exception record. + +Return Value: + + None. + +--*/ + +{ + + ULONG ControlPc; + CONTEXT ContextRecord; + FRAME_POINTERS EstablisherFrame; + PRUNTIME_FUNCTION FunctionEntry; + BOOLEAN InFunction; + ULONG NextPc; + NTSTATUS Status; + + // + // Capture the current context, virtually unwind to the caller of this + // routine, set the fault instruction address to that of the caller, and + // call the raise exception system service. + // + + RtlCaptureContext(&ContextRecord); + ControlPc = (ULONG)ContextRecord.IntRa - 4; + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + NextPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4; + ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Fir; + Status = ZwRaiseException(ExceptionRecord, &ContextRecord, TRUE); + + // + // There should never be a return from this system service unless + // there is a problem with the argument list itself. Raise another + // exception specifying the status value returned. + // + + RtlRaiseStatus(Status); + return; +} + +VOID +RtlRaiseStatus ( + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This function raises an exception with the specified status value. The + exception is marked as noncontinuable with no parameters. + + N.B. This routine is a shell routine that simply calls another routine + to do the real work. The reason this is done is to avoid a problem + in try/finally scopes where the last statement in the scope is a + call to raise an exception. + +Arguments: + + Status - Supplies the status value to be used as the exception code + for the exception that is to be raised. + +Return Value: + + None. + +--*/ + +{ + +#if DBG + if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) { + DbgPrint("RtlRaiseStatus(Status = %lx)\n", Status); + } +#endif + RtlpRaiseStatus(Status); + return; +} + +VOID +RtlpRaiseStatus ( + IN NTSTATUS Status + ) + +/*++ + +Routine Description: + + This function raises an exception with the specified status value. The + exception is marked as noncontinuable with no parameters. + +Arguments: + + Status - Supplies the status value to be used as the exception code + for the exception that is to be raised. + +Return Value: + + None. + +--*/ + +{ + + ULONG ControlPc; + CONTEXT ContextRecord; + FRAME_POINTERS EstablisherFrame; + EXCEPTION_RECORD ExceptionRecord; + PRUNTIME_FUNCTION FunctionEntry; + BOOLEAN InFunction; + ULONG NextPc; + + // + // Construct an exception record. + // + + ExceptionRecord.ExceptionCode = Status; + ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL; + ExceptionRecord.NumberParameters = 0; + ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + + // + // Capture the current context, virtually unwind to the caller of this + // routine, set the fault instruction address to that of the caller, and + // call the raise exception system service. + // + + RtlCaptureContext(&ContextRecord); + ControlPc = (ULONG)ContextRecord.IntRa - 4; + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + NextPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4; + ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Fir; + Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, TRUE); + + // + // There should never be a return from this system service unless + // there is a problem with the argument list itself. Raise another + // exception specifying the status value returned. + // + + RtlRaiseStatus(Status); + return; +} + +VOID +RtlUnwind ( + IN PVOID TargetFrame OPTIONAL, + IN PVOID TargetIp OPTIONAL, + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN PVOID ReturnValue + ) + +/*++ + +Routine Description: + + This function initiates an unwind of procedure call frames. The machine + state at the time of the call to unwind is captured in a context record + and the unwinding flag is set in the exception flags of the exception + record. If the TargetFrame parameter is not specified, then the exit unwind + flag is also set in the exception flags of the exception record. A backward + scan through the procedure call frames is then performed to find the target + of the unwind operation. + + As each frame is encounter, the PC where control left the corresponding + function is determined and used to lookup exception handler information + in the runtime function table built by the linker. If the respective + routine has an exception handler, then the handler is called. + + N.B. This routine is provided for backward compatibility with release 1. + +Arguments: + + TargetFrame - Supplies an optional pointer to the call frame that is the + target of the unwind. If this parameter is not specified, then an exit + unwind is performed. + + TargetIp - Supplies an optional instruction address that specifies the + continuation address of the unwind. This address is ignored if the + target frame parameter is not specified. + + ExceptionRecord - Supplies an optional pointer to an exception record. + + ReturnValue - Supplies a value that is to be placed in the integer + function return register just before continuing execution. + +Return Value: + + None. + +--*/ + +{ + CONTEXT ContextRecord; + + + // + // Call real unwind routine specifying a context record as an + // extra argument. + // + + RtlUnwind2(TargetFrame, + TargetIp, + ExceptionRecord, + ReturnValue, + &ContextRecord); + + return; +} + +VOID +RtlUnwind2 ( + IN PVOID TargetFrame OPTIONAL, + IN PVOID TargetIp OPTIONAL, + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN PVOID ReturnValue, + IN PCONTEXT ContextRecord + ) + +/*++ + +Routine Description: + + This function initiates an unwind of procedure call frames. The machine + state at the time of the call to unwind is captured in a context record + and the unwinding flag is set in the exception flags of the exception + record. If the TargetFrame parameter is not specified, then the exit unwind + flag is also set in the exception flags of the exception record. A backward + scan through the procedure call frames is then performed to find the target + of the unwind operation. + + As each frame is encounter, the PC where control left the corresponding + function is determined and used to lookup exception handler information + in the runtime function table built by the linker. If the respective + routine has an exception handler, then the handler is called. + + N.B. This routine is provided for backward compatibility with release 1. + +Arguments: + + TargetFrame - Supplies an optional pointer to the call frame that is the + target of the unwind. If this parameter is not specified, then an exit + unwind is performed. + + TargetIp - Supplies an optional instruction address that specifies the + continuation address of the unwind. This address is ignored if the + target frame parameter is not specified. + + ExceptionRecord - Supplies an optional pointer to an exception record. + + ReturnValue - Supplies a value that is to be placed in the integer + function return register just before continuing execution. + +Return Value: + + None. + +--*/ + +{ + ULONG ControlPc; +#if DBG + ULONG ControlPcHistory[PC_HISTORY_DEPTH]; + ULONG ControlPcHistoryIndex = 0; +#endif + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + FRAME_POINTERS EstablisherFrame; + ULONG ExceptionFlags; + EXCEPTION_RECORD ExceptionRecord1; +#if DBG + LONG FrameDepth = 0; +#endif + PRUNTIME_FUNCTION FunctionEntry; + ULONG HighLimit; + BOOLEAN InFunction; + ULONG LowLimit; + ULONG NextPc; + +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("\nRtlUnwind(TargetFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n", + TargetFrame, TargetIp, ReturnValue); + } +#endif + // + // Get current stack limits, capture the current context, virtually + // unwind to the caller of this routine, get the initial PC value, and + // set the unwind target address. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + RtlCaptureContext(ContextRecord); + ControlPc = (ULONG)ContextRecord->IntRa - 4; + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + NextPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + ControlPc = NextPc; + ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp; + + // + // If an exception record is not specified, then build a local exception + // record for use in calling exception handlers during the unwind operation. + // + + if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { + ExceptionRecord = &ExceptionRecord1; + ExceptionRecord1.ExceptionCode = STATUS_UNWIND; + ExceptionRecord1.ExceptionRecord = NULL; + ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc; + ExceptionRecord1.NumberParameters = 0; + } + + // + // If the target frame of the unwind is specified, then a normal unwind + // is being performed. Otherwise, an exit unwind is being performed. + // + + ExceptionFlags = EXCEPTION_UNWINDING; + if (ARGUMENT_PRESENT(TargetFrame) == FALSE) { + ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND; + } + + // + // Scan backward through the call frame hierarchy and call exception + // handlers until the target frame of the unwind is reached. + // + + do { +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwind: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n", + FrameDepth, ContextRecord->IntSp, ControlPc); + FrameDepth -= 1; + } +#endif + + // + // Lookup the function table entry using the point at which control + // left the procedure. + // + + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + + // + // If there is a function table entry for the routine, then virtually + // unwind to the caller of the routine to obtain the virtual frame + // pointer of the establisher, but don't update the context record. + // + + if (FunctionEntry != NULL) { + NextPc = RtlpVirtualUnwind(ControlPc, + FunctionEntry, + ContextRecord, + &InFunction, + (PULONG)&EstablisherFrame); + + // + // If the virtual frame pointer is not within the specified stack + // limits, the virtual frame pointer is unaligned, or the target + // frame is below the virtual frame and an exit unwind is not being + // performed, then raise the exception STATUS_BAD_STACK. Otherwise, + // check to determine if the current routine has an exception + // handler. + // + + if ((EstablisherFrame.Virtual < LowLimit) || + (EstablisherFrame.Virtual > HighLimit) || + ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && + ((ULONG)TargetFrame < EstablisherFrame.Virtual)) || + ((EstablisherFrame.Virtual & 0xF) != 0)) { +#if DBG + DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n"); + DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n", + EstablisherFrame.Virtual, EstablisherFrame.Real); + DbgPrint(" TargetFrame = %08lx\n", TargetFrame); + if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && + ((ULONG)TargetFrame < EstablisherFrame.Virtual)) { + DbgPrint(" TargetFrame is below EstablisherFrame!\n"); + } + DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n", + (ULONG)ContextRecord->IntSp); + DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n", + LowLimit, HighLimit); + DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n", + NextPc, ControlPc); + DbgPrint(" Now raising STATUS_BAD_STACK exception.\n"); +#endif + RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord); + + } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) { +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlUnwind: ExceptionHandler = %lx, HandlerData = %lx\n", + FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData); +} +#endif + + // + // The frame has an exception handler. + // + // The control PC, establisher frame pointer, the address + // of the function table entry, and the address of the + // context record are all stored in the dispatcher context. + // This information is used by the unwind linkage routine + // and can be used by the exception handler itself. + // + // A linkage routine written in assembler is used to actually + // call the actual exception handler. This is required by the + // exception handler that is associated with the linkage + // routine so it can have access to two sets of dispatcher + // context when it is called. + // + + DispatcherContext.ControlPc = ControlPc; + DispatcherContext.FunctionEntry = FunctionEntry; + DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual; + DispatcherContext.ContextRecord = ContextRecord; + + // + // Call the exception handler. + // + + do { + + // + // If the establisher frame is the target of the unwind + // operation, then set the target unwind flag. + // + + if ((ULONG)TargetFrame == EstablisherFrame.Virtual) { + ExceptionFlags |= EXCEPTION_TARGET_UNWIND; + } + + ExceptionRecord->ExceptionFlags = ExceptionFlags; + + // + // Set the specified return value in case the exception + // handler directly continues execution. + // + + ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue; +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwind: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc); + } +#endif + Disposition = + RtlpExecuteHandlerForUnwind(ExceptionRecord, + EstablisherFrame.Virtual, + ContextRecord, + &DispatcherContext, + FunctionEntry->ExceptionHandler); +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwind: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition); + } +#endif + + // + // Clear target unwind and collided unwind flags. + // + + ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND | + EXCEPTION_TARGET_UNWIND); + + // + // Case on the handler disposition. + // + + switch (Disposition) { + + // + // The disposition is to continue the search. + // + // If the target frame has not been reached, then + // virtually unwind to the caller of the current + // routine, update the context record, and continue + // the search for a handler. + // + + case ExceptionContinueSearch : + if (EstablisherFrame.Virtual != (ULONG)TargetFrame) { + NextPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + } + + break; + + // + // The disposition is collided unwind. + // + // Set the target of the current unwind to the context + // record of the previous unwind, and reexecute the + // exception handler from the collided frame with the + // collided unwind flag set in the exception record. + // + + case ExceptionCollidedUnwind : + ControlPc = DispatcherContext.ControlPc; + FunctionEntry = DispatcherContext.FunctionEntry; + ContextRecord = DispatcherContext.ContextRecord; + ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp; + ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; + EstablisherFrame.Virtual = DispatcherContext.EstablisherFrame; + break; + + // + // All other disposition values are invalid. + // + // Raise invalid disposition exception. + // + + default : + RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); + } + + } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0); + } else { + + // + // Virtually unwind to the caller of the current routine and + // update the context record. + // + + if (EstablisherFrame.Virtual != (ULONG)TargetFrame) { + NextPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + } + } + + } else { + + // + // Set point at which control left the previous routine. + // + + NextPc = (ULONG)ContextRecord->IntRa - 4; + + // + // If the next control PC is the same as the old control PC, then + // the function table is not correctly formed. + // + + if (NextPc == ControlPc) { +#if DBG + ULONG Count; + DbgPrint("\n****** Warning - malformed function table (unwind).\n"); + DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc); + for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) { + if (ControlPcHistoryIndex > 0) { + ControlPcHistoryIndex -= 1; + ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH]; + DbgPrint(", %08lx", ControlPc); + } + } + DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n"); + DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n"); +#endif + RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); + } + } + + // + // Set point at which control left the previous routine. + // + +#if DBG + ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc; + ControlPcHistoryIndex += 1; +#endif + ControlPc = NextPc; + + } while ((EstablisherFrame.Virtual < HighLimit) && + (EstablisherFrame.Virtual != (ULONG)TargetFrame)); + + // + // If the establisher stack pointer is equal to the target frame + // pointer, then continue execution. Otherwise, an exit unwind was + // performed or the target of the unwind did not exist and the + // debugger and subsystem are given a second chance to handle the + // unwind. + // + + if (EstablisherFrame.Virtual == (ULONG)TargetFrame) { + ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue; +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("RtlUnwind: finished unwinding, and calling RtlpRestoreContext(%lx)\n",ContextRecord); + } +#endif + RtlpRestoreContext(ContextRecord); + + } else { +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("RtlUnwind: finished unwinding, but calling ZwRaiseException\n"); + } +#endif + ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); + } + +} + +#if DBG +// +// Define an array of symbolic names for the integer registers. +// + +PCHAR RtlpIntegerRegisterNames[32] = { + "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", // 0 - 7 + "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", // 8 - 15 + "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", // 16 - 23 + "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", // 24 - 31 +}; + +// +// This function disassembles the instruction at the given address. It is +// only used for debugging and recognizes only those few instructions that +// are relevant during reverse execution of the prologue by virtual unwind. +// + +VOID +_RtlpDebugDisassemble ( + IN ULONG ControlPc, + IN PCONTEXT ContextRecord + ) +{ + UCHAR Comments[50]; + PULONGLONG FloatingRegister; + ULONG Function; + ULONG Hint; + ULONG Literal8; + ALPHA_INSTRUCTION Instruction; + PULONGLONG IntegerRegister; + LONG Offset16; + UCHAR Operands[20]; + ULONG Opcode; + PCHAR OpName; + ULONG Ra; + ULONG Rb; + ULONG Rc; + PCHAR RaName; + PCHAR RbName; + PCHAR RcName; + + if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND_DETAIL) { + Instruction.Long = *((PULONG)ControlPc); + Hint = Instruction.Jump.Hint; + Literal8 = Instruction.OpLit.Literal; + Offset16 = Instruction.Memory.MemDisp; + Opcode = Instruction.Memory.Opcode; + Ra = Instruction.OpReg.Ra; + RaName = RtlpIntegerRegisterNames[Ra]; + Rb = Instruction.OpReg.Rb; + RbName = RtlpIntegerRegisterNames[Rb]; + Rc = Instruction.OpReg.Rc; + RcName = RtlpIntegerRegisterNames[Rc]; + + IntegerRegister = &ContextRecord->IntV0; + FloatingRegister = &ContextRecord->FltF0; + + OpName = NULL; + switch (Opcode) { + case JMP_OP : + if (Instruction.Jump.Function == RET_FUNC) { + OpName = "ret"; + sprintf(Operands, "%s, (%s), %04lx", RaName, RbName, Hint); + sprintf(Comments, "%s = %Lx", RbName, IntegerRegister[Rb]); + } + break; + + case LDAH_OP : + case LDA_OP : + case STQ_OP : + if (Opcode == LDA_OP) { + OpName = "lda"; + + } else if (Opcode == LDAH_OP) { + OpName = "ldah"; + + } else if (Opcode == STQ_OP) { + OpName = "stq"; + } + sprintf(Operands, "%s, $%d(%s)", RaName, Offset16, RbName); + sprintf(Comments, "%s = %Lx", RaName, IntegerRegister[Ra]); + break; + + case ARITH_OP : + case BIT_OP : + Function = Instruction.OpReg.Function; + if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC)) { + OpName = "addq"; + + } else if ((Opcode == ARITH_OP) && (Function == SUBQ_FUNC)) { + OpName = "subq"; + + } else if ((Opcode == BIT_OP) && (Function == BIS_FUNC)) { + OpName = "bis"; + + } else { + break; + } + if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) { + sprintf(Operands, "%s, %s, %s", RaName, RbName, RcName); + + } else { + sprintf(Operands, "%s, $%d, %s", RaName, Literal8, RcName); + } + sprintf(Comments, "%s = %Lx", RcName, IntegerRegister[Rc]); + break; + + case FPOP_OP : + if (Instruction.FpOp.Function == CPYS_FUNC) { + OpName = "cpys"; + sprintf(Operands, "f%d, f%d, f%d", Ra, Rb, Rc); + sprintf(Comments, "f%d = %Lx", Rc, FloatingRegister[Rc]); + } + break; + + case STT_OP : + OpName = "stt"; + sprintf(Operands, "f%d, $%d(%s)", Ra, Offset16, RbName); + sprintf(Comments, "f%d = %Lx", Ra, FloatingRegister[Ra]); + break; + } + if (OpName == NULL) { + OpName = "???"; + sprintf(Operands, "..."); + sprintf(Comments, "Unknown to virtual unwind."); + } + DbgPrint(" %08lx: %08lx %-5s %-16s // %s\n", + ControlPc, Instruction.Long, OpName, Operands, Comments); + } + return; +} + +#define _RtlpFoundTrapFrame(NextPc) \ + if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \ + DbgPrint(" *** Looks like a trap frame (fake prologue), Fir = %lx\n", \ + NextPc); \ + } + +#define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame) \ + if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \ + DbgPrint("RtlVirtualUnwind: EstablisherFrame Virtual = %08lx, Real = %08lx\n", \ + (EstablisherFrame)->Virtual, (EstablisherFrame)->Real); \ + DbgPrint("RtlVirtualUnwind: returning NextPc = %lx, sp = %lx\n\n", \ + NextPc, ContextRecord->IntSp); \ + } + +#else + +#define _RtlpDebugDisassemble(ControlPc, ContextRecord) +#define _RtlpFoundTrapFrame(NextPc) +#define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame) + +#endif + +ULONG +RtlVirtualUnwind ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PFRAME_POINTERS EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by executing its + prologue code backwards. Given the current context and the instructions + that preserve registers in the prologue, it is possible to recreate the + nonvolatile context at the point the function was called. + + If the function is a leaf function, then the address where control left + the previous frame is obtained from the context record. If the function + is a nested function, but not an exception or interrupt frame, then the + prologue code is executed backwards and the address where control left + the previous frame is obtained from the updated context record. + + Otherwise, an exception or interrupt entry to the system is being unwound + and a specially coded prologue restores the return address twice. Once + from the fault instruction address and once from the saved return address + register. The first restore is returned as the function value and the + second restore is placed in the updated context record. + + During the unwind, the virtual and real frame pointers for the function + are calculated and returned in the given frame pointers structure. + + If a context pointers record is specified, then the address where each + register is restored from is recorded in the appropriate element of the + context pointers record. + +Arguments: + + ControlPc - Supplies the address where control left the specified + function. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + + InFunction - Supplies a pointer to a variable that receives whether the + control PC is within the current function. + + EstablisherFrame - Supplies a pointer to a frame pointers structure + that will receive the values for the virtual frame pointer and the + real frame pointer. The value of the real frame pointer is reliable + only when InFunction is TRUE. + + ContextPointers - Supplies an optional pointer to a context pointers + record. + +Return Value: + + The address where control left the previous frame is returned as the + function value. + +Implementation Notes: + + N.B. "where control left" is not the "return address" of the call in the + previous frame. For normal frames, NextPc points to the last instruction + that completed in the previous frame (the JSR/BSR). The difference between + NextPc and NextPc + 4 (return address) is important for correct behavior + in boundary cases of exception addresses and scope tables. + + For exception and interrupt frames, NextPc is obtained from the trap frame + contination address (Fir). For faults and synchronous traps, NextPc is both + the last instruction to execute in the previous frame and the next + instruction to execute if the function were to return. For asynchronous + traps, NextPc is the continuation address. It is the responsibility of the + compiler to insert TRAPB instructions to insure asynchronous traps do not + occur outside the scope from the instruction(s) that caused them. + + N.B. in this and other files where RtlVirtualUnwind is used, the variable + named NextPc is perhaps more accurately, LastPc - the last PC value in + the previous frame, or CallPc - the address of the call instruction, or + ControlPc - the address where control left the previous frame. Instead + think of NextPc as the next PC to use in another call to virtual unwind. + + The Alpha version of virtual unwind is similar in design, but slightly + more complex than the Mips version. This is because Alpha compilers + are given more flexibility to optimize generated code and instruction + sequences, including within procedure prologues. In addition, because of + the current inability of the GEM compiler to materialize virtual frame + pointers, this function must manage both virtual and real frame pointers. + +--*/ + +{ + + ULONG Address; + ULONG DecrementOffset; + ULONG DecrementRegister; + ALPHA_INSTRUCTION FollowingInstruction; + PULONGLONG FloatingRegister; + ULONG FrameSize; + ULONG Function; + ALPHA_INSTRUCTION Instruction; + PULONGLONG IntegerRegister; + ULONG Literal8; + ULONG NextPc; + LONG Offset16; + ULONG Opcode; + ULONG Ra; + ULONG Rb; + ULONG Rc; + BOOLEAN RestoredRa; + BOOLEAN RestoredSp; + +#if DBG + if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { + DbgPrint("\nRtlVirtualUnwind(ControlPc = %lx, FunctionEntry = %lx,) sp = %lx\n", + ControlPc, FunctionEntry, ContextRecord->IntSp); + } +#endif +#if DBG + if ((FunctionEntry == NULL) || (ControlPc & 0x3) || + (FunctionEntry->BeginAddress >= FunctionEntry->EndAddress) || +#if 0 + (ControlPc < FunctionEntry->BeginAddress) || + (ControlPc >= FunctionEntry->EndAddress) || +#endif + (FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) || + (FunctionEntry->PrologEndAddress >= FunctionEntry->EndAddress)) { + DbgPrint("\n****** Warning - invalid PC or function table entry (virtual unwind).\n"); + return ControlPc; + } +#endif + // + // Set the base address of the integer and floating register arrays within + // the context record. Each set of 32 registers is known to be contiguous. + // + + IntegerRegister = &ContextRecord->IntV0; + FloatingRegister = &ContextRecord->FltF0; + + // + // Handle the epilogue case where the next instruction is a return. + // + // Exception handlers cannot be called if the ControlPc is within the + // epilogue because exception handlers expect to operate with a current + // stack frame. The value of SP is not current within the epilogue. + // + + Instruction.Long = *((PULONG)ControlPc); + if (IS_RETURN_0001_INSTRUCTION(Instruction.Long)) { + Rb = Instruction.Jump.Rb; + NextPc = (ULONG)IntegerRegister[Rb] - 4; + + // + // The instruction at the point where control left the specified + // function is a return, so any saved registers have already been + // restored, and the stack pointer has already been adjusted. The + // stack does not need to be unwound in this case and the saved + // return address register is returned as the function value. + // + // In fact, reverse execution of the prologue is not possible in + // this case: the stack pointer has already been incremented and + // so, for this frame, neither a valid stack pointer nor frame + // pointer exists from which to begin reverse execution of the + // prologue. In addition, the integrity of any data on the stack + // below the stack pointer is never guaranteed (due to interrupts + // and exceptions). + // + // The epilogue instruction sequence is: + // + // ==> ret zero, (Ra), 1 // return + // or + // + // mov ra, Rx // save return address + // ... + // ==> ret zero, (Rx), 1 // return + // + + EstablisherFrame->Real = 0; + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + *InFunction = FALSE; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame); + return NextPc; + } + + // + // Handle the epilogue case where the next two instructions are a stack + // frame deallocation and a return. + // + + FollowingInstruction.Long = *((PULONG)(ControlPc + 4)); + if (IS_RETURN_0001_INSTRUCTION(FollowingInstruction.Long)) { + Rb = FollowingInstruction.Jump.Rb; + NextPc = (ULONG)IntegerRegister[Rb] - 4; + + // + // The second instruction following the point where control + // left the specified function is a return. If the instruction + // before the return is a stack increment instruction, then all + // saved registers have already been restored except for SP. + // The value of the stack pointer register cannot be recovered + // through reverse execution of the prologue because in order + // to begin reverse execution either the stack pointer or the + // frame pointer (if any) must still be valid. + // + // Instead, the effect that the stack increment instruction + // would have had on the context is manually applied to the + // current context. This is forward execution of the epilogue + // rather than reverse execution of the prologue. + // + // In an epilogue, as in a prologue, the stack pointer is always + // adjusted with a single instruction: either an immediate-value + // (lda) or a register-value (addq) add instruction. + // + + Function = Instruction.OpReg.Function; + Offset16 = Instruction.Memory.MemDisp; + Opcode = Instruction.OpReg.Opcode; + Ra = Instruction.OpReg.Ra; + Rb = Instruction.OpReg.Rb; + Rc = Instruction.OpReg.Rc; + + if ((Opcode == LDA_OP) && (Ra == SP_REG)) { + + // + // Load Address instruction. + // + // Since the destination (Ra) register is SP, an immediate- + // value stack deallocation operation is being performed. The + // displacement value should be added to SP. The displacement + // value is assumed to be positive. The amount of stack + // deallocation possible using this instruction ranges from + // 16 to 32752 (32768 - 16) bytes. The base register (Rb) is + // usually SP, but may be another register. + // + // The epilogue instruction sequence is: + // + // ==> lda sp, +N(sp) // deallocate stack frame + // ret zero, (ra) // return + // or + // + // ==> lda sp, +N(Rx) // restore SP and deallocate frame + // ret zero, (ra) // return + // + + ContextRecord->IntSp = Offset16 + IntegerRegister[Rb]; + EstablisherFrame->Real = 0; + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + *InFunction = FALSE; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + _RtlpDebugDisassemble(ControlPc + 4, ContextRecord); + _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame); + return NextPc; + + } else if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC) && + (Rc == SP_REG) && + (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) { + + // + // Add Quadword instruction. + // + // Since both source operands are registers, and the + // destination register is SP, a register-value stack + // deallocation is being performed. The value of the two + // source registers should be added and this is the new + // value of SP. One of the source registers is usually SP, + // but may be another register. + // + // The epilogue instruction sequence is: + // + // ldiq Rx, N // set [large] frame size + // ... + // ==> addq sp, Rx, sp // deallocate stack frame + // ret zero, (ra) // return + // or + // + // ==> addq Rx, Ry, sp // restore SP and deallocate frame + // ret zero, (ra) // return + // + + ContextRecord->IntSp = IntegerRegister[Ra] + IntegerRegister[Rb]; + EstablisherFrame->Real = 0; + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + *InFunction = FALSE; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + _RtlpDebugDisassemble(ControlPc + 4, ContextRecord); + _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame); + return NextPc; + } + } + + // + // By default set the frame pointers to the current value of SP. + // + // When a procedure is called, the value of SP before the stack + // allocation instruction is the virtual frame pointer. When reverse + // executing instructions in the prologue, the value of SP before the + // stack allocation instruction is encountered is the real frame + // pointer. This is the current value of SP unless the procedure uses + // a frame pointer (e.g., FP_REG). + // + + EstablisherFrame->Real = (ULONG)ContextRecord->IntSp; + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + + // + // If the address where control left the specified function is beyond + // the end of the prologue, then the control PC is considered to be + // within the function and the control address is set to the end of + // the prologue. Otherwise, the control PC is not considered to be + // within the function (i.e., the prologue). + // + // N.B. PrologEndAddress is equal to BeginAddress for a leaf function. + // + // The low-order two bits of PrologEndAddress are reserved for the IEEE + // exception mode and so must be masked out. + // + + if ((ControlPc < FunctionEntry->BeginAddress) || + (ControlPc >= FunctionEntry->PrologEndAddress)) { + *InFunction = TRUE; + ControlPc = (FunctionEntry->PrologEndAddress & (~0x3)); + + } else { + *InFunction = FALSE; + } + + // + // Scan backward through the prologue to reload callee saved registers + // that were stored or copied and to increment the stack pointer if it + // was decremented. + // + + DecrementRegister = ZERO_REG; + NextPc = (ULONG)ContextRecord->IntRa - 4; + RestoredRa = FALSE; + RestoredSp = FALSE; + while (ControlPc > FunctionEntry->BeginAddress) { + + // + // Get instruction value, decode fields, case on opcode value, and + // reverse register store and stack decrement operations. + // N.B. The location of Opcode, Ra, Rb, and Rc is the same across + // all opcode formats. The same is not true for Function. + // + + ControlPc -= 4; + Instruction.Long = *((PULONG)ControlPc); + Function = Instruction.OpReg.Function; + Literal8 = Instruction.OpLit.Literal; + Offset16 = Instruction.Memory.MemDisp; + Opcode = Instruction.OpReg.Opcode; + Ra = Instruction.OpReg.Ra; + Rb = Instruction.OpReg.Rb; + Rc = Instruction.OpReg.Rc; + + // + // Compare against each instruction type that will affect the context + // and that is allowed in a prologue. Any other instructions found + // in the prologue will be ignored since they are assumed to have no + // effect on the context. + // + + switch (Opcode) { + + case STQ_OP : + + // + // Store Quad instruction. + // + // If the base register is SP, then reload the source register + // value from the value stored on the stack. + // + // The prologue instruction sequence is: + // + // ==> stq Rx, N(sp) // save integer register Rx + // + + if ((Rb == SP_REG) && (Ra != ZERO_REG)) { + + // + // Reload the register by retrieving the value previously + // stored on the stack. + // + + Address = Offset16 + ContextRecord->IntSp; + IntegerRegister[Ra] = *((PULONGLONG)Address); + + // + // If the destination register is RA and this is the first + // time that RA is being restored, then set the address of + // where control left the previous frame. Otherwise, if this + // is the second time RA is being restored, then the first + // one was an interrupt or exception address and the return + // PC should not have been biased by 4. + // + + if (Ra == RA_REG) { + if (RestoredRa == FALSE) { + NextPc = (ULONG)ContextRecord->IntRa - 4; + RestoredRa = TRUE; + + } else { + NextPc += 4; + _RtlpFoundTrapFrame(NextPc); + } + + // + // Otherwise, if the destination register is SP and this is + // the first time that SP is being restored, then set the + // establisher frame pointers. + // + + } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) { + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + EstablisherFrame->Real = (ULONG)ContextRecord->IntSp; + RestoredSp = TRUE; + } + + // + // If a context pointer record is specified, then record + // the address where the destination register contents + // are stored. + // + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[Ra] = (PULONGLONG)Address; + } + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + break; + + case LDAH_OP : + Offset16 <<= 16; + + case LDA_OP : + + // + // Load Address High, Load Address instruction. + // + // There are several cases where the lda and/or ldah instructions + // are used: one to decrement the stack pointer directly, and the + // others to load immediate values into another register and that + // register is then used to decrement the stack pointer. + // + // In the examples below, as a single instructions or as a pair, + // a lda may be substituted for a ldah and visa-versa. + // + + if (Ra == SP_REG) { + if (Rb == SP_REG) { + + // + // If both the destination (Ra) and base (Rb) registers + // are SP, then a standard stack allocation was performed + // and the negated displacement value is the stack frame + // size. The amount of stack allocation possible using + // the lda instruction ranges from 16 to 32768 bytes and + // the amount of stack allocation possible using the ldah + // instruction ranges from 65536 to 2GB in multiples of + // 65536 bytes. It is rare for the ldah instruction to be + // used in this manner. + // + // The prologue instruction sequence is: + // + // ==> lda sp, -N(sp) // allocate stack frame + // + + FrameSize = -Offset16; + goto StackAllocation; + + } else { + + // + // The destination register is SP and the base register + // is not SP, so this instruction must be the second + // half of an instruction pair to allocate a large size + // (>32768 bytes) stack frame. Save the displacement value + // as the partial decrement value and postpone adjusting + // the value of SP until the first instruction of the pair + // is encountered. + // + // The prologue instruction sequence is: + // + // ldah Rx, -N(sp) // prepare new SP (upper) + // ==> lda sp, sN(Rx) // allocate stack frame + // + + DecrementRegister = Rb; + DecrementOffset = Offset16; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + + } else if (Ra == DecrementRegister) { + if (Rb == DecrementRegister) { + + // + // Both the destination and base registers are the + // decrement register, so this instruction exists as the + // second half of a two instruction pair to load a + // 31-bit immediate value into the decrement register. + // Save the displacement value as the partial decrement + // value. + // + // The prologue instruction sequence is: + // + // ldah Rx, +N(zero) // set frame size (upper) + // ==> lda Rx, sN(Rx) // set frame size (+lower) + // ... + // subq sp, Rx, sp // allocate stack frame + // + + DecrementOffset += Offset16; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + + } else if (Rb == ZERO_REG) { + + // + // The destination register is the decrement register and + // the base register is zero, so this instruction exists + // to load an immediate value into the decrement register. + // The stack frame size is the new displacement value added + // to the previous displacement value, if any. + // + // The prologue instruction sequence is: + // + // ==> lda Rx, +N(zero) // set frame size + // ... + // subq sp, Rx, sp // allocate stack frame + // or + // + // ==> ldah Rx, +N(zero) // set frame size (upper) + // lda Rx, sN(Rx) // set frame size (+lower) + // ... + // subq sp, Rx, sp // allocate stack frame + // + + FrameSize = (Offset16 + DecrementOffset); + goto StackAllocation; + + } else if (Rb == SP_REG) { + + // + // The destination (Ra) register is SP and the base (Rb) + // register is the decrement register, so a two + // instruction, large size (>32768 bytes) stack frame + // allocation was performed. Add the new displacement + // value to the previous displacement value. The negated + // displacement value is the stack frame size. + // + // The prologue instruction sequence is: + // + // ==> ldah Rx, -N(sp) // prepare new SP (upper) + // lda sp, sN(Rx) // allocate stack frame + // + + FrameSize = -(Offset16 + (LONG)DecrementOffset); + goto StackAllocation; + } + } + break; + + case ARITH_OP : + + if ((Function == ADDQ_FUNC) && + (Instruction.OpReg.RbvType != RBV_REGISTER_FORMAT)) { + + // + // Add Quadword (immediate) instruction. + // + // If the first source register is zero, and the second + // operand is a literal, and the destination register is + // the decrement register, then the instruction exists + // to load an unsigned immediate value less than 256 into + // the decrement register. The immediate value is the stack + // frame size. + // + // The prologue instruction sequence is: + // + // ==> addq zero, N, Rx // set frame size + // ... + // subq sp, Rx, sp // allocate stack frame + // + + if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) { + FrameSize = Literal8; + goto StackAllocation; + } + + } else if ((Function == SUBQ_FUNC) && + (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) { + + // + // Subtract Quadword (register) instruction. + // + // If both source operands are registers and the first + // source (minuend) register and the destination + // (difference) register are both SP, then a register value + // stack allocation was performed and the second source + // (subtrahend) register value will be added to SP when its + // value is known. Until that time save the register number of + // this decrement register. + // + // The prologue instruction sequence is: + // + // ldiq Rx, N // set frame size + // ... + // ==> subq sp, Rx, sp // allocate stack frame + // + + if ((Ra == SP_REG) && (Rc == SP_REG)) { + DecrementRegister = Rb; + DecrementOffset = 0; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + } + break; + + case BIT_OP : + + // + // If the second operand is a register the bit set instruction + // may be a register move instruction, otherwise if the second + // operand is a literal, the bit set instruction may be a load + // immediate value instruction. + // + + if ((Function == BIS_FUNC) && (Rc != ZERO_REG)) { + if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) { + + // + // Bit Set (register move) instruction. + // + // If both source registers are the same register, or + // one of the source registers is zero, then this is a + // register move operation. Restore the value of the + // source register by copying the current destination + // register value back to the source register. + // + // The prologue instruction sequence is: + // + // ==> bis Rx, Rx, Ry // copy register Rx + // or + // + // ==> bis Rx, zero, Ry // copy register Rx + // or + // + // ==> bis zero, Rx, Ry // copy register Rx + // + + if (Ra == ZERO_REG) { + + // + // Map the third case above to the first case. + // + + Ra = Rb; + + } else if (Rb == ZERO_REG) { + + // + // Map the second case above to the first case. + // + + Rb = Ra; + } + + if ((Ra == Rb) && (Ra != ZERO_REG)) { + IntegerRegister[Ra] = IntegerRegister[Rc]; + + // + // If the destination register is RA and this is the + // first time that RA is being restored, then set the + // address of where control left the previous frame. + // Otherwise, if this is the second time RA is being + // restored, then the first one was an interrupt or + // exception address and the return PC should not + // have been biased by 4. + // + + if (Ra == RA_REG) { + if (RestoredRa == FALSE) { + NextPc = (ULONG)ContextRecord->IntRa - 4; + RestoredRa = TRUE; + + } else { + NextPc += 4; + _RtlpFoundTrapFrame(NextPc); + } + + // + // If the source register is SP and this is the first + // time SP is set, then this is a frame pointer set + // instruction. Reset the frame pointers to this new + // value of SP. + // + + } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) { + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + EstablisherFrame->Real = (ULONG)ContextRecord->IntSp; + RestoredSp = TRUE; + } + + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + + } else { + + // + // Bit Set (load immediate) instruction. + // + // If the first source register is zero, and the second + // operand is a literal, and the destination register is + // the decrement register, then this instruction exists + // to load an unsigned immediate value less than 256 into + // the decrement register. The decrement register value is + // the stack frame size. + // + // The prologue instruction sequence is: + // + // ==> bis zero, N, Rx // set frame size + // ... + // subq sp, Rx, sp // allocate stack frame + // + + if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) { + FrameSize = Literal8; +StackAllocation: + // + // Add the frame size to SP to reverse the stack frame + // allocation, leave the real frame pointer as is, set + // the virtual frame pointer with the updated SP value, + // and clear the decrement register. + // + + ContextRecord->IntSp += FrameSize; + EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp; + DecrementRegister = ZERO_REG; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + } + } + break; + + case STT_OP : + + // + // Store T-Floating (quadword integer) instruction. + // + // If the base register is SP, then reload the source register + // value from the value stored on the stack. + // + // The prologue instruction sequence is: + // + // ==> stt Fx, N(sp) // save floating register Fx + // + + if ((Rb == SP_REG) && (Ra != FZERO_REG)) { + + // + // Reload the register by retrieving the value previously + // stored on the stack. + // + + Address = Offset16 + ContextRecord->IntSp; + FloatingRegister[Ra] = *((PULONGLONG)Address); + + // + // If a context pointer record is specified, then record + // the address where the destination register contents are + // stored. + // + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->FloatingContext[Ra] = (PULONGLONG)Address; + } + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + break; + + case FPOP_OP : + + // + // N.B. The floating operate function field is not the same as + // the integer operate nor the jump function fields. + // + + if (Instruction.FpOp.Function == CPYS_FUNC) { + + // + // Copy Sign (floating-point move) instruction. + // + // If both source registers are the same register, then this is + // a floating-point register move operation. Restore the value + // of the source register by copying the current destination + // register value to the source register. + // + // The prologue instruction sequence is: + // + // ==> cpys Fx, Fx, Fy // copy floating register Fx + // + + if ((Ra == Rb) && (Ra != FZERO_REG)) { + FloatingRegister[Ra] = FloatingRegister[Rc]; + _RtlpDebugDisassemble(ControlPc, ContextRecord); + } + } + + default : + break; + } + } + + _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame); + return NextPc; +} + +ULONG +RtlpVirtualUnwind ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PULONG EstablisherFrame + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specfified function by executing its + prologue code backwards. + + If the function is a leaf function, then the address where control left + the previous frame is obtained from the context record. If the function + is a nested function, but not an exception or interrupt frame, then the + prologue code is executed backwards and the address where control left + the previous frame is obtained from the updated context record. + + Otherwise, an exception or interrupt entry to the system is being unwound + and a specially coded prologue restores the return address twice. Once + from the fault instruction address and once from the saved return address + register. The first restore is returned as the function value and the + second restore is place in the updated context record. + + If a context pointers record is specified, then the address where each + nonvolatile registers is restored from is recorded in the appropriate + element of the context pointers record. + + N.B. This function copies the specified context record and only computes + the establisher frame and whether control is actually in a function. + +Arguments: + + ControlPc - Supplies the address where control left the specified + function. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + + InFunction - Supplies a pointer to a variable that receives whether the + control PC is within the current function. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + ContextPointers - Supplies an optional pointer to a context pointers + record. + +Return Value: + + The address where control left the previous frame is returned as the + function value. + +--*/ + +{ + + CONTEXT LocalContext; + + // + // Copy the context record so updates will not be reflected in the + // original copy and then virtually unwind to the caller of the + // specified control point. + // + + RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT)); + return RtlVirtualUnwind(ControlPc, + FunctionEntry, + &LocalContext, + InFunction, + (PFRAME_POINTERS)EstablisherFrame, + NULL); +} diff --git a/private/ntos/rtl/alpha/getcalr.c b/private/ntos/rtl/alpha/getcalr.c new file mode 100644 index 000000000..01bbe89cb --- /dev/null +++ b/private/ntos/rtl/alpha/getcalr.c @@ -0,0 +1,189 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + getcalr.c + +Abstract: + + This module implements the routine RtlGetCallerAddress. It will + return the address of the caller, and the callers caller to the + specified procedure. + +Author: + + Larry Osterman (larryo) 18-Mar-1991 (with help from DaveC) + +Revision History: + + 18-Mar-1991 larryo + + Created + + Thomas Van Baak (tvb) 5-May-1992 + + Adapted for Alpha AXP. + +--*/ +#include "ntrtlp.h" + +// +// Undefine get callers address since it is defined as a macro. +// + +#undef RtlGetCallersAddress + +VOID +RtlGetCallersAddress ( + OUT PVOID *CallersPc, + OUT PVOID *CallersCallersPc + ) + +/*++ + +Routine Description: + + This routine returns the address of the call to the routine that called + this routine, and the address of the call to the routine that called + the routine that called this routine. For example, if A called B called + C which called this routine, the return addresses in B and A would be + returned. + +Arguments: + + CallersPc - Supplies a pointer to a variable that receives the address + of the caller of the caller of this routine (B). + + CallersCallersPc - Supplies a pointer to a variable that receives the + address of the caller of the caller of the caller of this routine + (A). + +Return Value: + + None. + +Note: + + If either of the calling stack frames exceeds the limits of the stack, + they are set to NULL. + +--*/ + +{ +#ifdef REALLY_GET_CALLERS_CALLER + CONTEXT ContextRecord; + FRAME_POINTERS EstablisherFrame; + PRUNTIME_FUNCTION FunctionEntry; + BOOLEAN InFunction; + ULONG HighLimit, LowLimit; + ULONG NextPc; + + // + // Assume the function table entries for the various routines cannot be + // found or there are not four procedure activation records on the stack. + // + + *CallersPc = NULL; + *CallersCallersPc = NULL; + + // + // Capture the current context. + // + + RtlCaptureContext(&ContextRecord); + NextPc = (ULONG)ContextRecord.IntRa; + + // + // Get the high and low limits of the current thread's stack. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + + // + // Attempt to unwind to the caller of this routine (C). The FunctionEntry + // returned here will be that of this routine since NextPc was the return + // address of our call to RtlCaptureContext. For the purposes of this + // function, the +4 and -4 adjustments to NextPc are unnecessary. + // + + FunctionEntry = RtlLookupFunctionEntry(NextPc); + if (FunctionEntry != NULL) { + + // + // A function entry was found for this routine. Virtually unwind + // to the caller of this routine (C). + // + + NextPc = RtlVirtualUnwind(NextPc, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + // + // Attempt to unwind to the caller of the caller of this routine (B). + // + + FunctionEntry = RtlLookupFunctionEntry(NextPc); + if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.IntSp < HighLimit)) { + + // + // A function table entry was found for the caller of the caller + // of this routine (B). Virtually unwind to the caller of the + // caller of this routine (B). + // + + NextPc = RtlVirtualUnwind(NextPc, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + *CallersPc = (PVOID)NextPc; + + // + // Attempt to unwind to the caller of the caller of the caller + // of the caller of this routine (A). + // + + FunctionEntry = RtlLookupFunctionEntry(NextPc); + if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.IntSp < HighLimit)) { + + // + // A function table entry was found for the caller of the + // caller of the caller of this routine (A). Virtually unwind + // to the caller of the caller of the caller of this routine + // (A). + // + + NextPc = RtlVirtualUnwind(NextPc, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + *CallersCallersPc = (PVOID)NextPc; + } + } + } +#else + *CallersPc = NULL; + *CallersCallersPc = NULL; +#endif + return; +} + +USHORT +RtlCaptureStackBackTrace( + IN ULONG FramesToSkip, + IN ULONG FramesToCapture, + OUT PVOID *BackTrace, + OUT PULONG BackTraceHash + ) +{ + return 0; +} diff --git a/private/ntos/rtl/alpha/ghandler.c b/private/ntos/rtl/alpha/ghandler.c new file mode 100644 index 000000000..c15c615b5 --- /dev/null +++ b/private/ntos/rtl/alpha/ghandler.c @@ -0,0 +1,436 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + ghandler.c + +Abstract: + + This module implements the C specific exception handler that provides + structured exception handling for code generated by the GEM compiler. + +Author: + + John Parks (parks) 12-Jan-1993 + Thomas Van Baak (tvb) 28-Jan-1993 + +Environment: + + Any mode. + +Revision History: + +--*/ + +#include "nt.h" + +// +// Define procedure prototypes for exception filter and termination handler +// execution routines defined in jmpunwnd.s. +// + +LONG +__C_ExecuteExceptionFilter ( + PEXCEPTION_POINTERS ExceptionPointers, + EXCEPTION_FILTER ExceptionFilter, + ULONG EstablisherFrame + ); + +ULONG +__C_ExecuteTerminationHandler ( + BOOLEAN AbnormalTermination, + TERMINATION_HANDLER TerminationHandler, + ULONG EstablisherFrame + ); + +// +// Define local procedure prototypes. +// + +EXCEPTION_DISPOSITION +_OtsCSpecificHandler ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PVOID EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext + ); + +ULONG +_OtsLocalFinallyUnwind ( + IN PSEH_CONTEXT SehContext, + IN PSEH_BLOCK TargetSeb, + IN PVOID RealFramePointer + ); + +// +// Define local macros. +// + +#define IS_EXCEPT(Seb) ((Seb)->JumpTarget != 0) +#define IS_FINALLY(Seb) ((Seb)->JumpTarget == 0) + +// +// Initialize an exception record for the unwind with the SEB of the target +// included as one information parameter. This is done so that the target +// frame of the unwind may execute all the finally handlers necessary given +// the SEB pointer at the unwind target. +// + +#define MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, Seb) { \ + ExceptionRecord->ExceptionCode = STATUS_UNWIND; \ + ExceptionRecord->ExceptionFlags = EXCEPTION_UNWINDING; \ + ExceptionRecord->ExceptionRecord = NULL; \ + ExceptionRecord->ExceptionAddress = 0; \ + ExceptionRecord->NumberParameters = 1; \ + ExceptionRecord->ExceptionInformation[0] = (ULONG)(Seb); \ + } + +EXCEPTION_DISPOSITION +_OtsCSpecificHandler ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PVOID EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext + ) + +/*++ + +Routine Description: + + This function walks up the list of SEB's associated with the specified + procedure and calls except filters and finally handlers as necessary. + + It is called in two different contexts: + (i) by the exception dispatcher after an exception is raised + (ii) by the unwinder during an unwind operation + + In the first case, is searches the SEB list for except filters to evaluate. + In the second case, it searches for finally handlers to execute. + +Arguments: + + ExceptionRecord - Supplies a pointer to an exception record. + + EstablisherFrame - Supplies a (virtual frame) pointer to the frame of the + establisher function. + + ContextRecord - Supplies a pointer to a context record. + + DispatcherContext - Supplies a pointer to the exception dispatcher or + unwind dispatcher context. + +Return Value: + + If the exception is handled by one of the exception filter routines, then + there is no return from this routine and RtlUnwind is called. Otherwise, + an exception disposition value of continue execution or continue search is + returned. + +Notes: + In context (i) there are 3 possibilities: + + (a) If an exception filter returns a value greater that 0 (meaning + that the associated handler should be invoked) there is no + return from this function. RtlUnwind is called to unwind the + stack to the exception handler corresponding to that filter. + + (b) If an exception filter returns a value less than 0 (meaning + that the exception should be dismissed), this routine returns + value ExceptionContinueExecution. + + (c) If every filter returns value 0 (meaning that the search for a + handler should continue elsewhere), this function returns + ExceptionContinueSearch. + + In context (ii) there are 2 possibilities: + + (d) If no branches are detected out of finally handlers, this + function returns ExceptionContinueSearch. + + (e) If a branch is detected out of a finally handler, there is no + return from this routine. RtlUnwind is called to unwind to the + branch target (and cancel the current unwind). + + There may be long jumps out of both except filters and finally handlers + in which case this routine will be peeled off the stack without returning. + +--*/ + +{ + + ULONG ContinuationAddress; + EXCEPTION_FILTER ExceptionFilter; + PVOID ExceptionHandler; + EXCEPTION_POINTERS ExceptionPointers; + LONG FilterValue; + ULONG RealFramePointer; + PSEH_BLOCK Seb; + PSEH_CONTEXT SehContext; + PSEH_BLOCK TargetSeb; + TERMINATION_HANDLER TerminationHandler; + + // + // Get the address of the SEH context which is at some negative offset + // from the virtual frame pointer. For GEM, the handler data field of + // the function entry contains that offset. The current SEB pointer and + // the RFP (static link) are obtained from the SEH context. + // + + SehContext = (PSEH_CONTEXT)((ULONG)EstablisherFrame + + (LONG)DispatcherContext->FunctionEntry->HandlerData); + RealFramePointer = SehContext->RealFramePointer; + + // + // If this is a dispatching context, walk up the list of SEBs evaluating + // except filters. + // + + if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) { + + // + // Set up the ExceptionPointers structure that is used by except + // filters to obtain data for the GetExceptionInformation intrinsic + // function. Copy the current SEB pointer into a local variable + // because the real SEB pointer is only modified in unwind contexts. + // + + ExceptionPointers.ExceptionRecord = ExceptionRecord; + ExceptionPointers.ContextRecord = ContextRecord; + + for (Seb = SehContext->CurrentSeb; Seb != NULL; Seb = Seb->ParentSeb) { + if (IS_EXCEPT(Seb)) { + + // + // This is an except filter. Get the addresses of the filter + // and exception handler from the SEB, then call the except + // filter. + // + + ExceptionFilter = (EXCEPTION_FILTER)Seb->HandlerAddress; + ExceptionHandler = (PVOID)Seb->JumpTarget; + + FilterValue = __C_ExecuteExceptionFilter(&ExceptionPointers, + ExceptionFilter, + RealFramePointer); + + // + // If the except filter < 0, dismiss the exception. If > 0, + // store the exception code on the stack for the except + // handler, modify the given ExceptionRecord so that finally + // handlers will be called properly during the unwind, then + // unwind down to the except handler. If = 0, resume the + // search for except filters. + // + + if (FilterValue < 0) { + return ExceptionContinueExecution; + + } else if (FilterValue > 0) { + SehContext->ExceptionCode = ExceptionRecord->ExceptionCode; + MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, + Seb->ParentSeb); + RtlUnwind2(EstablisherFrame, + ExceptionHandler, + ExceptionRecord, + 0, + ContextRecord); + } + } + } + + } else if (!IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)) { + + // + // This is an unwind but is not the target frame. Since the function + // is being terminated, finally handlers for all try bodies that are + // presently in scope must be executed. Walk up the SEB list all the + // way to the top executing finally handlers. This corresponds to + // exiting all try bodies that are presently in scope. + // + + while (SehContext->CurrentSeb != NULL) { + + // + // Get the address of the SEB and then update the SEH context. + // + + Seb = SehContext->CurrentSeb; + SehContext->CurrentSeb = Seb->ParentSeb; + + if (IS_FINALLY(Seb)) { + + // + // This is a finally handler. Get the address of the handler + // from the SEB and call the finally handler. + // + + TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress; + ContinuationAddress = + __C_ExecuteTerminationHandler(TRUE, + TerminationHandler, + RealFramePointer); + + // + // If the finally handler returns a non-zero result, there + // was a branch out of the handler (to that address) and this + // routine should unwind to that target. + // + + if (ContinuationAddress != 0) { + MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, + SehContext->CurrentSeb); + RtlUnwind(EstablisherFrame, + (PVOID)ContinuationAddress, + ExceptionRecord, + 0); + } + } + } + + } else { + + // + // This is the target frame of an unwind. Since the target may be + // in a different try scope than the one defined by the current SEB + // pointer, finally handlers between the two scopes must execute. + // Walk up the SEB list from the current SEB to the target SEB and + // execute all finally handlers encountered. + // + + TargetSeb = (PSEH_BLOCK)ExceptionRecord->ExceptionInformation[0]; + ContinuationAddress = _OtsLocalFinallyUnwind(SehContext, + TargetSeb, + (PVOID)RealFramePointer); + if (ContinuationAddress != 0) { + + // + // A non-zero result indicates there was a branch out of a + // finally handler that was being executed during the unwind. + // This routine should unwind to that address. + // + + MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, + SehContext->CurrentSeb); + RtlUnwind(EstablisherFrame, + (PVOID)ContinuationAddress, + ExceptionRecord, + 0); + } + } + + // + // Continue search for exception or termination handlers. + // + + return ExceptionContinueSearch; +} + +ULONG +_OtsLocalFinallyUnwind ( + IN PSEH_CONTEXT SehContext, + IN PSEH_BLOCK TargetSeb, + IN PVOID RealFramePointer + ) + +/*++ + +Routine Description: + + This function walks up the SEB tree of the current procedure from the + current SEB to the target SEB and executes all the finally handlers it + encounters. + + Calls to this function are inserted into user code by the compiler when + there are branches out of guarded regions that may require finally + handlers to execute. + + This function is also called from _OtsCSpecificHandler when the target + frame is reached during an unwind operation. There may be finally handlers + that should execute before resuming execution at the unwind target. + +Arguments: + + SehContext - Supplies the address of the SEH context structure which is + located in the stack frame. + + TargetSeb - Supplies the address of the SEB corresponding to the branch + target address. + + RealFramePointer - Supplies the (real frame) pointer of the establisher + frame, which is the current stack frame. This is used to set up the + static link if a finally handler is invoked. + +Return Value: + + If a branch out of a finally handler is detected, the function value is + the address of the branch target. Otherwise, the function value is zero. + +--*/ + +{ + + ULONG ContinuationAddress; + BOOLEAN Nested; + PSEH_BLOCK Seb; + TERMINATION_HANDLER TerminationHandler; + + // + // If the SEB pointers are the same, no finally handlers need to execute. + // The branch is to a target location in the same guarded scope. + // + + if (SehContext->CurrentSeb == TargetSeb) { + return 0; + } + + // + // If the current SEB scope is not nested within the target SEB scope, no + // finally handlers need to execute. Reset the current SEB pointer to the + // target SEB pointer and return. + // + + Nested = FALSE; + Seb = SehContext->CurrentSeb; + + while (Seb != NULL) { + Seb = Seb->ParentSeb; + if (Seb == TargetSeb) { + Nested = TRUE; + break; + } + } + if (Nested == FALSE) { + SehContext->CurrentSeb = TargetSeb; + return 0; + } + + // + // Walk up the list of SEB blocks executing finally handlers. If a branch + // out of a finally is encountered along the way, return the target + // address, otherwise return 0. + // + + while (SehContext->CurrentSeb != TargetSeb) { + + // + // Get the address of the SEB and then update the SEH context. + // + + Seb = SehContext->CurrentSeb; + SehContext->CurrentSeb = Seb->ParentSeb; + + if (IS_FINALLY(Seb)) { + TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress; + ContinuationAddress = + __C_ExecuteTerminationHandler(TRUE, + TerminationHandler, + (ULONG)RealFramePointer); + if (ContinuationAddress != 0) { + return ContinuationAddress; + } + } + } + return 0; +} diff --git a/private/ntos/rtl/alpha/largeint.s b/private/ntos/rtl/alpha/largeint.s new file mode 100644 index 000000000..e6fb89dbb --- /dev/null +++ b/private/ntos/rtl/alpha/largeint.s @@ -0,0 +1,999 @@ +// TITLE("Large Integer Arithmetic") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// Copyright (c) 1993 Digital Equipment Corporation +// +// Module Name: +// +// largeint.s +// +// Abstract: +// +// This module implements routines for performing extended integer +// arithmetic. +// +// Author: +// +// David N. Cutler (davec) 18-Apr-1990 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +// Thomas Van Baak (tvb) 9-May-1992 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + +// +// Alpha AXP Implementation Notes: +// +// The LargeInteger functions defined below implement a set of portable +// 64-bit integer arithmetic operations for x86, Mips, and Alpha systems +// using the LARGE_INTEGER data type. Code using LARGE_INTEGER variables +// and calling these functions will be portable across all NT platforms. +// This is the recommended approach to 64-bit arithmetic on NT. +// +// However, if performance is more important than portability, then for +// Alpha systems, the native 64-bit integer data types may be used instead +// of the LARGE_INTEGER type and the LargeInteger functions. The Alpha C +// compilers support a __int64 data type (renamed LONGLONG in the system +// header files). All C integer arithmetic operators may be used with +// these quadword types. This eliminates the need for, and the overhead +// of, any of the portable LargeInteger functions. +// +// In general, a LARGE_INTEGER cannot simply be converted to a LONGLONG +// because of explicit references in application code to the 32-bit LowPart +// and HighPart members of the LARGE_INTEGER structure. +// +// The performance difference between using the portable LARGE_INTEGER +// types with LargeInteger functions and the Alpha LONGLONG types and +// operations is often not significant enough to warrant modifying otherwise +// portable code. In addition, future compiler optimization and inlining may +// actually result in identical performance between portable LARGE_INTEGER +// code and Alpha specific LONGLONG code. Therefore it is recommended to +// keep NT source code portable. +// +// The Alpha source for the large integer functions below differs from the +// Mips version since for Alpha a 64-bit argument or return value is passed +// through a 64-bit integer register. In addition, most operations are +// implemented with a single instruction. The division routines below, +// however, like Mips, use a simple shift/subtract algorithm which is +// considerably slower than the algorithm used by Alpha runtime division +// routines. +// + + SBTTL("Convert Long to Large Integer") +//++ +// +// LARGE_INTEGER +// RtlConvertLongToLargeInteger ( +// IN LONG SignedInteger +// ) +// +// Routine Description: +// +// This function converts a signed integer to a signed large integer +// and returns the result. +// +// Arguments: +// +// SignedInteger (a0) - Supplies the value to convert. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlConvertLongToLargeInteger) + + addl a0, 0, v0 // ensure canonical (signed long) form + ret zero, (ra) // return + + .end RtlConvertLongToLargeInteger + + SBTTL("Convert Ulong to Large Integer") +//++ +// +// LARGE_INTEGER +// RtlConvertUlongToLargeInteger ( +// IN ULONG UnsignedInteger +// ) +// +// Routine Description: +// +// This function converts an unsigned integer to a signed large +// integer and returns the result. +// +// Arguments: +// +// UnsignedInteger (a0) - Supplies the value to convert. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlConvertUlongToLargeInteger) + + zap a0, 0xf0, v0 // convert canonical ULONG to quadword + ret zero, (ra) // return + + .end RtlConvertUlongToLargeInteger + + SBTTL("Enlarged Signed Integer Multiply") +//++ +// +// LARGE_INTEGER +// RtlEnlargedIntegerMultiply ( +// IN LONG Multiplicand, +// IN LONG Multiplier +// ) +// +// Routine Description: +// +// This function multiplies a signed integer by a signed integer and +// returns a signed large integer result. +// +// N.B. An overflow is not possible. +// +// Arguments: +// +// Multiplicand (a0) - Supplies the multiplicand value. +// +// Multiplier (a1) - Supplies the multiplier value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlEnlargedIntegerMultiply) + + addl a0, 0, a0 // ensure canonical (signed long) form + addl a1, 0, a1 // ensure canonical (signed long) form + mulq a0, a1, v0 // multiply signed both quadwords + ret zero, (ra) // return + + .end RtlEnlargedIntegerMultiply + + SBTTL("Enlarged Unsigned Divide") +//++ +// +// ULONG +// RtlEnlargedUnsignedDivide ( +// IN ULARGE_INTEGER Dividend, +// IN ULONG Divisor, +// IN OUT PULONG Remainder OPTIONAL +// ) +// +// Routine Description: +// +// This function divides an unsigned large integer by an unsigned long +// and returns the resultant quotient and optionally the remainder. +// +// N.B. An overflow or divide by zero exception is possible. +// +// Arguments: +// +// Dividend (a0) - Supplies the unsigned 64-bit dividend value. +// +// Divisor (a1) - Supplies the unsigned 32-bit divisor value. +// +// Remainder (a2) - Supplies an optional pointer to a variable that +// receives the unsigned 32-bit remainder. +// +// Return Value: +// +// The unsigned long integer quotient is returned as the function value. +// +//-- + + LEAF_ENTRY(RtlEnlargedUnsignedDivide) + +// +// Check for division by zero. +// + + zap a1, 0xf0, a1 // convert ULONG divisor to quadword + beq a1, 30f // trap if divisor is zero + +// +// Check for overflow. If the divisor is less than the upper half of the +// dividend the quotient would be wider than 32 bits. +// + + srl a0, 32, t0 // get upper longword of dividend + cmpule a1, t0, t1 // is divisor <= upper dividend? + bne t1, 40f // if ne[true], then overflow trap + +// +// Perform the shift/subtract loop 8 times and 4 bits per loop. +// +// t0 - Temp used for 0/1 results of compares. +// t1 - High 64-bits of 128-bit (t1, a0) dividend. +// t2 - Loop counter. +// + + ldiq t2, 32/4 // set iteration count + + srl a0, 32, t1 // get top 32 bits of carry-out + sll a0, 32, a0 // preshift first 32 bits left + +10: cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + subq t2, 1, t2 // any more iterations? + bne t2, 10b // + +// +// Finished with remainder value in t1 and quotient value in a0. +// + + addl a0, 0, v0 // set longword quotient return value + beq a2, 20f // skip optional remainder store + stl t1, 0(a2) // store longword remainder + +20: ret zero, (ra) // return + +// +// Generate an exception for divide by zero and return a zero quotient if the +// caller continues execution. +// + +30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO + + GENERATE_TRAP + + ldil v0, 0 // return zero quotient + ret zero, (ra) // return + +// +// Generate an exception for overflow. +// + +40: ldiq a0, 0x8000000000000000 // + subqv zero, a0, v0 // negate in order to overflow + trapb // wait for trap to occur + ret zero, (ra) // return + + .end RtlEnlargedUnsignedDivide + + SBTTL("Enlarged Unsigned Integer Multiply") +//++ +// +// LARGE_INTEGER +// RtlEnlargedUnsignedMultiply ( +// IN ULONG Multiplicand, +// IN ULONG Multiplier +// ) +// +// Routine Description: +// +// This function multiplies an unsigned integer by an unsigned integer +// and returns a signed large integer result. +// +// Arguments: +// +// Multiplicand (a0) - Supplies the multiplicand value. +// +// Multiplier (a1) - Supplies the multiplier value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlEnlargedUnsignedMultiply) + + zap a0, 0xf0, a0 // convert canonical ULONG to quadword + zap a1, 0xf0, a1 // convert canonical ULONG to quadword + mulq a0, a1, v0 // multiply signed both quadwords + ret zero, (ra) // return + + .end RtlEnlargedUnsignedMultiply + + SBTTL("Extended Integer Multiply") +//++ +// +// LARGE_INTEGER +// RtlExtendedIntegerMultiply ( +// IN LARGE_INTEGER Multiplicand, +// IN LONG Multiplier +// ) +// +// Routine Description: +// +// This function multiplies a signed large integer by a signed integer and +// returns the signed large integer result. +// +// N.B. An overflow is possible, but no exception is generated. +// +// Arguments: +// +// Multiplicand (a0) - Supplies the multiplicand value. +// +// Multiplier (a1) - Supplies the multiplier value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlExtendedIntegerMultiply) + + addl a1, 0, a1 // ensure canonical (signed long) form + mulq a0, a1, v0 // multiply signed both quadwords + ret zero, (ra) // return + + .end RtlExtendedIntegerMultiply + + SBTTL("Extended Large Integer Divide") +//++ +// +// LARGE_INTEGER +// RtlExtendedLargeIntegerDivide ( +// IN LARGE_INTEGER Dividend, +// IN ULONG Divisor, +// IN OUT PULONG Remainder OPTIONAL +// ) +// +// Routine Description: +// +// This function divides an unsigned large integer by an unsigned long +// and returns the quadword quotient and optionally the long remainder. +// +// N.B. A divide by zero exception is possible. +// +// Arguments: +// +// Dividend (a0) - Supplies the dividend value. +// +// Divisor (a1) - Supplies the divisor value. +// +// Remainder (a2) - Supplies an optional pointer to a variable that +// receives the remainder. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlExtendedLargeIntegerDivide) + +// +// Check for division by zero. +// + + zap a1, 0xf0, a1 // convert canonical ULONG to quadword + beq a1, 30f // trap if divisor is zero + +// +// Perform the shift/subtract loop 16 times and 4 bits per loop. +// +// t0 - Temp used for 0/1 results of compares. +// t1 - High 64-bits of 128-bit (t1, a0) dividend. +// t2 - Loop counter. +// + + ldiq t2, 64/4 // set iteration count + + ldiq t1, 0 // zero-extend dividend to 128 bits + +10: cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + subq t2, 1, t2 // any more iterations? + bne t2, 10b // + +// +// Finished with remainder value in t1 and quotient value in a0. +// + + mov a0, v0 // set quadword quotient return value + beq a2, 20f // skip optional remainder store + stl t1, 0(a2) // store longword remainder + +20: ret zero, (ra) // return + +// +// Generate an exception for divide by zero. +// + +30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO + + GENERATE_TRAP + + br zero, 30b // in case they continue + + .end RtlExtendedLargeIntegerDivide + + SBTTL("Extended Magic Divide") +//++ +// +// LARGE_INTEGER +// RtlExtendedMagicDivide ( +// IN LARGE_INTEGER Dividend, +// IN LARGE_INTEGER MagicDivisor, +// IN CCHAR ShiftCount +// ) +// +// Routine Description: +// +// This function divides a signed large integer by an unsigned large integer +// and returns the signed large integer result. The division is performed +// using reciprocal multiplication of a signed large integer value by an +// unsigned large integer fraction which represents the most significant +// 64-bits of the reciprocal divisor rounded up in its least significant bit +// and normalized with respect to bit 63. A shift count is also provided +// which is used to truncate the fractional bits from the result value. +// +// Arguments: +// +// Dividend (a0) - Supplies the dividend value. +// +// MagicDivisor (a1) - Supplies the magic divisor value which +// is a 64-bit multiplicative reciprocal. +// +// Shiftcount (a2) - Supplies the right shift adjustment value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlExtendedMagicDivide) + +// +// Make the dividend positive for the reciprocal multiplication to work. +// + + negq a0, t0 // negate dividend + cmovgt a0, a0, t0 // get absolute value in t0 + +// +// Multiply both quadword arguments together and take only the upper 64 bits of +// the resulting 128 bit product. This can be done using the umulh instruction. +// +// Division of a dividend by a constant divisor through reciprocal +// multiplication works because a/b is equivalent to (a*x)/(b*x) for any +// value of x. Now if b is a constant, some "magic" integer x can be chosen, +// based on the value of b, so that (b*x) is very close to a large power of +// two (e.g., 2^64). Then a/b = (a*x)/(b*x) = (a*x)/(2^64) = (a*x)>>64. This +// effectively turns the problem of division by a constant into multiplication +// by a constant which is a much faster operation. +// + + umulh t0, a1, t1 // multiply high both quadword arguments + sra t1, a2, t1 // shift result right by requested amount + +// +// Make the result negative if the dividend was negative. +// + + negq t1, t0 // negate result + cmovgt a0, t1, t0 // restore sign of dividend + + mov t0, v0 // set quadword result return value + ret zero, (ra) // return + + .end RtlExtendedMagicDivide + + SBTTL("Large Integer Add") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerAdd ( +// IN LARGE_INTEGER Addend1, +// IN LARGE_INTEGER Addend2 +// ) +// +// Routine Description: +// +// This function adds a signed large integer to a signed large integer and +// returns the signed large integer result. +// +// N.B. An overflow is possible, but no exception is generated. +// +// Arguments: +// +// Addend1 (a0) - Supplies the first addend value. +// +// Addend2 (a1) - Supplies the second addend value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerAdd) + + addq a0, a1, v0 // add both quadword arguments + ret zero, (ra) // return + + .end RtlLargeIntegerAdd + + SBTTL("Large Integer Arithmetic Shift Right") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerArithmeticShift ( +// IN LARGE_INTEGER LargeInteger, +// IN CCHAR ShiftCount +// ) +// +// Routine Description: +// +// This function shifts a signed large integer right by an unsigned integer +// modulo 64 and returns the shifted signed large integer result. +// +// Arguments: +// +// LargeInteger (a0) - Supplies the large integer to be shifted. +// +// ShiftCount (a1) - Supplies the right shift count. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerArithmeticShift) + + sra a0, a1, v0 // shift the quadword right/arithmetic + ret zero, (ra) // return + + .end RtlLargeIntegerArithmeticShift + + SBTTL("Large Integer Divide") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerDivide ( +// IN LARGE_INTEGER Dividend, +// IN LARGE_INTEGER Divisor, +// IN OUT PLARGE_INTEGER Remainder OPTIONAL +// ) +// +// Routine Description: +// +// This function divides an unsigned large integer by an unsigned large +// integer and returns the quadword quotient and optionally the quadword +// remainder. +// +// N.B. A divide by zero exception is possible. +// +// Arguments: +// +// Dividend (a0) - Supplies the dividend value. +// +// Divisor (a1) - Supplies the divisor value. +// +// Remainder (a2) - Supplies an optional pointer to a variable that +// receives the remainder. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerDivide) + +// +// Check for division by zero. +// + + beq a1, 30f // trap if divisor is zero + +// +// Perform the shift/subtract loop 16 times and 4 bits per loop. +// +// t0 - Temp used for 0/1 results of compares. +// t1 - High 64-bits of 128-bit (t1, a0) dividend. +// t2 - Loop counter. +// + + ldiq t2, 64/4 // set iteration count + + ldiq t1, 0 // zero-extend dividend to 128 bits + +10: cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + cmplt a0, 0, t0 // predict low-dividend shift carry-out + addq a0, a0, a0 // shift low-dividend left + addq t1, t1, t1 // shift high-dividend left + bis t1, t0, t1 // merge in carry-out of low-dividend + + cmpule a1, t1, t0 // if dividend >= divisor, + addq a0, t0, a0 // then set quotient bit + subq t1, a1, t0 // subtract divisor from dividend, + cmovlbs a0, t0, t1 // if dividend >= divisor + + subq t2, 1, t2 // any more iterations? + bne t2, 10b // + +// +// Finished with remainder value in t1 and quotient value in a0. +// + + mov a0, v0 // set quadword quotient return value + beq a2, 20f // skip optional remainder store + stq t1, 0(a2) // store quadword remainder + +20: ret zero, (ra) // return + +// +// Generate an exception for divide by zero. +// + +30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO + + GENERATE_TRAP + + br zero, 30b // in case they continue + + .end RtlLargeIntegerDivide + + SBTTL("Large Integer Negate") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerNegate ( +// IN LARGE_INTEGER Subtrahend +// ) +// +// Routine Description: +// +// This function negates a signed large integer and returns the signed +// large integer result. +// +// N.B. An overflow is possible, but no exception is generated. +// +// Arguments: +// +// Subtrahend (a0) - Supplies the subtrahend value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerNegate) + + subq zero, a0, v0 // negate the quadword argument + ret zero, (ra) // return + + .end RtlLargeIntegerNegate + + SBTTL("Large Integer Shift Left") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerShiftLeft ( +// IN LARGE_INTEGER LargeInteger, +// IN CCHAR ShiftCount +// ) +// +// Routine Description: +// +// This function shifts a signed large integer left by an unsigned integer +// modulo 64 and returns the shifted signed large integer result. +// +// Arguments: +// +// LargeInteger (a0) - Supplies the large integer to be shifted. +// +// ShiftCount (a1) - Supplies the left shift count. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerShiftLeft) + + sll a0, a1, v0 // shift the quadword argument left + ret zero, (ra) // return + + .end RtlLargeIntegerShiftLeft + + SBTTL("Large Integer Logical Shift Right") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerShiftRight ( +// IN LARGE_INTEGER LargeInteger, +// IN CCHAR ShiftCount +// ) +// +// Routine Description: +// +// This function shifts an unsigned large integer right by an unsigned +// integer modulo 64 and returns the shifted unsigned large integer result. +// +// Arguments: +// +// LargeInteger (a0) - Supplies the large integer to be shifted. +// +// ShiftCount (a1) - Supplies the right shift count. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerShiftRight) + + srl a0, a1, v0 // shift the quadword right/logical + ret zero, (ra) // return + + .end RtlLargeIntegerShiftRight + + SBTTL("Large Integer Subtract") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerSubtract ( +// IN LARGE_INTEGER Minuend, +// IN LARGE_INTEGER Subtrahend +// ) +// +// Routine Description: +// +// This function subtracts a signed large integer from a signed large +// integer and returns the signed large integer result. +// +// N.B. An overflow is possible, but no exception is generated. +// +// Arguments: +// +// Minuend (a0) - Supplies the minuend value. +// +// Subtrahend (a1) - Supplies the subtrahend value. +// +// Return Value: +// +// The large integer result is returned as the function value in v0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerSubtract) + + subq a0, a1, v0 // subtract the quadword arguments + ret zero, (ra) // return + + .end RtlLargeIntegerSubtract + + SBTTL("128-bit Signed Integer Multiplication") +//++ +// +// VOID +// Rtlp128BitSignedMultiply ( +// IN LONGLONG Multiplicand, +// IN LONGLONG Multiplier, +// IN OUT PULONGLONG ProductLower, +// IN OUT PLONGLONG ProductUpper +// ) +// +// Routine Description: +// +// This function multiplies a signed quadword (or signed large integer) +// by a signed quadword (or signed large integer) and returns the full +// 128-bit signed product indirectly through pointers to two quadwords. +// +// N.B. Signed multiplication is implemented with an unsigned multiply +// followed by up to two subtractions. The subtractions are necessary for +// the following reason. Within an N-bit register, a negative N-bit two's +// compliment signed integer (-x), is equal to (2^N - x). So in this case +// of 64x64 bit multiplication, the following holds: +// +// (-x) * (-y) = +// (2^64 - x) * (2^64 - y) = +// 2^128 - (2^64)*x - (2^64)*y + (x*y) +// +// The lower 64-bits of the 128-bit product is determined solely by the +// (x*y) term. For a 128-bit result, the 2^128 term is irrelevant. And if +// either the x and/or the y operand is negative, then either y and/or x +// must be subtracted from the upper 64 bits of the 128-bit product. +// +// Arguments: +// +// Multiplicand (a0) - Supplies the multiplicand value. +// +// Multiplier (a1) - Supplies the multiplier value. +// +// ProductLower (a2) - Supplies a pointer to an unsigned quadword variable +// that receives the lower 64-bits of the product. +// +// ProductLower (a3) - Supplies a pointer to a signed quadword variable +// that receives the upper 64-bits of the product. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(Rtlp128BitSignedMultiply) + + mulq a0, a1, t0 // get lower 64 bits of product + stq t0, 0(a2) // store lower half of 128-bit product + + umulh a0, a1, t1 // get upper 64 bits of product + subq t1, a0, t2 // subtract first operand from product + cmovge a1, t1, t2 // if second operand is negative + subq t2, a1, t3 // subtract second operand from product + cmovge a0, t2, t3 // if first operand is negative + stq t3, 0(a3) // store upper half of 128-bit product + + ret zero, (ra) // return + + .end Rtlp128BitSignedMultiply + + SBTTL("128-bit Unsigned Integer Multiplication") +//++ +// +// VOID +// Rtlp128BitUnsignedMultiply ( +// IN ULONGLONG Multiplicand, +// IN ULONGLONG Multiplier, +// IN OUT PULONGLONG ProductLower, +// IN OUT PULONGLONG ProductUpper +// ) +// +// Routine Description: +// +// This function multiplies an unsigned quadword (or large integer) by an +// unsigned quadword (or large integer) and returns the full 128-bit unsigned +// product indirectly through pointers to two unsigned quadwords. +// +// Arguments: +// +// Multiplicand (a0) - Supplies the multiplicand value. +// +// Multiplier (a1) - Supplies the multiplier value. +// +// ProductLower (a2) - Supplies a pointer to an unsigned quadword variable +// that receives the lower 64-bits of the product. +// +// ProductLower (a3) - Supplies a pointer to an unsigned quadword variable +// that receives the upper 64-bits of the product. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(Rtlp128BitUnsignedMultiply) + + mulq a0, a1, t0 // get lower 64 bits of product + stq t0, 0(a2) // store lower half of 128-bit product + + umulh a0, a1, t1 // get upper 64 bits of product + stq t1, 0(a3) // store upper half of 128-bit product + + ret zero, (ra) // return + + .end Rtlp128BitUnsignedMultiply diff --git a/private/ntos/rtl/alpha/localrtl.c b/private/ntos/rtl/alpha/localrtl.c new file mode 100644 index 000000000..e8e91b9ab --- /dev/null +++ b/private/ntos/rtl/alpha/localrtl.c @@ -0,0 +1,160 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + localrtl.c + +Abstract: + + This module contains alternate implementations of each of the Rtl Memory + functions and other common functions required by the Rtl Memory test + programs. + +Author: + + Thomas Van Baak (tvb) 11-Jan-1993 + +Revision History: + +--*/ + +#include +#include "localrtl.h" + +// +// Simple pattern generator. +// + +#define PATTERN "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define PATTERN_SIZE (sizeof(PATTERN) - 1) +UCHAR Pattern[] = PATTERN; + +VOID +FillPattern(PUCHAR To, ULONG Length) +{ + ULONG Index; + ULONG Rotor = 0; + + for (Index = 0; Index < Length; Index += 1) { + To[Index] = Pattern[Rotor]; + Rotor += 1; + if (Rotor == PATTERN_SIZE) { + Rotor = 0; + } + } +} + +// +// The following functions are simple, non-optimized, (and thus maybe even +// bug-proof) implementations of each of the Rtl Memory functions. +// + +ULONG +LocalCompareMemory ( + PVOID Source1, + PVOID Source2, + ULONG Length + ) +{ + ULONG Index; + PUCHAR Left = Source1; + ULONG Match; + PUCHAR Right = Source2; + + Match = 0; + for (Index = 0; Index < Length; Index += 1) { + if (Left[Index] != Right[Index]) { + break; + } + Match += 1; + } + return Match; +} + +ULONG +LocalCompareMemoryUlong ( + PVOID Source, + ULONG Length, + ULONG Pattern + ) +{ + PULONG From = Source; + ULONG Index; + ULONG Match; + + Match = 0; + for (Index = 0; Index < Length / sizeof(ULONG); Index += 1) { + if (From[Index] != Pattern) { + break; + } + Match += sizeof(ULONG); + } + return Match; +} + +VOID +LocalMoveMemory ( + PVOID Destination, + PVOID Source, + ULONG Length + ) +{ + PUCHAR From = Source; + ULONG Index; + PUCHAR To = Destination; + + for (Index = 0; Index < Length; Index += 1) { + if (To <= From) { + To[Index] = From[Index]; + + } else { + To[Length - 1 - Index] = From[Length - 1 - Index]; + } + } +} + +VOID +LocalFillMemory ( + PVOID Destination, + ULONG Length, + UCHAR Fill + ) +{ + ULONG Index; + PUCHAR To = Destination; + + for (Index = 0; Index < Length; Index += 1) { + To[Index] = Fill; + } +} + +VOID +LocalFillMemoryUlong ( + PVOID Destination, + ULONG Length, + ULONG Pattern + ) +{ + ULONG Index; + PULONG To = Destination; + + for (Index = 0; Index < Length / sizeof(ULONG); Index += 1) { + To[Index] = Pattern; + } +} + +VOID +LocalZeroMemory ( + PVOID Destination, + ULONG Length + ) +{ + ULONG Index; + PUCHAR To = Destination; + + for (Index = 0; Index < Length; Index += 1) { + To[Index] = 0; + } +} diff --git a/private/ntos/rtl/alpha/localrtl.h b/private/ntos/rtl/alpha/localrtl.h new file mode 100644 index 000000000..5445f2b38 --- /dev/null +++ b/private/ntos/rtl/alpha/localrtl.h @@ -0,0 +1,82 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + localrtl.h + +Abstract: + + This module contains the local Rtl header file. + +Author: + + Thomas Van Baak (tvb) 11-Jan-1993 + +Revision History: + +--*/ + +// +// Define function prototypes of local Rtl Memory functions. +// + +ULONG +LocalCompareMemory ( + PVOID Source1, + PVOID Source2, + ULONG Length + ); + +ULONG +LocalCompareMemoryUlong ( + PVOID Source, + ULONG Length, + ULONG Pattern + ); + +VOID +LocalMoveMemory ( + PVOID Destination, + CONST VOID *Source, + ULONG Length + ); + +VOID +LocalFillMemory ( + PVOID Destination, + ULONG Length, + UCHAR Fill + ); + +VOID +LocalFillMemoryUlong ( + PVOID Destination, + ULONG Length, + ULONG Pattern + ); + +VOID +LocalZeroMemory ( + PVOID Destination, + ULONG Length + ); + +// +// Define function prototypes of other common functions. +// + +VOID +FillPattern( + PUCHAR To, + ULONG Length + ); + +// +// Define maximum values for the string tests. +// + +#define MAX_MARGIN 8 +#define MAX_OFFSET 32 +#define MAX_LENGTH 72 diff --git a/private/ntos/rtl/alpha/longjmp.s b/private/ntos/rtl/alpha/longjmp.s new file mode 100644 index 000000000..1999d1573 --- /dev/null +++ b/private/ntos/rtl/alpha/longjmp.s @@ -0,0 +1,200 @@ +// TITLE("Long Jump") +//++ +// +// Copyright (c) 1993 Microsoft Corporation +// Copyright (c) 1993 Digital Equipment Corporation +// +// Module Name: +// +// longjmp.s +// +// Abstract: +// +// This module implements the Alpha specific routine to perform a long +// jump operation. Three jump buffer types are supported: unsafe, safe +// acc-style (virtual frame pointer, PC mapped SEH scope), and safe +// GEM-style (real frame pointer, SEB-based SEH context). +// +// N.B. This function has been replaced by setjmp/setjmpex/longjmp in the +// C runtime library. It remains here for backwards compatibility of +// Beta 2 applications expecting setjmp and longjmp to be present in +// ntdll, or for new acc or GEM compiled applications that link with +// ntdll before libc. +// +// Author: +// +// David N. Cutler (davec) 2-Apr-1993 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +// Thomas Van Baak (tvb) 22-Apr-1993 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + +// +// Define jump buffer types. +// +// _JMPBUF_TYPE_ZERO was used by the Beta 2 ntdll setjmp and can be handled +// the same as _JMPBUF_TYPE_ACC. +// +// _JMPBUF_TYPE_FAST is for jump buffers containing the set of non-volatile +// integer and floating registers. This form of setjmp/longjmp is not +// compatible with SEH. It is an order of magnitude faster however. +// +// _JMPBUF_TYPE_ACC is for setjmp/longjmp compatible with SEH. The Alpha +// acc compiler uses a virtual frame pointer, and SEH scope is inferred +// from the PC through passive PC-mapping scope tables. +// +// _JMPBUF_TYPE_GEM is for setjmp/longjmp compatible with SEH. The Alpha +// GEM C compiler uses a real frame pointer, and SEH scope is maintained +// actively with an SEB pointer. +// + +#define _JMPBUF_TYPE_ZERO 0 +#define _JMPBUF_TYPE_FAST 1 +#define _JMPBUF_TYPE_ACC 2 +#define _JMPBUF_TYPE_GEM 3 + + SBTTL("Long Jump") +//++ +// +// int +// longjmp ( +// IN jmp_buf JumpBuffer, +// IN int ReturnValue +// ) +// +// Routine Description: +// +// This function performs a long jump to the context specified by the +// jump buffer. +// +// Arguments: +// +// JumpBuffer (a0) - Supplies the address of a jump buffer that contains +// jump information. +// +// N.B. This is an array of double's to force quadword alignment. +// +// ReturnValue (a1) - Supplies the value that is to be returned to the +// caller of set jump. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(longjmp) + + ldil t0, 1 // force nonzero value, if + cmoveq a1, t0, a1 // given return value is zero + + ldl t1, JbType(a0) // get setjmp context type flag + subq t1, 1, t2 // if eq 1, fast, unsafe longjmp + bne t2, 10f // otherwise, provide safe longjmp + +// +// Type 0x1: Provide unsafe handling of longjmp. +// + + mov a1, v0 // set return value + .set noreorder + .set noat + ldt f2, JbFltF2(a0) // restore floating registers f2 - f9 + ldt f3, JbFltF3(a0) // + ldt f4, JbFltF4(a0) // + ldt f5, JbFltF5(a0) // + ldt f6, JbFltF6(a0) // + ldt f7, JbFltF7(a0) // + ldt f8, JbFltF8(a0) // + ldt f9, JbFltF9(a0) // + + ldq s0, JbIntS0(a0) // restore integer registers s0 - s6/fp + ldq s1, JbIntS1(a0) // + ldq s2, JbIntS2(a0) // + ldq s3, JbIntS3(a0) // + ldq s4, JbIntS4(a0) // + ldq s5, JbIntS5(a0) // + ldq fp, JbIntS6(a0) // + .set at + .set reorder + + ldq a1, JbFir(a0) // get setjmp return address + ldq sp, JbIntSp(a0) // restore stack pointer + jmp zero, (a1) // jump back to setjmp site + +// +// Type 0x0: Provide safe handling of longjmp (idw 404 style). +// Type 0x2: Provide safe handling of longjmp (acc style). +// + +10: bic t1, 0x2, t2 // if 0 or 2, safe acc longjmp + bne t2, longjmpRfp // if not, safe GEM longjmp + + mov a1, a3 // set return value + mov zero, a2 // set exception record address + ldl a1, JbPc(a0) // set target instruction address + ldl a0, JbFp(a0) // set target virtual frame pointer + br zero, RtlUnwind // finish in common code + + .end longjmp + + SBTTL("Long Jump - GEM") + + .struct 0 +LjRa: .space 8 // saved return address + .space 8 // padding for 16-byte stack alignment +LjEr: .space ExceptionRecordLength // local exception record +LongjmpFrameLength: + +// +// Type 0x3: Provide safe handling of longjmp (GEM style). +// + + NESTED_ENTRY(longjmpRfp, LongjmpFrameLength, ra) + + lda sp, -LongjmpFrameLength(sp) // allocate stack frame + stq ra, LjRa(sp) // save return address + + PROLOGUE_END + +// +// The SEB is passed to the GEM exception handler through an exception +// record. Set up the following local exception record: +// +// ExceptionRecord.ExceptionCode = STATUS_UNWIND; +// ExceptionRecord.ExceptionFlags = EXCEPTION_UNWINDING; +// ExceptionRecord.ExceptionRecord = NULL; +// ExceptionRecord.ExceptionAddress = 0; +// ExceptionRecord.NumberParameters = 1; +// ExceptionRecord.ExceptionInformation[0] = Seb; +// + +10: mov a1, a3 // set return value + lda a2, LjEr(sp) // set exception record address + + ldil t0, STATUS_UNWIND // get status code + stl t0, ErExceptionCode(a2) // store in exception record + ldil t1, EXCEPTION_UNWINDING // get exception flags + stl t1, ErExceptionFlags(a2) // store in exception record + stl zero, ErExceptionRecord(a2) // clear indirect exception record + stl zero, ErExceptionAddress(a2) // clear exception address + ldil t2, 1 // get number of parameters + stl t2, ErNumberParameters(a2) // store in exception record + ldl t3, JbSeb(a0) // get SEB pointer + stl t3, ErExceptionInformation(a2) // store in exception record + ldl a1, JbPc(a0) // set target instruction address + ldl a0, JbFp(a0) // set target real frame pointer + + bsr ra, RtlUnwindRfp // finish in common code + + .end longjmpRfp diff --git a/private/ntos/rtl/alpha/lzntaxp.s b/private/ntos/rtl/alpha/lzntaxp.s new file mode 100644 index 000000000..2eea2462d --- /dev/null +++ b/private/ntos/rtl/alpha/lzntaxp.s @@ -0,0 +1,485 @@ +// TITLE("Decompression Engine") +//++ +// +// Copyright (c) 1994 Microsoft Corporation +// +// Module Name: +// +// lzntaxp.s +// +// Abstract: +// +// This module implements the lznt1 decompression engine needed +// to support file system decompression. +// +// Author: +// +// John Vert (jvert) 19-Jul-1994 +// +// Environment: +// +// Any. +// +// Revision History: +// +//-- + +#include "ksalpha.h" + + + SBTTL("Decompress a buffer") +//++ +// NTSTATUS +// LZNT1DecompressChunk ( +// OUT PUCHAR UncompressedBuffer, +// IN PUCHAR EndOfUncompressedBufferPlus1, +// IN PUCHAR CompressedBuffer, +// IN PUCHAR EndOfCompressedBufferPlus1, +// OUT PULONG FinalUncompressedChunkSize +// ) +// +// Routine Description: +// +// This function decodes a stream of compression tokens and places the +// resultant output into the destination buffer. The format of the input +// is described ..\lznt1.c. As the input is decoded, checks are made to +// ensure that no data is read past the end of the compressed input buffer +// and that no data is stored past the end of the output buffer. Violations +// indicate corrupt input and are indicated by a status return. +// +// +// Arguments: +// +// UncompressedBuffer (a0) - pointer to destination of uncompression. +// +// EndOfUncompressedBufferPlus1 (a1) - pointer just beyond the +// output buffer. This is used for consistency checking of the stored +// compressed data. +// +// CompressedBuffer (a2) - pointer to compressed source. This begins +// with a header word followed by a tag byte describing which of the +// following tokens are literals and which are copy groups. +// +// EndOfCompressedBufferPlus1 (a3) - pointer just beyond end of input +// buffer. This is used to terminate the decompression. +// +// FinalUncompressedChunkSize (a4) - pointer to a returned decompressed +// size. This has meaningful data ONLY when LZNT1DecompressChunk returns +// STATUS_SUCCESS +// +// Return Value: +// +// STATUS_SUCCESS is returned only if the decompression consumes thee entire +// input buffer and does not exceed the output buffer. +// STATUS_BAD_COMPRESSION_BUFFER is returned when the output buffer would be +// overflowed. +//-- +// +// +// Register usage: +// a0 - current destination pointer +// a1 - end of output buffer +// a2 - current source pointer +// a3 - end of compressed buffer +// a4 - pointer to decompressed size +// a5 - current decompressed size +// v0 - boundary for next format transition +// t0 - count of consecutive copy tokens +// t1 - current flag byte +// t2 - bits of t1 processed +// t3 - temp +// t4 - temp +// t5 - bytes following flag byte +// t6 - temp +// t7 - temp +// t8 - temp +// t9 - temp +// t10 - temp +// t11 - current length mask +// t12 - current displacement shift +// + LEAF_ENTRY(LZNT1DecompressChunk) + + bis zero, zero, a5 // initialize decompressed size + ldil t12, 12 // get initial displacement shift + lda t11, -1(zero) + sll t11, 12, t11 // get initial length mask + + addq a0, 16, v0 // get displacement boundary + subq a3, 1, a3 // adjust input buffer end +10: + addq a0, 8, t3 // check for at least 8 bytes available output + addq a2, 17, t4 // check for at least 17 bytes available input + cmpule t3, a1, t2 // check for output buffer exceeded + cmpule t4, a3, t3 // check for input buffer exceeded + ldq_u t0, 0(a2) // load flag byte and any subsequent bytes + extbl t0, a2, t1 // extract flag byte + addq a2, 1, a2 + beq t3, CopyTailFlag // input buffer exceeded + ldq_u t6, 7(a2) // load subsequent bytes + and a2, 7, t10 // check for qword alignment + extql t0, a2, t3 // extract low part of next 8 bytes + extqh t6, a2, t4 // extract high part + bis t3, t4, t5 // merge + cmoveq t10, t6, t5 // qword aligned, undo merge + beq t2, CopyTailFlag // output buffer exceeded + bne t1, 20f // !=0 deal with copy tokens + +// +// This is the special case where the next 8 bytes are literal tokens. +// + addq a5, 8, a5 // increment bytes copied + addq a0, 8, a0 // increment destination pointer + lda a2, 8(a2) // compute pointer to next tag byte + and a0, 7, t4 // check for qword-aligned destination + bne t4, 15f + +// +// Destination is quadword aligned, do direct store +// + stq t5, -8(a0) + br zero, 10b // do next tag byte +15: +// +// Destination is not quadword aligned, merge eight bytes into buffer. +// + ldq_u t4, -8(a0) // get low destination + mskql t4, a0, t0 // clear position in destination + insql t5, a0, t2 // get low part in position + bis t0, t2, t4 // merge in new bytes + stq_u t4, -8(a0) // store low part + ldq_u t4, -1(a0) // get high destination + mskqh t4, a0, t0 // clear position in destination + insqh t5, a0, t2 // get high part in position + bis t0, t2, t4 // merge in new bytes + stq_u t4, -1(a0) // store high part + br zero, 10b // do next tag byte + +20: +// +// Tag indicates both literal bytes and copy tokens. The approach +// we use here is to loop through the bits counting the consecutive +// literal bytes until we find a copy token. +// + bis zero, zero, t0 // set bit count to zero + ldil t2, 8 // set count of bits to process +25: + blbs t1, CopyToken // go copy the token. + +// +// Count the consecutive clear bits. +// + srl t1, 1, t1 + addq t0, 1, t0 + blbs t1, 30f + srl t1, 1, t1 + addq t0, 1, t0 + blbs t1, 30f + srl t1, 1, t1 + addq t0, 1, t0 + blbs t1, 30f + srl t1, 1, t1 + addq t0, 1, t0 + blbs t1, 30f + srl t1, 1, t1 + addq t0, 1, t0 + blbs t1, 30f + srl t1, 1, t1 + addq t0, 1, t0 + blbs t1, 30f + srl t1, 1, t1 + addq t0, 1, t0 +30: + bis zero, 1, t9 // compute byte mask + sll t9, t0, t3 + subq t3, 1, t9 + zapnot t5, t9, t4 // get masked bytes to store + and a0, 7, t3 // get byte position of dest + addq t3, t0, t10 // compute ending offset + sll t9, t3, t9 // shift byte mask into position + ldq_u t7, 0(a0) // get low part of dest. + insql t4, t3, t6 // insert source bytes into position + zap t7, t9, t8 // clear dest + bis t8, t6, t7 // merge bytes to store + stq_u t7, 0(a0) // store merged result + +// +// Check to see whether the bytes to store extend into the next +// quadword of the destination. +// + cmpult t10, 8, t9 + bne t9, 40f // ending offset < 8, next quadword unaffected + insqh t4, t3, t6 // shift source bytes into position + stq_u t6, 8(a0) // store merged results + +40: + addq a0, t0, a0 // adjust destination pointer + addq a2, t0, a2 // adjust source pointer + addq a5, t0, a5 // adjust bytes copied + subq t2, t0, t2 // adjust flag bits left + +CopyToken: +// +// Get the token word +// + ldq_u t10, 0(a2) + ldq_u t6, 1(a2) + extwl t10, a2, t7 + extwh t6, a2, t8 + bis t7, t8, t9 + +// +// Check the displacement and length. +// +50: + cmpult v0, a0, t10 + bne t10, UpdateFormat // if nez, max displacement < output + srl t9, t12, t7 // compute offset + andnot t9, t11, t8 // compute length + addq t8, 3, t8 + addq t7, 1, t7 + +// +// Check displacement against number of bytes copied +// + cmpule t7, a5, t10 + beq t10, ErrorExit // if eqz, bytes copied <= displacement +// +// Account for end of output buffer and compute ending +// address of copy. +// + addq a0, t8, t9 + cmpule t9, a1, t10 + cmoveq t10, a1, t9 // if ending address > buffer end, set + // buffer end to ending address + subq a0, t7, t5 // compute copy source +// +// Do the copy. +// t5 - source +// a0 - dest +// t9 - end of destination +// +// If the source is more than eight bytes away from the destination, +// we can copy a quadword at a time. Otherwise, we must copy a byte +// at a time to ensure that fills work correctly. +// + + subq t9, a0, t7 // compute number of bytes to copy + addq a5, t7, a5 // adjust bytes copied here, the only + // time this will not be correct is + // in an error condition. + subq a0, t5, t10 // test if source is >= 8 bytes away + cmpult t10, 8, t10 // from destination + bne t10, FillBytes // if so, do byte fill + +// +// Write the low part of the first quadword out. This will cause the +// destination to become qword aligned. +// + ldq_u t10, 0(t5) // get low part of source qword + ldq_u t8, 7(t5) // get high part of source qword + extql t10, t5, t7 + extqh t8, t5, t3 + bis t3, t7, t10 // get aligned qword + ldq_u t7, 0(a0) // get low part of source destination + insql t10, a0, t3 + mskql t7, a0, t4 // clear bytes in destination + bis t4, t3, t7 // merge qword into destination + stq_u t7, 0(a0) // store low part of quadword + + addq a0, 8, t10 // compute qword-aligned destination + bic t10, 7, t10 + subq t10, a0, t8 + addq t5, t8, t5 // increment source + bis t10, zero, a0 // increment destination + +// +// Recompute number of quadwords to copy now that the destination has +// been qword aligned +// + subq t9, a0, t7 + cmovlt t7, t9, a0 // back up destination if we went too far + ble t7, 64f // no bytes remaining + srl t7, 3, t4 + and t5, 7, t3 // get alignment of source + ldq_u t10, 0(t5) + bne t3, UnalignedQwordCopy + beq t4, 60f // no qwords remaining + +AlignedQwordLoop: + stq t10, 0(a0) // store qword + addq t5, 8, t5 // increment source + addq a0, 8, a0 // increment dest + subq t4, 1, t4 // decrement remaining qword + ldq t10, 0(t5) // get next qword + bne t4, AlignedQwordLoop + cmpult a0, t9, t4 + beq t4, 64f // no bytes reamining +// +// Tail bytes are in t10, go ahead and store them. +// We know we will not store beyond the containing qword of +// the end of the buffer +// +60: + stq t10, 0(a0) + bis t9, zero, a0 // increment dest + br zero, 64f + +UnalignedQwordCopy: + beq t4, 65f // no qword remaining + +UnalignedQwordLoop: + ldq_u t8, 8(t5) + extql t10, t5, t10 + extqh t8, t5, t7 + bis t7, t10, t10 + stq t10, 0(a0) + bis t8, zero, t10 + addq t5, 8, t5 // increment source + addq a0, 8, a0 // increment dest + subq t4, 1, t4 // decrement remaining qwords + bne t4, UnalignedQwordLoop + cmpult a0, t9, t4 + beq t4, 64f // no bytes remaining + +// +// Low word of the tail bytes are in t10 +// Get the high part, then go ahead and store them. +// We know we will not store beyond the containing qword +// of the end of the buffer. +// +65: + ldq_u t8, 8(t5) // get high part of tail bytes + extql t10, t5, t7 // extract low part + extqh t8, t5, t4 // extract high part + bis t7, t4, t10 // merge + stq t10, 0(a0) // store result + bis t9, zero, a0 // increment dest. + br zero, 64f + +FillBytes: + ldq_u t10, 0(t5) + ldq_u t8, 0(a0) + extbl t10, t5, t7 + insbl t7, a0, t10 + mskbl t8, a0, t4 + bis t10, t4, t7 + stq_u t7, 0(a0) + addq a0, 1, a0 + addq t5, 1, t5 + cmpult a0, t9, t4 + bne t4, FillBytes +64: +// +// Token successfully copied. +// + addq a2, 2, a2 + subq t2, 1, t2 // decrement remaining bits + srl t1, 1, t1 // shift flag byte + beq t2, 10b // no more bits remaining + + addq a0, t2, t3 // check for enough output bytes remaining + cmpule t3, a1, t4 + beq t4, CopyTail + + addq a2, 14, t3 // check for enough input bytes remaining + cmpule t3, a3, t4 + beq t4, CopyTail + + addq a2, t2, t3 // point to last byte. +// +// Get remaining bytes +// + bis zero, zero, t0 // set # clear bits back to zero + ldq_u t5, 0(t3) + and a2, 7, t7 + beq t7, 65f // source quadword aligned, no shift/merge required + extql t6, a2, t4 + extqh t5, a2, t8 + bis t4, t8, t5 +65: + bne t1, 25b // if any literal tokens remain, repeat + + bis zero, 1, t9 // compute byte mask + sll t9, t2, t3 + subq t3, 1, t9 + zapnot t5, t9, t4 // get masked bytes to store + and a0, 7, t3 // get byte position of dest + addq t3, t2, t10 // compute ending offset + sll t9, t3, t9 // shift byte mask into position + ldq_u t7, 0(a0) // get low part of dest. + insql t4, t3, t6 // insert source bytes into position + zap t7, t9, t8 // clear dest + bis t8, t6, t7 // merge bytes to store + stq_u t7, 0(a0) // store merged result + +// +// Check to see whether the bytes to store extend into the next +// quadword of the destination. +// + cmpult t10, 8, t9 + bne t9, 70f // ending offset < 8, next quadword unaffected + insqh t4, t3, t6 // insert source bytes into position + stq_u t6, 8(a0) // store merged results + +70: + addq a0, t2, a0 // adjust destination pointer + addq a2, t2, a2 // adjust source pointer + addq a5, t2, a5 // adjust bytes copied + br zero, 10b + +UpdateFormat: + subq a0, a5, t10 // compute original pointer + subq v0, t10, t7 // compute current max displacement + sll t7, 1, t7 // + addq t7, t10, v0 // compute new max displacemnt + srl t11, 1, t11 // compute new length mask + subq t12, 1, t12 // compute new displacement shift + br zero, 50b // start again. + +// +// a0 - the destination +// a1 - the last byte of the destination +// t1 - flag byte +// +CopyTailFlag: + ldil t2, 8 // set count of bits to process +CopyTail: + cmpult a0, a1, t10 + beq t10, SuccessExit // finished + cmpule a2, a3, t10 + beq t10, SuccessExit // finished + blbc t1, CT15 // skip copy token + cmpeq a2, a3, t10 + beq t10, CopyToken // more than one byte left + br zero, ErrorExit // only one byte left, error +CT15: + ldq_u t10, 0(a2) + extbl t10, a2, t5 + ldq_u t7, 0(a0) + insbl t5, a0, t6 + mskbl t7, a0, t8 + bis t6, t8, t7 + stq_u t7, 0(a0) + addq a0, 1, a0 + addq a2, 1, a2 + srl t1, 1, t1 + subq t2, 1, t2 + addq a5, 1, a5 + bne t2, CopyTail + cmpule a2, a3, t10 + beq t10, SuccessExit // finished + ldq_u t0, 0(a2) // load flag byte and any subsequent bytes + extbl t0, a2, t1 // extract flag byte + addq a2, 1, a2 + br zero, CopyTailFlag + +SuccessExit: + bis zero, zero, v0 + stl a5, 0(a4) + ret zero, (ra) + +ErrorExit: + ldil v0, STATUS_BAD_COMPRESSION_BUFFER + ret zero, (ra) + .end LZNT1DecompressChunk diff --git a/private/ntos/rtl/alpha/mvmem.s b/private/ntos/rtl/alpha/mvmem.s new file mode 100644 index 000000000..c5ccc9a81 --- /dev/null +++ b/private/ntos/rtl/alpha/mvmem.s @@ -0,0 +1,1920 @@ +// TITLE("Compare, Move, Zero, and Fill Memory Support") +//++ +// +// Copyright (c) 1992 Digital Equipment Corporation +// +// Module Name: +// +// mvmem.s +// +// Abstract: +// +// This module implements functions to compare, move, zero, and fill +// blocks of memory. If the memory is aligned, then these functions +// are very efficient. +// +// N.B. These routines MUST preserve all floating state since they are +// frequently called from interrupt service routines that normally +// do not save or restore floating state. +// +// Author: +// +// Joe Notarangelo 21-May-1992 +// +// Environment: +// +// User or Kernel mode. +// +// Revision History: +// +// Monty VanderBilt 14-Feb-1996 Avoid memory loads and branch takens between +// load lock and store conditional instructions +// to conform with all alpha architecture rules. +// Monty VanderBilt 27-Feb-1996 Added RtlZeroBytes and RtlFillBytes to support +// byte granularity access when necessary. +//-- + +#include "ksalpha.h" + + SBTTL("Compare Memory") +//++ +// +// ULONG +// RtlCompareMemory ( +// IN PVOID Source1, +// IN PVOID Source2, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function compares two blocks of memory and returns the number +// of bytes that compared equal. +// +// Arguments: +// +// Source1 (a0) - Supplies a pointer to the first block of memory to +// compare. +// +// Source2 (a1) - Supplies a pointer to the second block of memory to +// compare. +// +// Length (a2) - Supplies the length, in bytes, of the memory to be +// compared. +// +// Return Value: +// +// The number of bytes that compared equal is returned as the function +// value. If all bytes compared equal, then the length of the orginal +// block of memory is returned. +// +//-- + + + LEAF_ENTRY(RtlCompareMemory) + + bis a2, zero, v0 // save length of comparison + beq a2, 90f // (JAE) quit if nothing to compare + xor a0, a1, t0 // check for compatible alignment + and t0, 0x7, t0 // low bits only + bne t0, CompareUnaligned // if ne, incompatible alignment + +// +// Compare memory aligned +// + +CompareAligned: // + +// +// compare memory until sources are aligned +// + and a0, 0x7, t0 // get low bits + bne t0, 10f // if ne, sources not aligned yet + br zero, 30f // already aligned, predicted + + +10: + ldq_u t1, 0(a0) // get unaligned quad at source 1 + ldq_u t2, 0(a1) // get unaligned quad at source 2 + +20: + extbl t1, t0, t4 // byte at t0 in source 1 quad + extbl t2, t0, t5 // byte at t0 in source 2 quad + xor t4, t5, t3 // t1 = t2 ? + bne t3, 110f // not equal, miscompare + subq a2, 1, a2 // decrement bytes to compare + beq a2, 90f // if eq, compare success + addq t0, 1, t0 // increment pointer within quad + cmpeq t0, 8, t3 // t0 = 8?, if so first quadword done + beq t3, 20b // continue while t0 < 8 + + + addq a0, 8, a0 // increment to next quadword + addq a1, 8, a1 // increment source 2 to next also + bic a0, 7, a0 // align source 1 quadword + bic a1, 7, a1 // align source 2 quadword + + +// +// aligned block compare, compare blocks of 64 bytes +// + +30: + srl a2, 6, t0 // t0 = number of 64 byte blocks + beq t0, 50f // if eq, no 64 byte blocks + +// +// N.B. loads from each of the sources were separated in case these +// blocks are fighting for the cache +// + .set noat +40: + ldq t1, 0(a0) // t1 = source 1, quad 0 + ldq t2, 8(a0) // t2 = source 1, quad 1 + ldq t3, 16(a0) // t3 = source 1, quad 2 + addq a1, 64, a1 // increment source 2 pointer + ldq t4, 24(a0) // t4 = source 1, quad 3 + + ldq t5, -64(a1) // t5 = source 2, quad 0 + ldq a4, -56(a1) // a4 = source 2, quad 1 + ldq a5, -48(a1) // a5 = source 2, quad 2 + xor t1, t5, $at // quad 0 match? + bne $at, 200f // if ne[false], miscompare + ldq t5, -40(a1) // t5 = source 2, quad 3 + ldq t1, 32(a0) // t1 = source 1, quad 4 + xor t2, a4, $at // quad 1 match? + bne $at, 122f // if ne[false], miscompare + ldq t2, 40(a0) // t2 = source 1, quad 5 + xor t3, a5, $at // quad 2 match? + bne $at, 124f // if ne[false], miscompare + ldq t3, 48(a0) // t3 = source 1, quad 6 + xor t4, t5, $at // quad 3 match? + bne $at, 126f // if ne[false], miscompare + ldq t4, 56(a0) // t4 = source 1, quad 7 + + ldq t5, -32(a1) // t5 = source 2, quad 4 + addq a0, 64, a0 // increment source 1 pointer + ldq a4, -24(a1) // a4 = source 2, quad 5 + subq t0, 1, t0 // decrement blocks to compare + ldq a5, -16(a1) // a5 = source 2, quad 6 + xor t1, t5, $at // quad 4 match? + bne $at, 130f // if ne[false], miscompare + ldq t5, -8(a1) // t5 = source 2, quad 7 + xor t2, a4, $at // quad 5 match? + bne $at, 132f // if ne[false], miscompare + xor t3, a5, $at // quad 6 match? + bne $at, 134f // if ne[false], miscompare + xor t4, t5, $at // quad 7 match? + bne $at, 136f // if ne[false], miscompare + subq a2, 64, a2 // decrement bytes to compare + bne t0, 40b // if ne, more blocks to compare + .set at + + +// +// Compare quadwords +// + +50: + srl a2, 3, t0 // t0 = number of quadwords to compare + beq t0, 70f // if eq, no quadwords to compare + + .set noat +60: + ldq t1, 0(a0) // t1 = quad from source 1 + lda a0, 8(a0) // increment source 1 pointer + ldq t2, 0(a1) // t2 = quad from source 2 + lda a1, 8(a1) // increment source 2 pointer + xor t1, t2, $at // are quadwords equal? + bne $at, 200f // if ne, miscompare + subq t0, 1, t0 // decrement quads to compare + subq a2, 8, a2 // decrement bytes to compare + bne t0, 60b // if ne, more quads to compare + + .set at + +// +// Compare bytes in last quadword +// + +// a2 = number of bytes to compare, less than 8, greater than zero +// a0, a1, quad-aligned to last quadword + + beq a2, 80f // if eq, all bytes compared + + .set noat +70: + ldq t1, 0(a0) // t1 = quad at source 1 + ldq t2, 0(a1) // t2 = quad at source 2 + bis zero, 0xff, t0 // zap mask + sll t0, a2, t0 // + zap t1, t0, t1 // zero bytes not compared + zap t2, t0, t2 // same for source 2 + xor t1, t2, $at // compare quadwords + bne $at, 200f // if ne, miscompare + + .set at +// +// Successful compare +// v0 already contains full length +// + +80: + ret zero, (ra) // return + + +// +// Sources have incompatible alignment +// +CompareUnaligned: + + +// +// Compare until source 1 (a0) is aligned +// + + and a0, 0x7, t0 // get byte position of pointer + beq t0, 30f // if eq, already aligned + + ldq_u t1, 0(a0) // get unaligned quad at a0 + +10: + ldq_u t2, 0(a1) // get unaligned quad at a1 + extbl t1, t0, t4 // get byte to compare from source 1 + extbl t2, a1, t2 // get byte to compare from source 2 + xor t4, t2, t3 // do bytes match? + bne t3, 110f // if ne, miscompare + subq a2, 1, a2 // decrement bytes to compare + beq a2, 90f // (JAE) quit if nothing left to compare + addq t0, 1, t0 // increment byte within source 1 + addq a1, 1, a1 // increment source 2 pointer + cmpeq t0, 8, t3 // finished with source 1 quad? + beq t3, 10b // if eq[false], more to compare + + addq a0, 7, a0 // point to next source 1 quad + bic a0, 7, a0 // align to quadword + + +// +// Compare 64-byte blocks +// + +30: + srl a2, 6, t0 // t0 = number of blocks to compare + beq t0, 50f // if eq, no blocks to move + + ldq_u t1, 0(a1) // get source 2 unaligned quad 1 + + .set noat +40: + ldq_u t2, 7(a1) // get source 2 unaligned quad 2 + addq a0, 64, a0 // increment source 1 pointer + ldq_u t3, 15(a1) // get source 2 unaligned quad 3 + extql t1, a1, t1 // bytes from unaligned quad 1 + extqh t2, a1, $at // bytes from unaligned quad 2 + ldq_u t4, 23(a1) // get source 2 unaligned quad 4 + bis t1, $at, t1 // t1 = quadword 1 (source 2) + ldq_u t5, 31(a1) // get source 2 unaligned quad 5 + extql t2, a1, t2 // bytes from unaligned quad 2 + extqh t3, a1, $at // bytes from unaligned quad 3 + ldq a3, -64(a0) // a3 = quadword 1 (source 1) + bis t2, $at, t2 // t2 = quadword 2 (source 2) + ldq a4, -56(a0) // a4 = quadword 2 (source 1) + extql t3, a1, t3 // bytes from unaligned quad 3 + extqh t4, a1, $at // bytes from unaligned quad 4 + ldq a5, -48(a0) // a5 = quadword 3 (source 1) + bis t3, $at, t3 // t3 = quadword 3 (source 2) + extql t4, a1, t4 // bytes from unaligned quad 4 + extqh t5, a1, $at // bytes from unaligned quad 5 + subq t0, 1, t0 // decrement blocks to compare + bis t4, $at, t4 // t4 = quadword 4 (source 2) + + xor t1, a3, $at // match on quadword 1? + ldq a3, -40(a0) // a3 = quadword 4 (source 1) + bne $at, 200f // if ne, miscompare quad 1 + xor t2, a4, $at // match on quadword 2? + ldq_u t2, 39(a1) // get source 2 unaligned quad 6 + bne $at, 122f // if ne, miscompare quad 2 + xor t3, a5, $at // match on quadword 3? + ldq_u t3, 47(a1) // get source 2 unaligned quad 7 + bne $at, 124f // if ne, miscompare quad 3 + xor t4, a3, $at // match on quadword 4? + ldq_u t4, 55(a1) // get source 2 unaligned quad 8 + bne $at, 126f // if ne, miscompare quad 4 + ldq_u t1, 63(a1) // get source 2 unaligned quad 9 + + ldq a3, -32(a0) // a3 = quadword 5 (source 1) + extql t5, a1, t5 // bytes from unaligned quad 5 + extqh t2, a1, $at // bytes from unaligned quad 6 + ldq a4, -24(a0) // a4 = quadword 6 (source 1) + ldq a5, -16(a0) // a5 = quadword 7 (source 1) + bis t5, $at, t5 // t5 = quadword 5 (source 2) + + xor t5, a3, $at // match on quadword 5? + ldq a3, -8(a0) // a3 = quadword 8 (source 1) + bne $at, 130f // if ne, miscompare quad 5 + extql t2, a1, t2 // bytes from unaligned quad 6 + extqh t3, a1, $at // bytes from unaligned quad 7 + extql t3, a1, t3 // bytes from unaligned quad 7 + bis t2, $at, t2 // t2 = quadword 6 (source 2) + xor t2, a4, $at // match on quadword 6? + bne $at, 132f // if ne, miscompare quad 6 + extqh t4, a1, $at // bytes from unaligned quad 8 + extql t4, a1, t4 // bytes from unaligned quad 8 + bis t3, $at, t3 // t3 = quadword 7 (source 2) + xor t3, a5, $at // match on quadword 7? + bne $at, 134f // if ne, miscompare quad 7 + extqh t1, a1, $at // bytes from unaligned quad 9 + addq a1, 64, a1 // increment source 2 pointer + bis t4, $at, t4 // t4 = quadword 8 (source 2) + xor t4, a3, $at // match on quadword 8? + bne $at, 136f // if ne, miscompare quad 8 + subq a2, 64, a2 // decrement number of bytes to compare + bne t0, 40b // if ne, more blocks to compare + + .set at + +// +// Compare quadwords +// + + +50: + srl a2, 3, t0 // t0 = number of quads to compare + beq t0, 70f // if eq, no quads to compare + ldq_u t1, 0(a1) // get unaligned quad 1 (source 2) + + .set noat +60: + ldq_u t2, 7(a1) // get unaligned quad 2 (source 2) + ldq t3, 0(a0) // t3 = quadword 1 (source 1) + extql t1, a1, t1 // get bytes from unaligned quad 1 + extqh t2, a1, $at // get bytes from unaligned quad 2 + addq a1, 8, a1 // increment source 2 pointer + bis t1, $at, t1 // t1 = quadword 1 (source 2) + xor t1, t3, $at // match on quadword? + bne $at, 200f // if ne, miscompare + subq t0, 1, t0 // decrement quadwords to compare + addq a0, 8, a0 // increment source 1 pointer + subq a2, 8, a2 // decrement bytes to compare + bis t2, zero, t1 // save low quadword for next loop + bne t0, 60b // if ne, more quads to compare + + .set at + +// +// Compare bytes for final quadword +// + +70: + beq a2, 90f // if eq, comparison complete + + ldq t1, 0(a0) // get quadword from source 1 + bis zero, zero, t0 // t0 = byte position to compare + + .set noat +80: + ldq_u t2, 0(a1) // get unaligned quad from source 2 + extbl t1, t0, t3 // t3 = byte from source 1 + extbl t2, a1, t2 // t2 = byte from source 2 + xor t3, t2, $at // match on byte? + bne $at, 100f // if ne, miscompare on byte + addq t0, 1, t0 // increment byte position + addq a1, 1, a1 // increment source 2 pointer + subq a2, 1, a2 // decrement bytes to compare + bne a2, 80b // if ne, more bytes to compare + + .set at +// +// Successful full comparison +// + +90: + ret zero, (ra) // return, v0 already set + + +// +// Miscompare on last quadword +// + +100: + subq v0, a2, v0 // subtract bytes not compared + ret zero, (ra) // return + +// +// Miscompare on first quadword, unaligned case +// +// v0 = total bytes to compare +// a2 = bytes remaining to compare +// + +110: + subq v0, a2, v0 // bytes compared successfully + ret zero, (ra) // return + +// +// Miscompare on 64-byte block compare +// + +122: + subq a2, 8, a2 // miscompare on quad 2 + br zero, 200f // finish in common code + +124: + subq a2, 16, a2 // miscompare on quad 3 + br zero, 200f // finish in common code + +126: + subq a2, 24, a2 // miscompare on quad 4 + br zero, 200f // finish in common code + +130: + subq a2, 32, a2 // miscompare on quad 5 + br zero, 200f // finish in common code + +132: + subq a2, 40, a2 // miscompare on quad 6 + br zero, 200f // finish in common code + +134: + subq a2, 48, a2 // miscompare on quad 7 + br zero, 200f // finish in common code + +136: + subq a2, 56, a2 // miscompare on quad 8 + br zero, 200f // finish in common code + +// +// Miscompare, determine number of bytes that successfully compared +// $at = xor of relevant quads from sources, must be non-zero +// a2 = number of bytes left to compare +// + .set noat +200: + cmpbge zero, $at, $at // $at = mask of non-zero bytes + + // + // look for the first bit cleared in $at, this is the + // number of the first byte which differed + // + bis zero, zero, t0 // bit position to look for clear + +210: + blbc $at, 220f // if low clear, found difference + srl $at, 1, $at // check next bit + addq t0, 1, t0 // count bit position checked + br zero, 210b + +220: + subq v0, a2, v0 // subtract bytes yet to compare + addq v0, t0, v0 // add bytes that matched on last quad + + ret zero, (ra) + + .set at + + .end RtlCompareMemory + + + + SBTTL("Move Memory") +//++ +// +// VOID +// RtlMoveMemory ( +// IN PVOID Destination, +// IN PVOID Source, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function moves memory either forward or backward, aligned or +// unaligned, in 64-byte blocks, followed by 8-byte blocks, followed +// by any remaining bytes. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the destination address of +// the move operation. +// +// Source (a1) - Supplies a pointer to the source address of the move +// operation. +// +// Length (a2) - Supplies the length, in bytes, of the memory to be moved. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlMoveMemory) + + beq a2, 80f // if eq, no bytes to move +// +// If the source address is less than the destination address and source +// address plus the length of the move is greater than the destination +// address, then the source and destination overlap such that the move +// must be performed backwards. +// + + cmpult a0, a1, t0 // is destination less than source + bne t0, MoveForward // if eq [true] no overlap possible + addq a1, a2, t0 // compute source ending address + cmpult t0, a0, t1 // is source end less than dest. + beq t1, MoveBackward // if eq [false], overlap + +// +// Move memory forward aligned and unaligned. +// + +MoveForward: // + xor a0, a1, t0 // compare alignment bits + and t0, 0x7, t0 // isloate alignment comparison + bne t0, MoveForwardUnaligned // if ne, incompatible alignment + +// +// Move memory forward aligned. +// + +MoveForwardAligned: // + +// +// Move bytes until source and destination are quadword aligned +// + + and a0, 0x7, t0 // t0 = unaligned bits + bne t0, 5f // if ne, not quad aligned + br zero, 20f // predicted taken + +5: + ldq_u t2, 0(a0) // get unaligned quad from dest. + ldq_u t1, 0(a1) // get unaligned quadword from source +10: + beq a2, 15f // if eq, all bytes moved + extbl t1, t0, t3 // t3 = byte from source + insbl t3, t0, t3 // t3 = byte from source, in position + mskbl t2, t0, t2 // clear position in dest. quad + bis t2, t3, t2 // merge in byte from source + subq a2, 1, a2 // decrement bytes to move + addq t0, 1, t0 // increment byte within quad + cmpeq t0, 8, t3 // finished the quadword? + beq t3, 10b // if eq [false], do next byte +15: + stq_u t2, 0(a0) // store merged destination bytes + + addq a0, 7, a0 // move to next quadword + bic a0, 7, a0 // aligned quadword + + addq a1, 7, a1 // move to next quadword + bic a1, 7, a1 // aligned quadword + +// +// Check for 64-byte block moves +// + +20: + srl a2, 6, t0 // t0 = number of 64 byte blocks + beq t0, 40f // if eq no blocks to move + and a2, 64-1, a2 // a2 = residual bytes + +30: + ldq t1, 0(a1) // load 64 bytes from source + addq a0, 64, a0 // increment destination pointer + ldq v0, 56(a1) // + ldq a3, 32(a1) // + stq t1, -64(a0) // write to destination + ldq t2, 8(a1) // into volatile registers + ldq t3, 16(a1) // + ldq t4, 24(a1) // + subq t0, 1, t0 // decrement number of blocks + stq t2, -56(a0) // + ldq a4, 40(a1) // + stq t3, -48(a0) // + ldq a5, 48(a1) // + stq t4, -40(a0) // + addq a1, 64, a1 // increment source pointer + stq a3, -32(a0) // + stq a4, -24(a0) // + stq a5, -16(a0) // + stq v0, -8(a0) // + bne t0, 30b // if ne, more blocks to copy + +// +// Copy quadwords +// + +40: + srl a2, 3, t0 // t0 = number of quadwords to move + beq t0, 60f // if eq no quadwords to move + and a2, 8-1, a2 // a2 = residual bytes + +50: + ldq t1, 0(a1) // load quadword from source + addq a1, 8, a1 // increment source pointer + stq t1, 0(a0) // store quadword to destination + addq a0, 8, a0 // increment destination pointer + subq t0, 1, t0 // decrement number of quadwords + bne t0, 50b // if ne, more quadwords to move + +// +// Move final residual bytes +// + +60: + beq a2, 80f // if eq, no more bytes to move + ldq t1, 0(a1) // get last source quadword + ldq t2, 0(a0) // get last dest. quadword + bis zero, zero, t0 // t0 = next byte number to move + +70: + extbl t1, t0, t3 // extract byte from source + insbl t3, t0, t3 // t3 = source byte, in position + mskbl t2, t0, t2 // clear byte position for dest. + bis t2, t3, t2 // merge in source byte + addq t0, 1, t0 // increment byte position + subq a2, 1, a2 // decrement bytes to move + bne a2, 70b // if ne => more bytes to move + + stq t2, 0(a0) // store merged data + +// +// Finish aligned MoveForward +// + +80: + ret zero, (ra) // return + + + +// +// Move memory forward unaligned. +// + +MoveForwardUnaligned: // + + +// +// Move bytes until the destination is aligned +// + + and a0, 0x7, t0 // t0 = unaligned bits + beq t0, 100f // if eq, destination quad aligned + + ldq_u t2, 0(a0) // get unaligned quad from dest + +90: + beq a2, 95f // if eq no more bytes to move + ldq_u t1, 0(a1) // get unaligned quad from source + extbl t1, a1, t1 // extract source byte + insbl t1, t0, t1 // t1 = source byte, in position + mskbl t2, t0, t2 // clear byte position in dest. + bis t2, t1, t2 // merge in source byte + addq t0, 1, t0 // increment byte position + addq a1, 1, a1 // increment source pointer + subq a2, 1, a2 // decrement bytes to move + cmpeq t0, 8, t3 // t0 = 8? => quad finished + beq t3, 90b // if eq [false], more bytes to move +95: + stq_u t2, 0(a0) // store merged quadword + addq a0, 7, a0 // increment to next quad + bic a0, 7, a0 // align next quadword + +// +// Check for 64-byte blocks to move +// + +100: + srl a2, 6, t0 // t0 = number of blocks to move + beq t0, 120f // if eq no blocks to move + and a2, 64-1, a2 // a2 = residual bytes to move + + + ldq_u t1, 0(a1) // t1 = first unaligned quad + +110: + // get source data and merge it + // as we go + ldq_u t2, 7(a1) // t2 = second unaligned quad + extql t1, a1, t1 // extract applicable bytes from t1 + extqh t2, a1, v0 // extract applicable bytes from t2 + bis t1, v0, t1 // t1 = quad #1 + ldq_u t3, 15(a1) // t3 = third unaligned quad + extql t2, a1, t2 // extract applicable bytes from t2 + extqh t3, a1, v0 // extract applicable bytes from t3 + stq t1, 0(a0) // store quad #1 + bis t2, v0, t2 // t2 = quad #2 + ldq_u t4, 23(a1) // t4 = fourth unaligned quad + extql t3, a1, t3 // extract applicable bytes from t3 + extqh t4, a1, v0 // extract applicable bytes from t4 + stq t2, 8(a0) // store quad #2 + bis t3, v0, t3 // t3 = quad #3 + ldq_u t5, 31(a1) // t5 = fifth unaligned quad + extql t4, a1, t4 // extract applicable bytes from t4 + extqh t5, a1, v0 // extract applicable bytes from t5 + stq t3, 16(a0) // store quad #3 + bis t4, v0, t4 // t4 = quad #4 + ldq_u a3, 39(a1) // a3 = sixth unaligned quad + extql t5, a1, t5 // extract applicable bytes from t5 + extqh a3, a1, v0 // extract applicable bytes from a3 + stq t4, 24(a0) // store quad #4 + bis t5, v0, t5 // t5 = quad #5 + ldq_u a4, 47(a1) // a4 = seventh unaligned quad + extql a3, a1, a3 // extract applicable bytes from a3 + extqh a4, a1, v0 // extract applicable bytes from a4 + stq t5, 32(a0) // store quad #5 + bis a3, v0, a3 // a3 = quad #6 + ldq_u a5, 55(a1) // a5 = eighth unaligned quad + extql a4, a1, a4 // extract applicable bytes from a4 + extqh a5, a1, v0 // extract applicable bytes from a5 + stq a3, 40(a0) // store quad #6 + bis a4, v0, a4 // a4 = quad #7 + ldq_u t1, 63(a1) // t1 = ninth unaligned = 1st of next + extql a5, a1, a5 // extract applicable bytes from a5 + extqh t1, a1, v0 // extract applicable bytes from t1 + stq a4, 48(a0) // store quad #7 + bis a5, v0, a5 // a5 = quad #8 + addq a1, 64, a1 // increment source pointer + stq a5, 56(a0) // store quad #8 + addq a0, 64, a0 // increment destination pointer + subq t0, 1, t0 // decrement number of blocks + bne t0, 110b // if ne, more blocks to move + +// +// Move unaligned source quads to aligned destination quads +// + +120: + srl a2, 3, t0 // t0 = number of quads to move + beq t0, 140f // if eq no quads to move + and a2, 8-1, a2 // a2 = residual bytes + + + ldq_u t1, 0(a1) // t1 = first unaligned quad +130: + ldq_u t2, 7(a1) // t2 = second unaligned quad + addq a0, 8, a0 // increment destination pointer + extql t1, a1, t1 // extract applicable bytes from t1 + extqh t2, a1, v0 // extract applicable bytes from t2 + bis t1, v0, t1 // t1 = quadword of data + stq t1, -8(a0) // store data to destination + addq a1, 8, a1 // increment source pointer + subq t0, 1, t0 // decrement quads to move + bis t2, zero, t1 // t1 = first of next unaligned pair + bne t0, 130b // if ne, more quads to move + +// +// Move remaining bytes to final quadword +// + + +140: + beq a2, 160f // if eq no more bytes to move + ldq t2, 0(a0) // t2 = destination quadword + bis zero, zero, t3 // t3 = position for next insertion + +150: + ldq_u t1, 0(a1) // get unaligned source quad + extbl t1, a1, t1 // t1 = source byte + insbl t1, t3, t1 // t1 = source byte, in position + mskbl t2, t3, t2 // clear byte in destination + bis t2, t1, t2 // merge in source byte + addq a1, 1, a1 // increment source pointer + subq a2, 1, a2 // decrement bytes to move + addq t3, 1, t3 // increment destination position + bne a2, 150b // more bytes to move + + stq t2, 0(a0) // store merged data + +// +// Finish unaligned MoveForward +// + +160: + ret zero, (ra) // return + + +// +// Move memory backward. +// + +MoveBackward: // + + addq a0, a2, a0 // compute ending destination address + addq a1, a2, a1 // compute ending source address + subq a0, 1, a0 // point to last destination byte + subq a1, 1, a1 // point to last source byte + xor a0, a1, t0 // compare alignment bits + and t0, 0x7, t0 // isolate alignment comparison + bne t0, MoveBackwardUnaligned // if ne, incompatible alignment + +// +// Move memory backward aligned. +// + +MoveBackwardAligned: // + +// +// Move bytes until source and destination are quadword aligned +// + + and a0, 0x7, t0 // t0 = unaligned bits + cmpeq t0, 7, t1 // last byte position 7? + beq t1, 5f // if eq [false], not quad aligned + subq a0, 7, a0 // point to beginning of last quad + subq a1, 7, a1 // point to beginning of last quad + br zero, 30f // predicted taken + +5: + ldq_u t1, 0(a0) // get unaligned quad from dest. + ldq_u t2, 0(a1) // get unaligned quad from source + +10: + beq a2, 20f // if eq, all bytes moved + extbl t2, t0, t3 // t3 = byte from source + insbl t3, t0, t3 // t3 = byte from source, in position + mskbl t1, t0, t1 // clear position in destination + bis t1, t3, t1 // merge in byte from source + subq a2, 1, a2 // decrement bytes to move + subq t0, 1, t0 // decrement byte within quadword + cmplt t0, zero, t3 // finished the quadword? + beq t3, 10b // if eq [false], do next byte + +20: + stq_u t1, 0(a0) // store merged destination bytes + + subq a0, 8, a0 // move to previous quadword + bic a0, 7, a0 // aligned quadword + + subq a1, 8, a1 // move to previous quadword + bic a1, 7, a1 // aligned quadword + +// +// Check for 64-byte block moves +// + +30: + + srl a2, 6, t0 // t0 = number of 64 byte blocks + beq t0, 50f // if eq, no blocks to move + and a2, 64-1, a2 // a2 = residual bytes + +40: + ldq t1, 0(a1) // load 64 bytes from source into + subq a0, 64, a0 // decrement destination pointer + ldq v0, -56(a1) // + ldq a3, -32(a1) // + stq t1, 64(a0) // write to destination + ldq t2, -8(a1) // into volatile registers + ldq a5, -48(a1) // + ldq a4, -40(a1) // + stq t2, 56(a0) // + ldq t3, -16(a1) // + ldq t4, -24(a1) // + subq a1, 64, a1 // decrement source pointer + stq t3, 48(a0) // + stq t4, 40(a0) // + stq a3, 32(a0) // + subq t0, 1, t0 // decrement number of blocks + stq a4, 24(a0) // + stq a5, 16(a0) // + stq v0, 8(a0) // + bne t0, 40b // if ne, more blocks to copy + +// +// Copy quadwords +// + +50: + srl a2, 3, t0 // t0 = number of quadwords to move + beq t0, 70f // if eq no quadwords to move + and a2, 8-1, a2 // a2 = residual bytes + +60: + ldq t1, 0(a1) // load quadword from source + subq a1, 8, a1 // decrement source pointer + stq t1, 0(a0) // store quadword to destination + subq a0, 8, a0 // decrement destination pointer + subq t0, 1, t0 // decrement quadwords to move + bne t0, 60b // if ne, more quadwords to move + +// +// Move final residual bytes +// + +70: + beq a2, 90f // if eq, no more bytes to move + ldq t1, 0(a1) // get last source quadword + ldq t2, 0(a0) // get last destination quadword + bis zero, 7, t0 // t0 = next byte number to move + +80: + extbl t1, t0, t3 // extract byte from source + insbl t3, t0, t3 // t3 = source byte, in position + mskbl t2, t0, t2 // clear byte position for dest. + bis t2, t3, t2 // merge in source byte + subq t0, 1, t0 // decrement byte position + subq a2, 1, a2 // decrement bytes to move + bne a2, 80b // if ne, more bytes to move + + stq t2, 0(a0) // write destination data +// +// Finish aligned MoveBackward +// + +90: + + ret zero, (ra) // return + + +// +// Move memory backward unaligned. +// + +MoveBackwardUnaligned: // + + +// +// Move bytes until the destination is aligned +// + + and a0, 0x7, t0 // t0 = unaligned bits + cmpeq t0, 7, t1 // last byte of a quadword + beq t1, 95f // if eq[false], not aligned + subq a0, 7, a0 // align pointer to beginning of quad + br zero, 120f // + +95: + ldq_u t2, 0(a0) // get unaligned quad from dest. + +100: + beq a2, 110f // if eq, no more bytes to move + ldq_u t1, 0(a1) // get unaligned quad from source + extbl t1, a1, t1 // extract source byte + insbl t1, t0, t1 // t1 = source byte in position + mskbl t2, t0, t2 // clear byte position in dest. + bis t2, t1, t2 // merge source byte + subq t0, 1, t0 // decrement byte position + subq a1, 1, a1 // decrement source pointer + subq a2, 1, a2 // decrement number of bytes to move + cmplt t0, zero, t3 // t0 < 0? => quad finished + beq t3, 100b // if eq [false], more bytes to move + +110: + stq_u t2, 0(a0) // store merged quadword + + subq a0, 8, a0 // decrement dest. to previous quad + bic a0, 7, a0 // align previous quadword + +// +// Check for 64-byte blocks to move +// + +120: + + srl a2, 6, t0 // t0 = number of blocks to move + subq a1, 7, a1 // point to beginning of last quad + beq t0, 140f // if eq no blocks to move + and a2, 64-1, a2 // a2 = residual bytes to move + + ldq_u t1, 7(a1) // t1 = first unaligned quad + +130: + // get source data and merge it + // as we go + ldq_u t2, 0(a1) // t2 = second unaligned quad + extqh t1, a1, t1 // extract applicable bytes from t1 + extql t2, a1, v0 // extract applicable bytes from t2 + bis t1, v0, t1 // t1 = quad #1 + ldq_u t3, -8(a1) // t3 = third unaligned quad + extqh t2, a1, t2 // extract applicable bytes from t2 + extql t3, a1, v0 // extract applicable bytes from t3 + stq t1, 0(a0) // store quad #1 + bis t2, v0, t2 // t2 = quad #2 + ldq_u t4, -16(a1) // t4 = fourth unaligned quad + extqh t3, a1, t3 // extract applicable bytes from t3 + extql t4, a1, v0 // extract applicable bytes from t4 + stq t2, -8(a0) // store quad #2 + bis t3, v0, t3 // t3 = quad #3 + ldq_u t5, -24(a1) // t5 = fifth unaligned quad + extqh t4, a1, t4 // extract applicable bytes from t4 + extql t5, a1, v0 // extract applicable bytes from t5 + stq t3, -16(a0) // store quad #3 + bis t4, v0, t4 // t4 = quad #4 + ldq_u a3, -32(a1) // a3 = sixth unaligned quad + extqh t5, a1, t5 // extract applicable bytes from t5 + extql a3, a1, v0 // extract applicable bytes from a3 + stq t4, -24(a0) // store quad #4 + bis t5, v0, t5 // t5 = quad #5 + ldq_u a4, -40(a1) // a4 = seventh unaligned quad + extqh a3, a1, a3 // extract applicable bytes from a3 + extql a4, a1, v0 // extract applicable bytes from a4 + stq t5, -32(a0) // store quad #5 + bis a3, v0, a3 // a3 = quad #6 + ldq_u a5, -48(a1) // a5 = eighth unaligned quad + extqh a4, a1, a4 // extract applicable bytes from a4 + extql a5, a1, v0 // extract applicable bytes from a5 + stq a3, -40(a0) // store quad #6 + bis a4, v0, a4 // a4 = quad #7 + ldq_u t1, -56(a1) // t1 = ninth unaligned = 1st of next + extqh a5, a1, a5 // extract applicable bytes from a5 + extql t1, a1, v0 // extract applicable bytes from t1 + stq a4, -48(a0) // store quad #7 + bis a5, v0, a5 // a5 = quad #8 + subq a1, 64, a1 // increment source pointer + stq a5, -56(a0) // store quad #8 + subq a0, 64, a0 // increment destination pointer + subq t0, 1, t0 // decrement number of blocks + bne t0, 130b // if ne, more blocks to move + + +// +// Move unaligned source quads to aligned destination quads +// + +140: + srl a2, 3, t0 // t0 = number of quads to move + beq t0, 160f // if eq no quads to move + and a2, 8-1, a2 // a2 = residual bytes + + ldq_u t1, 7(a1) // t1 = first unaligned quad + +150: + ldq_u t2, 0(a1) // t2 = second unaligned quad + subq a0, 8, a0 // decrement destination pointer + extqh t1, a1, t1 // extract applicable bytes from t1 + extql t2, a1, v0 // extract applicable bytes from t2 + bis t1, v0, t1 // t1 = quadword of data + stq t1, 8(a0) // store data to destination + subq a1, 8, a1 // decrement source pointer + subq t0, 1, t0 // decrement quads to move + bis t2, zero, t1 // t1 = first of next unaligned pair + bne t0, 150b // if ne, more quads to move + +// +// Move remaining bytes to final quadword +// + +160: + beq a2, 180f // if eq, no more bytes to move + ldq t2, 0(a0) // t2 = destination quadword + bis zero, 7, t0 // t0 = position for next insertion + +170: + subq a1, 1, a1 // decrement source pointer + ldq_u t1, 8(a1) // get unaligned source quad + extbl t1, a1, t1 // t1 = source byte + insbl t1, t0, t1 // t1 = source byte, in position + mskbl t2, t0, t2 // clear byte position + bis t2, t1, t2 // merge in source byte + subq t0, 1, t0 // decrement byte position for dest. + subq a2, 1, a2 // decrement bytes to move + bne a2, 170b // if ne, more bytes to move + + stq t2, 0(a0) // + +// +// Finish unaligned MoveBackward +// + +180: + ret zero, (ra) // return + + .end RtlMoveMemory + + SBTTL("Zero Memory") +//++ +// +// VOID +// RtlZeroMemory ( +// IN PVOID Destination, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function zeros memory by first aligning the destination address to +// a quadword boundary, and then zeroing 64-byte blocks, followed by 8-byte +// blocks, followed by any remaining bytes. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the memory to zero. +// +// Length (a1) - Supplies the length, in bytes, of the memory to be zeroed. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlZeroMemory) + + bis zero, zero, a2 // set fill pattern + br zero, RtlpFillMemory // + + + SBTTL("Fill Memory") +//++ +// +// VOID +// RtlFillMemory ( +// IN PVOID Destination, +// IN ULONG Length, +// IN UCHAR Fill +// ) +// +// Routine Description: +// +// This function fills memory by first aligning the destination address to +// a longword boundary, and then filling 32-byte blocks, followed by 4-byte +// blocks, followed by any remaining bytes. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the memory to fill. +// +// Length (a1) - Supplies the length, in bytes, of the memory to be filled. +// +// Fill (a2) - Supplies the fill byte. +// +// N.B. The alternate entry memset expects the length and fill arguments +// to be reversed. It also returns the Destination pointer +// +// Return Value: +// +// None. +// +//-- + + ALTERNATE_ENTRY(memset) + + bis a0, zero, v0 // set return value + bis a1, zero, a3 // swap length and fill arguments + bis a2, zero, a1 // + bis a3, zero, a2 // + + ALTERNATE_ENTRY(RtlFillMemory) + + and a2, 0xff, a2 // clear excess bits + sll a2, 8, t0 // duplicate fill byte + bis a2, t0, a2 // generate fill word + sll a2, 16, t0 // duplicate fill word + bis a2, t0, a2 // generate fill longword + sll a2, 32, t0 // duplicate fill longword + bis a2, t0, a2 // generate fill quadword + +.align 3 // ensure quadword aligned target +// +// Fill memory with the pattern specified in register a2. +// + +RtlpFillMemory: // + +// +// Align destination to quadword +// + + beq a1, 80f // anything to fill? (paranoia) + and a0, 8-1, t0 // t0 = unaligned bits + bne t0, 5f // if ne, then not quad aligned + br zero, 20f // if eq, then quad aligned + +5: + ldq_u t1, 0(a0) // get unaligned quadword + // for first group of bytes +10: + beq a1, 15f // if eq no more bytes to fill + insbl a2, t0, t2 // get fill byte into position + mskbl t1, t0, t1 // clear byte for fill + bis t1, t2, t1 // put in fill byte + addq t0, 1, t0 // increment to next byte position + subq a1, 1, a1 // decrement bytes to fill + cmpeq t0, 8, t2 // t0 = 8? + beq t2, 10b // if eq [false] more bytes to do + +15: + stq_u t1, 0(a0) // store modified bytes + addq a0, 7, a0 // move a0 to next quadword + bic a0, 7, a0 // align a0 to quadword + +// +// Check for 64-byte blocks +// + +20: + srl a1, 6, t0 // t0 = number of 64 byte blocks + beq t0, 40f // if eq then no 64 byte blocks + and a1, 64-1, a1 // a1 = residual bytes to fill + +30: + stq a2, 0(a0) // store 64 bytes + stq a2, 8(a0) // + stq a2, 16(a0) // + stq a2, 24(a0) // + stq a2, 32(a0) // + stq a2, 40(a0) // + stq a2, 48(a0) // + stq a2, 56(a0) // + + subq t0, 1, t0 // decrement blocks remaining + addq a0, 64, a0 // increment destination pointer + bne t0, 30b // more blocks to write + + + +// +// Fill aligned quadwords +// + +40: + srl a1, 3, t0 // t0 = number of quadwords + bne t0, 55f // if ne quadwords left to fill + br zero, 60f // if eq no quadwords left + +55: + and a1, 8-1, a1 // a1 = residual bytes to fill + +50: + stq a2, 0(a0) // store quadword + subq t0, 1, t0 // decrement quadwords remaining + addq a0, 8, a0 // next quadword + bne t0, 50b // more quadwords to write + + +// +// Fill bytes for last quadword +// + +60: + bne a1, 65f // if ne bytes remain to be filled + br zero, 80f // if eq no more bytes to fill + +65: + ldq t1, 0(a0) // get last quadword + bis zero, zero, t0 // t0 = byte position to start fill + +70: + beq a1, 75f // if eq, no more bytes to fill + insbl a2, t0, t2 // get fill byte into position + mskbl t1, t0, t1 // clear fill byte position + bis t1, t2, t1 // insert fill byte + addq t0, 1, t0 // increment byte within quad + subq a1, 1, a1 // decrement bytes to fill + cmpeq t0, 8, t3 // t0 = 8? => finished quad + beq t3, 70b // if eq [false] more bytes to fill + +75: + stq t1, 0(a0) // write merged quadword + +// +// Finish up +// + +80: + ret zero, (ra) // return + + + .end RtlZeroMemory + + SBTTL("Fill Memory Ulong") +//++ +// +// VOID +// RtlFillMemoryUlong ( +// IN PVOID Destination, +// IN ULONG Length, +// IN ULONG Pattern +// ) +// +// Routine Description: +// +// This function fills memory with the specified longowrd pattern by +// filling 64-byte blocks followed by 8-byte blocks and finally +// 4-byte blocks. +// +// N.B. This routine assumes that the destination address is aligned +// on a longword boundary and that the length is an even multiple +// of longwords. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the memory to fill. +// +// Length (a1) - Supplies the length, in bytes, of the memory to be filled. +// +// Pattern (a2) - Supplies the fill pattern. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlFillMemoryUlong) + + bic a1, 3, a1 // make sure length is an even number + // of longwords + sll a2, 32, a3 // a3 = long pattern in upper 32 bits + srl a3, 32, t0 // clear upper bits, pattern in lower 32 + bis a3, t0, a3 // a3 = quad version of fill pattern + +// +// Make destination address quad-aligned +// + + and a0, 4, t0 // is a0 quad aligned? + beq t0, 10f // if eq, then a0 quad aligned + stl a2, 0(a0) // fill first longword + addq a0, 4, a0 // quad align a0 + subq a1, 4, a1 // bytes remaining to store + +// +// Check for 64-byte blocks to fill +// + +10: + srl a1, 6, t0 // t0 = # 64-byte blocks to fill + beq t0, 30f // if eq no 64 byte blocks + and a1, 64-1, a1 // a1 = residual bytes + +20: + stq a3, 0(a0) // store 64 bytes + stq a3, 8(a0) // + stq a3, 16(a0) // + stq a3, 24(a0) // + stq a3, 32(a0) // + stq a3, 40(a0) // + stq a3, 48(a0) // + stq a3, 56(a0) // + subq t0, 1, t0 // t0 = blocks remaining + addq a0, 64, a0 // increment address pointer + bne t0, 20b // if ne more blocks to fill + +// +// Fill 8 bytes at a time while we can, a1 = bytes remaining +// + +30: + srl a1, 3, t0 // t0 = # quadwords to fill + beq t0, 50f // if eq no quadwords left + and a1, 8-1, a1 // a1 = residual bytes +40: + stq a3, 0(a0) // store quadword + subq t0, 1, t0 // t0 = quadwords remaining + addq a0, 8, a0 // increment address pointer + bne t0, 40b // if ne more quadwords to fill + +// +// Fill last 4 bytes +// + +50: + beq a1, 60f // if eq no longwords remain + stl a2, 0(a0) // fill last longword + +// +// Finish up +// + +60: + ret zero, (ra) // return to caller + + + .end RtlFillMemoryUlong + + SBTTL("Copy Memory With Byte Granularity") +//++ +// +// VOID +// RtlCopyBytes ( +// IN PVOID Destination, +// IN PVOID Source, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function copies non-overlapping memory, aligned or unaligned, in +// 64-byte blocks, followed by 8-byte blocks, followed by any remaining +// bytes. Unlike RtlCopyMemory or RtlMoveMemory the copy is done such +// that byte granularity is assured for all platforms. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the destination address of +// the move operation. +// +// Source (a1) - Supplies a pointer to the source address of the move +// operation. +// +// Length (a2) - Supplies the length, in bytes, of the memory to be moved. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlCopyBytes) + +// +// Move memory forward aligned and unaligned. +// + + xor a0, a1, t0 // compare alignment bits + and t0, 0x7, t0 // isolate alignment comparison + bne t0, CopyForwardUnaligned // if ne, incompatible alignment + +// +// Source and Destination buffers have the same alignment. Move +// bytes until done or source and destination are quadword aligned +// + + and a0, 0x7, t0 // t0 = unaligned bits + bne t0, 5f // if ne, not quad aligned + br zero, 20f // predicted taken +5: + bis zero, zero, t1 // t4 = destination byte zap mask + bis zero, 1, t2 + sll t2, t0, t2 // t2 = next bit to set in zap mask +10: + beq a2, 15f // if eq, all bits set + bis t1, t2, t1 // set bit in zap mask + sll t2, 1, t2 // set next higher bit for zap mask + subq a2, 1, a2 // decrement bytes to move + addq t0, 1, t0 // increment byte within quad + cmpeq t0, 8, t3 // finished the quadword? + beq t3, 10b // if eq [false], do next byte +15: + ldq_u t2, 0(a1) // get unaligned quadword from source + zapnot t2, t1, t2 // clear source bytes + bic a0, 7, a3 // a3 = quadword base of destination +retry1: + ldq_l t0, 0(a3) // load destination quadword + zap t0, t1, t0 // clear destination bytes + or t0, t2, t0 // merge in bytes from source + stq_c t0, 0(a3) // store merged quadword conditional + beq t0, retry1f // if eq, retry failed interlock + + addq a0, 7, a0 // move to next quadword + bic a0, 7, a0 // aligned quadword + + addq a1, 7, a1 // move to next quadword + bic a1, 7, a1 // aligned quadword + +// +// Check for 64-byte block moves +// + +20: + srl a2, 6, t0 // t0 = number of 64 byte blocks + beq t0, 40f // if eq no blocks to move + and a2, 64-1, a2 // a2 = residual bytes + +30: + ldq t1, 0(a1) // load 64 bytes from source + addq a0, 64, a0 // increment destination pointer + ldq v0, 56(a1) // + ldq a3, 32(a1) // + stq t1, -64(a0) // write to destination + ldq t2, 8(a1) // into volatile registers + ldq t3, 16(a1) // + ldq t4, 24(a1) // + subq t0, 1, t0 // decrement number of blocks + stq t2, -56(a0) // + ldq a4, 40(a1) // + stq t3, -48(a0) // + ldq a5, 48(a1) // + stq t4, -40(a0) // + addq a1, 64, a1 // increment source pointer + stq a3, -32(a0) // + stq a4, -24(a0) // + stq a5, -16(a0) // + stq v0, -8(a0) // + bne t0, 30b // if ne, more blocks to copy + +// +// Copy quadwords +// + +40: + srl a2, 3, t0 // t0 = number of quadwords to move + beq t0, 60f // if eq no quadwords to move + and a2, 8-1, a2 // a2 = residual bytes + +50: + ldq t1, 0(a1) // load quadword from source + addq a1, 8, a1 // increment source pointer + stq t1, 0(a0) // store quadword to destination + addq a0, 8, a0 // increment destination pointer + subq t0, 1, t0 // decrement number of quadwords + bne t0, 50b // if ne, more quadwords to move + +// +// Move final residual bytes +// + +60: + beq a2, 80f // if eq, no more bytes to move + mov a2, t0 // t0 = number of bytes to move + mov -1, t1 // t1 = bit mask + sll t0, 3, t0 // # of bytes to # of bits + srl t1, t0, t1 // clear t0 bits + sll t1, t0, t0 // move it back + ldq t1, 0(a1) // get last source quadword + bic t1, t0, t1 // clear bytes not copied + not t0, t0 // complement to clear destination +retry2: + ldq_l t2, 0(a0) // get last destination quadword locked + bic t2, t0, t2 // clear bytes to be copied + bis t2, t1, t2 // move bytes from source + stq_c t2, 0(a0) // store merged quadword conditional + beq t2, retry2f // if eq, retry failed interlock + +// +// Finish aligned MoveForward +// + +80: + ret zero, (ra) // return + +// +// Move memory forward unaligned. +// + +CopyForwardUnaligned: // + +// +// Move bytes until the destination is aligned +// + + and a0, 0x7, t0 // t0 = unaligned bits + beq t0, 100f // if eq, destination quad aligned + bis zero, zero, t1 // t4 = destination byte zap mask + bis zero, 1, t2 + sll t2, t0, t2 // t2 = next bit to set in zap mask + mov zero, t4 // assemble destination bytes here +90: + beq a2, 95f // if eq no more bytes to move + bis t1, t2, t1 // set bit in zap mask + sll t2, 1, t2 // set next higher bit for zap mask + ldq_u t5, 0(a1) // get unaligned quad from source + extbl t5, a1, t5 // extract source byte + insbl t5, t0, t5 // t5 = source byte, in position + or t4, t5, t4 // merge in source byte + addq t0, 1, t0 // increment byte position + addq a1, 1, a1 // increment source pointer + subq a2, 1, a2 // decrement bytes to move + cmpeq t0, 8, t3 // t0 = 8? => quad finished + beq t3, 90b // if eq [false], more bytes to move +95: + bic a0, 0x7, a3 // a3 = quadword base of destination +retry3: + ldq_l t0, 0(a3) // load destination quadword + zap t0, t1, t0 // clear destination bytes + or t0, t4, t0 // merge in bytes from source + stq_c t0, 0(a3) // store merged quadword conditional + beq t0, retry3f // if eq, retry failed interlock + + addq a0, 7, a0 // increment to next quad + bic a0, 7, a0 // align next quadword + +// +// Check for 64-byte blocks to move +// + +100: + srl a2, 6, t0 // t0 = number of blocks to move + beq t0, 120f // if eq no blocks to move + and a2, 64-1, a2 // a2 = residual bytes to move + + ldq_u t1, 0(a1) // t1 = first unaligned quad +110: + // get source data and merge it + // as we go + ldq_u t2, 7(a1) // t2 = second unaligned quad + extql t1, a1, t1 // extract applicable bytes from t1 + extqh t2, a1, v0 // extract applicable bytes from t2 + bis t1, v0, t1 // t1 = quad #1 + ldq_u t3, 15(a1) // t3 = third unaligned quad + extql t2, a1, t2 // extract applicable bytes from t2 + extqh t3, a1, v0 // extract applicable bytes from t3 + stq t1, 0(a0) // store quad #1 + bis t2, v0, t2 // t2 = quad #2 + ldq_u t4, 23(a1) // t4 = fourth unaligned quad + extql t3, a1, t3 // extract applicable bytes from t3 + extqh t4, a1, v0 // extract applicable bytes from t4 + stq t2, 8(a0) // store quad #2 + bis t3, v0, t3 // t3 = quad #3 + ldq_u t5, 31(a1) // t5 = fifth unaligned quad + extql t4, a1, t4 // extract applicable bytes from t4 + extqh t5, a1, v0 // extract applicable bytes from t5 + stq t3, 16(a0) // store quad #3 + bis t4, v0, t4 // t4 = quad #4 + ldq_u a3, 39(a1) // a3 = sixth unaligned quad + extql t5, a1, t5 // extract applicable bytes from t5 + extqh a3, a1, v0 // extract applicable bytes from a3 + stq t4, 24(a0) // store quad #4 + bis t5, v0, t5 // t5 = quad #5 + ldq_u a4, 47(a1) // a4 = seventh unaligned quad + extql a3, a1, a3 // extract applicable bytes from a3 + extqh a4, a1, v0 // extract applicable bytes from a4 + stq t5, 32(a0) // store quad #5 + bis a3, v0, a3 // a3 = quad #6 + ldq_u a5, 55(a1) // a5 = eighth unaligned quad + extql a4, a1, a4 // extract applicable bytes from a4 + extqh a5, a1, v0 // extract applicable bytes from a5 + stq a3, 40(a0) // store quad #6 + bis a4, v0, a4 // a4 = quad #7 + ldq_u t1, 63(a1) // t1 = ninth unaligned = 1st of next + extql a5, a1, a5 // extract applicable bytes from a5 + extqh t1, a1, v0 // extract applicable bytes from t1 + stq a4, 48(a0) // store quad #7 + bis a5, v0, a5 // a5 = quad #8 + addq a1, 64, a1 // increment source pointer + stq a5, 56(a0) // store quad #8 + addq a0, 64, a0 // increment destination pointer + subq t0, 1, t0 // decrement number of blocks + bne t0, 110b // if ne, more blocks to move + +// +// Move unaligned source quads to aligned destination quads +// + +120: + srl a2, 3, t0 // t0 = number of quads to move + beq t0, 140f // if eq no quads to move + and a2, 8-1, a2 // a2 = residual bytes + + + ldq_u t1, 0(a1) // t1 = first unaligned quad +130: + ldq_u t2, 7(a1) // t2 = second unaligned quad + addq a0, 8, a0 // increment destination pointer + extql t1, a1, t1 // extract applicable bytes from t1 + extqh t2, a1, v0 // extract applicable bytes from t2 + bis t1, v0, t1 // t1 = quadword of data + stq t1, -8(a0) // store data to destination + addq a1, 8, a1 // increment source pointer + subq t0, 1, t0 // decrement quads to move + bis t2, zero, t1 // t1 = first of next unaligned pair + bne t0, 130b // if ne, more quads to move + +// +// Move remaining bytes to final quadword +// + +140: + beq a2, 160f // if eq no more bytes to move + + mov zero, t3 // t3 = position for next insertion + mov zero, t4 // assemble destination bytes here + mov a2, t0 // t0 = number of bytes to move + mov -1, t1 // t1 = bit mask + sll t0, 3, t0 // # of bytes to # of bits + srl t1, t0, t1 // clear t0 bits + sll t1, t0, t0 // move it back + not t0, t0 // complement for destination clear mask +150: + ldq_u t1, 0(a1) // get unaligned source quad + extbl t1, a1, t1 // t1 = source byte + insbl t1, t3, t1 // t1 = source byte, in position + bis t4, t1, t4 // merge in source byte + addq a1, 1, a1 // increment source pointer + subq a2, 1, a2 // decrement bytes to move + addq t3, 1, t3 // increment destination position + bne a2, 150b // more bytes to move +retry4: + ldq_l t2, 0(a0) // get last destination quadword locked + bic t2, t0, t2 // clear bytes to be copied + bis t2, t4, t2 // move bytes from source + stq_c t2, 0(a0) // store merged quadword conditional + beq t2, retry4f // if eq, retry failed interlock + +// +// Finish unaligned MoveForward +// + +160: + ret zero, (ra) // return + +// +// Out of line branches for failed store conditional. +// Don't need to restore anything, just try again. +// + +retry1f: + br retry1 +retry2f: + br retry2 +retry3f: + br retry3 +retry4f: + br retry4 + + .end RtlCopyBytes + + SBTTL("Zero Bytes") +//++ +// +// VOID +// RtlZeroBytes ( +// IN PVOID Destination, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function zeros memory by first aligning the destination address to +// a quadword boundary, and then zeroing 64-byte blocks, followed by 8-byte +// blocks, followed by any remaining bytes. Unlike RtlZeroMemory the copy is +// done such that byte granularity is assured for all platforms. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the memory to zero. +// +// Length (a1) - Supplies the length, in bytes, of the memory to be zeroed. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlZeroBytes) + + bis zero, zero, a2 // set fill pattern + br zero, RtlpFillBytes // + + + SBTTL("Fill Bytes") +//++ +// +// VOID +// RtlFillBytes ( +// IN PVOID Destination, +// IN ULONG Length, +// IN UCHAR Fill +// ) +// +// Routine Description: +// +// This function fills memory by first aligning the destination address to +// a longword boundary, and then filling 32-byte blocks, followed by 4-byte +// blocks, followed by any remaining bytes. Unlike RtlFillMemory the copy is +// done such that byte granularity is assured for all platforms. +// +// Arguments: +// +// Destination (a0) - Supplies a pointer to the memory to fill. +// +// Length (a1) - Supplies the length, in bytes, of the memory to be filled. +// +// Fill (a2) - Supplies the fill byte. +// +// N.B. The alternate entry memset expects the length and fill arguments +// to be reversed. It also returns the Destination pointer +// +// Return Value: +// +// None. +// +//-- + + ALTERNATE_ENTRY(RtlFillBytes) + + and a2, 0xff, a2 // clear excess bits + sll a2, 8, t0 // duplicate fill byte + bis a2, t0, a2 // generate fill word + sll a2, 16, t0 // duplicate fill word + bis a2, t0, a2 // generate fill longword + sll a2, 32, t0 // duplicate fill longword + bis a2, t0, a2 // generate fill quadword + +.align 3 // ensure quadword aligned target +// +// Fill memory with the pattern specified in register a2. +// + +RtlpFillBytes: // + +// +// Align destination to quadword +// + + beq a1, 80f // anything to fill? (paranoia) + and a0, 8-1, t0 // t0 = unaligned bits + bne t0, 5f // if ne, then not quad aligned + br zero, 20f // if eq, then quad aligned + +5: + bis zero, zero, t1 // t4 = destination byte zap mask + bis zero, 1, t2 + sll t2, t0, t2 // t2 = next bit to set in zap mask +10: + beq a1, 15f // if eq, all bits set + bis t1, t2, t1 // set bit in zap mask + sll t2, 1, t2 // set next higher bit for zap mask + subq a1, 1, a1 // decrement bytes to fill + addq t0, 1, t0 // increment byte within quad + cmpeq t0, 8, t3 // finished the quadword? + beq t3, 10b // if eq [false], do next byte +15: + zapnot a2, t1, t2 // clear fill bytes + bic a0, 7, a3 // a3 = quadword base of destination +retry5: + ldq_l t0, 0(a3) // load destination quadword + zap t0, t1, t0 // clear destination bytes + or t0, t2, t0 // merge in fill bytes + stq_c t0, 0(a3) // store merged quadword conditional + beq t0, retry5f // if eq, retry failed interlock + + addq a0, 7, a0 // move a0 to next quadword + bic a0, 7, a0 // align a0 to quadword + +// +// Check for 64-byte blocks +// + +20: + srl a1, 6, t0 // t0 = number of 64 byte blocks + beq t0, 40f // if eq then no 64 byte blocks + and a1, 64-1, a1 // a1 = residual bytes to fill + +30: + stq a2, 0(a0) // store 64 bytes + stq a2, 8(a0) // + stq a2, 16(a0) // + stq a2, 24(a0) // + stq a2, 32(a0) // + stq a2, 40(a0) // + stq a2, 48(a0) // + stq a2, 56(a0) // + + subq t0, 1, t0 // decrement blocks remaining + addq a0, 64, a0 // increment destination pointer + bne t0, 30b // more blocks to write + + + +// +// Fill aligned quadwords +// + +40: + srl a1, 3, t0 // t0 = number of quadwords + bne t0, 55f // if ne quadwords left to fill + br zero, 60f // if eq no quadwords left + +55: + and a1, 8-1, a1 // a1 = residual bytes to fill + +50: + stq a2, 0(a0) // store quadword + subq t0, 1, t0 // decrement quadwords remaining + addq a0, 8, a0 // next quadword + bne t0, 50b // more quadwords to write + +// +// Fill bytes for last quadword +// + +60: + beq a1, 80f // if eq no more bytes to fill + + mov a1, t0 // t0 = number of bytes to move + mov -1, t1 // t1 = bit mask + sll t0, 3, t0 // # of bytes to # of bits + srl t1, t0, t1 // clear t0 bits + sll t1, t0, t0 // move it back + bic a2, t0, t1 // clear fill bytes not copied + not t0, t0 // complement to clear destination +retry6: + ldq_l t2, 0(a0) // get last destination quadword locked + bic t2, t0, t2 // clear bytes to be copied + bis t2, t1, t2 // move bytes from source + stq_c t2, 0(a0) // store merged quadword conditional + beq t2, retry6f // if eq, retry failed interlock + +// +// Finish up +// + +80: + ret zero, (ra) // return + +// +// Out of line branches for failed store conditional. +// Don't need to restore anything, just try again. +// + +retry5f: + br retry5 +retry6f: + br retry6 + + .end RtlZeroBytes diff --git a/private/ntos/rtl/alpha/ntcurteb.s b/private/ntos/rtl/alpha/ntcurteb.s new file mode 100644 index 000000000..22c9f1f88 --- /dev/null +++ b/private/ntos/rtl/alpha/ntcurteb.s @@ -0,0 +1,57 @@ +// TITLE("Get Current TEB Pointer") +//++ +// +// Copyright (c) 1992 Digital Equipment Corporation +// +// Module Name: +// +// ntcurteb.s +// +// Abstract: +// +// This module implements the function to retrieve the current TEB pointer. +// +// Author: +// +// Joe Notarangelo 29-Jul-1992 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +//-- + +#include "ksalpha.h" + +//++ +// +// PTEB +// NtCurrentTeb( +// VOID +// ) +// +// Routine Description: +// +// This function returns the current TEB pointer retrieved via an unprivileged +// call pal. Since the call pal is unprivileged this routine is appropriate in +// any mode. +// +// Arguments: +// +// None. +// +// Return Value: +// +// Current TEB pointer. +// +//-- + + LEAF_ENTRY(NtCurrentTeb) + + GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) result in v0 + + ret zero, (ra) // return + + .end NtCurrentTeb diff --git a/private/ntos/rtl/alpha/ntrtlalp.h b/private/ntos/rtl/alpha/ntrtlalp.h new file mode 100644 index 000000000..772cf749b --- /dev/null +++ b/private/ntos/rtl/alpha/ntrtlalp.h @@ -0,0 +1,123 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ntrtlalp.h + +Abstract: + + Alpha specific parts of ntrtlp.h. + +Author: + + David N. Cutler (davec) 19-Apr-90 + +Revision History: + + Thomas Van Baak (tvb) 5-May-1992 + + Adapted for Alpha AXP. + +--*/ + +// +// Define exception routine function prototypes. +// + +EXCEPTION_DISPOSITION +RtlpExecuteHandlerForException ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN ULONG EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext, + IN PEXCEPTION_ROUTINE ExceptionRoutine + ); + +EXCEPTION_DISPOSITION +RtlpExecuteHandlerForUnwind ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN ULONG EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext, + IN PEXCEPTION_ROUTINE ExceptionRoutine + ); + +// +// Define procedure prototypes for exception filter and termination handler +// execution routines. +// + +LONG +RtlpExecuteExceptionFilter ( + PEXCEPTION_POINTERS ExceptionPointers, + EXCEPTION_FILTER ExceptionFilter, + ULONG EstablisherFrame + ); + +VOID +RtlpExecuteTerminationHandler ( + BOOLEAN AbnormalTermination, + TERMINATION_HANDLER TerminationHandler, + ULONG EstablisherFrame + ); + +// +// Define function prototype for restore context. +// + +VOID +RtlpRestoreContext ( + IN PCONTEXT Context + ); + +#if DBG + +// +// Define global flags to debug/validate exception handling for Alpha. +// + +extern ULONG RtlDebugFlags; + +// +// Print exception records as delivered by PALcode (KiDispatchException). +// + +#define RTL_DBG_PAL_EXCEPTION 0x00001 + +// +// Software raised exceptions (RtlRaiseException, RtlRaiseStatus). +// + +#define RTL_DBG_RAISE_EXCEPTION 0x00002 + +// +// Find a handler to take the exception (RtlDispatchException). +// + +#define RTL_DBG_DISPATCH_EXCEPTION 0x00030 +#define RTL_DBG_DISPATCH_EXCEPTION_DETAIL 0x00020 + +// +// Call handlers and unwind to a target frame (RtlUnwind). +// + +#define RTL_DBG_UNWIND 0x00300 +#define RTL_DBG_UNWIND_DETAIL 0x00200 + +// +// Climb one frame up the call stack (RtlVirtualUnwind). +// + +#define RTL_DBG_VIRTUAL_UNWIND 0x03000 +#define RTL_DBG_VIRTUAL_UNWIND_DETAIL 0x02000 + +// +// Find the function entry for a given PC (RtlLookupFunctionEntry). +// + +#define RTL_DBG_FUNCTION_ENTRY 0x30000 +#define RTL_DBG_FUNCTION_ENTRY_DETAIL 0x20000 + +#endif // DBG diff --git a/private/ntos/rtl/alpha/setjmp.s b/private/ntos/rtl/alpha/setjmp.s new file mode 100644 index 000000000..830f9465e --- /dev/null +++ b/private/ntos/rtl/alpha/setjmp.s @@ -0,0 +1,148 @@ +// TITLE("Set Jump") +//++ +// +// Copyright (c) 1993 Microsoft Corporation +// Copyright (c) 1993 Digital Equipment Corporation +// +// Module Name: +// +// setjmp.s +// +// Abstract: +// +// This module implements the Alpha acc compiler specific routine to +// provide SAFE handling of setjmp/longjmp with respect to structured +// exception handling. +// +// N.B. This function has been replaced by setjmp/setjmpex/longjmp in the +// C runtime library. It remains here for backwards compatibility of +// Beta 2 applications expecting setjmp and longjmp to be present in +// ntdll, or for new acc compiled applications that link with ntdll +// before libc. +// +// Author: +// +// David N. Cutler (davec) 2-Apr-1993 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +// Thomas Van Baak (tvb) 22-Apr-1993 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + + SBTTL("Set Jump") +//++ +// +// int +// setjmp ( +// IN jmp_buf JumpBuffer +// ) +// +// Routine Description: +// +// This function implements a safe setjmp. +// +// Arguments: +// +// JumpBuffer (a0) - Supplies the address of a jump buffer to store the +// jump information. +// +// N.B. This is an array of double's to force quadword alignment. +// +// Return Value: +// +// A value of zero is returned. +// +//-- + + .struct 0 +SjRa: .space 8 // saved return address +SjS0: .space 8 // saved integer register s0 +SjFl: .space 8 // InFunction flag variable +SjEf: .space 8 // EstablisherFrame(s) structure +SjCx: .space ContextFrameLength // context frame +SetjmpFrameLength: + + NESTED_ENTRY(setjmp, SetjmpFrameLength, zero) + + lda sp, -SetjmpFrameLength(sp) // allocate stack frame + stq ra, SjRa(sp) // save return address + stq s0, SjS0(sp) // save integer register s0 + + PROLOGUE_END + +// +// Save the nonvolatile machine state. +// + + lda t0, SjCx(sp) // address of context record + + .set noreorder + .set noat + stt f2, CxFltF2(t0) // save floating registers f2 - f9 + stt f3, CxFltF3(t0) // + stt f4, CxFltF4(t0) // + stt f5, CxFltF5(t0) // + stt f6, CxFltF6(t0) // + stt f7, CxFltF7(t0) // + stt f8, CxFltF8(t0) // + stt f9, CxFltF9(t0) // + + stq s0, CxIntS0(t0) // save integer registers s0 - fp/s6 + stq s1, CxIntS1(t0) // + stq s2, CxIntS2(t0) // + stq s3, CxIntS3(t0) // + stq s4, CxIntS4(t0) // + stq s5, CxIntS5(t0) // + stq fp, CxIntFp(t0) // + .set at + .set reorder + + stq gp, CxIntGp(t0) // save integer register gp + lda v0, SetjmpFrameLength(sp) // compute stack pointer of caller + stq v0, CxIntSp(t0) // save caller stack pointer + stq ra, CxIntRa(t0) // save return address + stq ra, CxFir(t0) // save continuation address + ldil t1, 2 // get acc safe setjmp flag + stl t1, JbType(a0) // set jump buffer context type + stl ra, JbPc(a0) // save target instruction address + mov a0, s0 // preserve jump buffer address + +// +// Perform unwind to determine the virtual frame pointer of the caller. +// + + subl ra, 4, a0 // compute control PC address + bsr RtlLookupFunctionEntry // lookup function table address + + ldq a0, SjRa(sp) // get return address + subl a0, 4, a0 // compute control PC address + mov v0, a1 // set address of function entry + lda a2, SjCx(sp) // address of context record + lda a3, SjFl(sp) // set address of in function variable + lda a4, SjEf(sp) // set frame pointers address + mov zero, a5 // set context pointer array address + bsr RtlVirtualUnwind // compute virtual frame pointer value + +// +// Set return value, restore registers, deallocate stack frame, and return. +// + + ldl t0, SjEf(sp) // get virtual frame pointer + stl t0, JbFp(s0) // save virtual frame pointer address + mov zero, v0 // set return value + + ldq s0, SjS0(sp) // restore integer register s0 + ldq ra, SjRa(sp) // restore return address + lda sp, SetjmpFrameLength(sp) // deallocate stack frame + ret zero, (ra) // return + + .end setjmp diff --git a/private/ntos/rtl/alpha/tcmpmem.c b/private/ntos/rtl/alpha/tcmpmem.c new file mode 100644 index 000000000..11ef83631 --- /dev/null +++ b/private/ntos/rtl/alpha/tcmpmem.c @@ -0,0 +1,105 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + tcmpmem.c + +Abstract: + + This module implements a test of the operation of the RtlCompareMemory + function by running an exhaustive test of every case of string offset, + compare length, and return value up to and a little beyond one 32-byte + cache line. This represents over one million test cases. It is assumed + any bugs that exist will be found within this range. If only the error + count summary is desired, type "tcmpmem > nul" instead. + +Author: + + Thomas Van Baak (tvb) 11-Jan-1993 + +Environment: + + User mode. + +Revision History: + +--*/ + +#include +#include +#include +#include "localrtl.h" + +#define BUFFER_SIZE (MAX_OFFSET + MAX_LENGTH) + +UCHAR String1[BUFFER_SIZE]; +UCHAR String2[BUFFER_SIZE]; + +void +_CRTAPI1 +main() +{ + ULONG ErrorCount; + ULONG Expected; + ULONG Length; + ULONG Offset1; + ULONG Offset2; + ULONG Result; + ULONG TestCases; + + fprintf(stderr, "Testing RtlCompareMemory\n"); + ErrorCount = 0; + TestCases = 0; + + for (Offset1 = 0; Offset1 <= MAX_OFFSET; Offset1 += 1) { + + // + // Copy the test pattern to Offset1 in String1 and then for each + // possible offset of String1, for each possible offset of String2, + // for each possible string compare length, and for each expected + // return value, make a call RtlCompareMemory. + // + + FillPattern(&String1[Offset1], MAX_LENGTH); + for (Offset2 = 0; Offset2 <= MAX_OFFSET; Offset2 += 1) { + for (Length = 0; Length <= MAX_LENGTH; Length += 1) { + for (Expected = 0; Expected <= Length; Expected += 1) { + + // + // Copy the test pattern starting at Offset2 in String2, + // change one byte at location `Expected', call + // RtlCompareMemory, and check that the function value + // is in fact the expected value. + // + + FillPattern(&String2[Offset2], MAX_LENGTH); + String2[Offset2 + Expected] = ' '; + Result = RtlCompareMemory(&String1[Offset1], + &String2[Offset2], + Length); + TestCases += 1; + if (Result != Expected) { + ErrorCount += 1; + + // + // The function failed to return the proper value. + // + + printf("ERROR: Offset1 = %d, Offset2 = %d, Length = %d, Expected = %d, Result = %d\n", + Offset1, Offset2, Length, Expected, Result); + printf(" String1[Offset1] = %lx: <%.*s>\n", + &String1[Offset1], Length, &String1[Offset1]); + printf(" String2[Offset2] = %lx: <%.*s>\n", + &String2[Offset2], Length, &String2[Offset2]); + printf("\n"); + } + } + } + } + } + + fprintf(stderr, "Test of RtlCompareMemory completed: "); + fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount); +} diff --git a/private/ntos/rtl/alpha/tfilmem.c b/private/ntos/rtl/alpha/tfilmem.c new file mode 100644 index 000000000..0a347599c --- /dev/null +++ b/private/ntos/rtl/alpha/tfilmem.c @@ -0,0 +1,79 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + tfilmem.c + +Abstract: + + This module implements a test of the operation of the RtlFillMemory + function by running an exhaustive test of every case of string offset + and length up to and a little beyond one 32-byte cache line. This + represents several thousand test cases. It is assumed any bugs that + exist will be found within this range. If only the error count summary + is desired, type "tfilmem > nul" instead. + +Author: + + Thomas Van Baak (tvb) 13-Jan-1993 + +Environment: + + User mode. + +Revision History: + +--*/ + +#include +#include +#include +#include "localrtl.h" + +#define BUFFER_SIZE (MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN) + +UCHAR Buffer1[BUFFER_SIZE]; +UCHAR Buffer2[BUFFER_SIZE]; + +void +_CRTAPI1 +main() +{ + ULONG ErrorCount; + ULONG Length; + ULONG Offset; + ULONG Result; + ULONG TestCases; + + fprintf(stderr, "Testing RtlFillMemory\n"); + ErrorCount = 0; + TestCases = 0; + + for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) { + for (Length = 0; Length <= MAX_LENGTH; Length += 1) { + + FillPattern(Buffer1, BUFFER_SIZE); + FillPattern(Buffer2, BUFFER_SIZE); + LocalFillMemory(&Buffer1[Offset], Length, '@'); + RtlFillMemory(&Buffer2[Offset], Length, '@'); + + Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE); + + TestCases += 1; + if (Result != BUFFER_SIZE) { + ErrorCount += 1; + + printf("ERROR: Offset = %d, Length = %d\n", Offset, Length); + printf("Buffers differ starting at byte %d:\n", Result); + printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1); + printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2); + printf("\n"); + } + } + } + + fprintf(stderr, "Test of RtlFillMemory completed: "); + fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount); +} diff --git a/private/ntos/rtl/alpha/tmovmem.c b/private/ntos/rtl/alpha/tmovmem.c new file mode 100644 index 000000000..3b381d9ba --- /dev/null +++ b/private/ntos/rtl/alpha/tmovmem.c @@ -0,0 +1,129 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + tmovmem.c + +Abstract: + + This module implements a test of the operation of the RtlMoveMemory + function by running an exhaustive test of every case of string offset, + move length, and relative overlap of the two strings up to and a little + beyond one 32-byte cache line. This represents several hundred thousand + test cases. It is assumed any bugs that exist will be found within this + range. If only the error count summary is desired, type "tmovmem > nul" + instead. + +Author: + + Thomas Van Baak (tvb) 13-Jan-1993 + +Environment: + + User mode. + +Revision History: + +--*/ + +#include +#include +#include +#include "localrtl.h" + +// +// Two strings are defined within a large buffer. The target string is +// initially below the source string with a small gap between the two +// strings. A margin around the strings ensures any bytes accidentally +// changed outside the strings are detectable. As the length of the strings +// and the offset of the source string are varied, the target string wanders +// from well below, through, and well above the source string. +// + +#define MIN_OVERLAP (-(MAX_LENGTH + MAX_MARGIN)) +#define MAX_OVERLAP (MAX_LENGTH + MAX_MARGIN - 1) + +#define BUFFER_SIZE (MAX_MARGIN + MAX_LENGTH + MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN + MAX_LENGTH + MAX_MARGIN) + +UCHAR Buffer0[BUFFER_SIZE]; +UCHAR Buffer1[BUFFER_SIZE]; +UCHAR Buffer2[BUFFER_SIZE]; + +void +_CRTAPI1 +main() +{ + ULONG ErrorCount; + ULONG Length; + ULONG Offset; + LONG Overlap; + ULONG Result; + ULONG Source; + ULONG Target; + ULONG TestCases; + + fprintf(stderr, "Testing RtlMoveMemory\n"); + ErrorCount = 0; + TestCases = 0; + + // + // Make a call to RtlMoveMemory for all possible source string offsets + // within a cache line, for a large set of string lengths, and a wide + // range of positions of the target string relative to the source string, + // including all possible overlapping string configurations. + // + + for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) { + for (Length = 0; Length <= MAX_LENGTH; Length += 1) { + for (Overlap = MIN_OVERLAP; Overlap <= MAX_OVERLAP; Overlap += 1) { + + // + // The same string configuration is made in two different + // buffers. RtlMoveMemory is used on the two strings in one + // buffer and the trusted LocalMoveMemory on the two strings + // in the other buffer. The entire buffers are compared to + // determine if the two move functions agree. + // + + FillPattern(Buffer1, BUFFER_SIZE); + FillPattern(Buffer2, BUFFER_SIZE); + + Source = MAX_MARGIN + MAX_LENGTH + MAX_MARGIN + Offset; + Target = Source + Overlap; + + LocalMoveMemory(&Buffer1[Target], &Buffer1[Source], Length); + RtlMoveMemory(&Buffer2[Target], &Buffer2[Source], Length); + + Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE); + + TestCases += 1; + if (Result != BUFFER_SIZE) { + ErrorCount += 1; + + printf("ERROR: Offset = %d, Length = %d, Overlap = %d\n", + Offset, Length, Overlap); + printf("RtlMoveMemory( &Buffer[ %d ], &Buffer[ %d ], %d )\n", + Target, Source, Length); + + FillPattern(Buffer0, BUFFER_SIZE); + printf(" Original Source = %lx: <%.*s>\n", + &Buffer0[Source], Length, &Buffer0[Source]); + printf(" Expected Target = %lx: <%.*s>\n", + &Buffer1[Target], Length, &Buffer1[Target]); + printf(" Actual Target = %lx: <%.*s>\n", + &Buffer2[Target], Length, &Buffer2[Target]); + printf("\n"); + printf("Buffers differ starting at byte %d:\n", Result); + printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1); + printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2); + printf("\n"); + } + } + } + } + + fprintf(stderr, "Test of RtlMoveMemory completed: "); + fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount); +} diff --git a/private/ntos/rtl/alpha/trampoln.s b/private/ntos/rtl/alpha/trampoln.s new file mode 100644 index 000000000..b37d471e7 --- /dev/null +++ b/private/ntos/rtl/alpha/trampoln.s @@ -0,0 +1,524 @@ +// TITLE("Trampoline Code For User Mode APC and Exception Dispatching") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// Copyright (c) 1992 Digital Equipment Corporation +// +// Module Name: +// +// trampoln.s +// +// Abstract: +// +// This module implements the trampoline code necessary to dispatch user +// mode APCs and exceptions. +// +// Author: +// +// David N. Cutler (davec) 3-Apr-1990 +// +// Environment: +// +// User mode only. +// +// Revision History: +// +// Thomas Van Baak (tvb) 11-May-1992 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + +// +// Define length of exception dispatcher stack frame. +// + +#define ExceptionDispatcherFrameLength (ExceptionRecordLength + ContextFrameLength) + + SBTTL("User APC Dispatcher") +//++ +// +// The following code is never executed. Its purpose is to support unwinding +// through the call to the APC dispatcher. +// +//-- + +// +// N.B. This function specifies its own private exception handler. +// + EXCEPTION_HANDLER(KiUserApcHandler) + + NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero); + + .set noreorder + .set noat + stq sp, CxIntSp(sp) // save stack pointer + stq ra, CxIntRa(sp) // save return address + stq ra, CxFir(sp) // set continuation address + stq fp, CxIntFp(sp) // save integer register fp + stq gp, CxIntGp(sp) // save integer register gp + + stq s0, CxIntS0(sp) // save integer registers s0 - s5 + stq s1, CxIntS1(sp) // + stq s2, CxIntS2(sp) // + stq s3, CxIntS3(sp) // + stq s4, CxIntS4(sp) // + stq s5, CxIntS5(sp) // + + stt f2, CxFltF2(sp) // save floating registers f2 - f9 + stt f3, CxFltF3(sp) // + stt f4, CxFltF4(sp) // + stt f5, CxFltF5(sp) // + stt f6, CxFltF6(sp) // + stt f7, CxFltF7(sp) // + stt f8, CxFltF8(sp) // + stt f9, CxFltF9(sp) // + + mov sp, fp // set frame pointer + .set at + .set reorder + + PROLOGUE_END + +//++ +// +// VOID +// KiUserApcDispatcher ( +// IN PVOID NormalContext, +// IN PVOID SystemArgument1, +// IN PVOID SystemArgument2, +// IN PKNORMAL_ROUTINE NormalRoutine +// ) +// +// Routine Description: +// +// This routine is entered on return from kernel mode to deliver an APC +// in user mode. The context frame for this routine was built when the +// APC interrupt was processed and contains the entire machine state of +// the current thread. The specified APC routine is called and then the +// machine state is restored and execution is continued. +// +// Arguments: +// +// a0 - Supplies the normal context parameter that was specified when the +// APC was initialized. +// +// a1 - Supplies the first argument that was provided by the executive when +// the APC was queued. +// +// a2 - Supplies the second argument that was provided by the executive +// when the APC was queued. +// +// a3 - Supplies the address of the function that is to be called. +// +// N.B. Register sp supplies a pointer to a context frame. +// +// N.B. Register fp supplies the same value as sp and is used as a frame +// pointer. +// +// Return Value: +// +// None. +// +//-- + +// +// N.B. This function is not called in the typical way. Instead of a normal +// subroutine call to the nested entry point above, the alternate entry point +// address below is stuffed into the Fir address of the trap frame. Thus when +// the kernel returns from the trap, the following code is executed directly. +// + ALTERNATE_ENTRY(KiUserApcDispatcher) + + jsr ra, (a3) // call specified APC routine + + mov fp, a0 // set address of context frame + ldil a1, TRUE // set test alert argument true + bsr ra, ZwContinue // execute system service to continue + mov v0, s0 // save status value + +// +// Unsuccessful completion after attempting to continue execution. Use the +// return status as the exception code, set noncontinuable exception and +// attempt to raise another exception. Note there is no return from raise +// status. +// + +10: mov s0, a0 // set status value + bsr ra, RtlRaiseStatus // raise exception + br zero, 10b // loop on return + + .end KiUserApcDispatch + + SBTTL("User APC Exception Handler") +//++ +// +// EXCEPTION_DISPOSITION +// KiUserApcHandler ( +// IN PEXCEPTION_RECORD ExceptionRecord, +// IN ULONG EstablisherFrame, +// IN OUT PCONTEXT ContextRecord, +// IN OUT PDISPATCHER_CONTEXT DispatcherContext +// +// Routine Description: +// +// This function is called when an exception occurs in an APC routine +// or one of its dynamic descendents, or when an unwind through the +// APC dispatcher is in progress. If an unwind is in progress, then test +// alert is called to ensure that all currently queued APCs are executed. +// +// Arguments: +// +// ExceptionRecord (a0) - Supplies a pointer to an exception record. +// +// EstablisherFrame (a1) - Supplies the frame pointer of the establisher +// of this exception handler. +// +// ContextRecord (a2) - Supplies a pointer to a context record. +// +// DispatcherContext (a3) - Supplies a pointer to the dispatcher context +// record. +// +// Return Value: +// +// ExceptionContinueSearch is returned as the function value. +// +//-- + + .struct 0 +HdRa: .space 8 // saved return address + .space 1 * 8 // required for 16-byte stack alignment +HandlerFrameLength: // length of handler frame + + NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero) + + lda sp, -HandlerFrameLength(sp) // allocate stack frame + stq ra, HdRa(sp) // save return address + + PROLOGUE_END + +// +// The following code is equivalent to: +// +// EXCEPTION_DISPOSITION +// KiUserApcHandler(IN PEXCEPTION_RECORD ExceptionRecord) +// { +// if (IS_UNWINDING(ExceptionRecord->ExceptionFlags)) { +// NtTestAlert(); +// } +// return ExceptionContinueSearch +// } +// + + ldl t0, ErExceptionFlags(a0) // get exception flags + and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress + beq t0, 10f // if eq, no unwind in progress + + bsr ra, ZwTestAlert // test for alert pending + +10: ldil v0, ExceptionContinueSearch // set disposition value + ldq ra, HdRa(sp) // restore return address + lda sp, HandlerFrameLength(sp) // deallocate stack frame + ret zero, (ra) // return + + .end KiUserApcHandler + + + SBTTL("User Callback Dispatcher") +//++ +// +// The following code is never executed. Its purpose is to support unwinding +// through the call to the exception dispatcher. +// +//-- + + NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero); +.set noreorder + stq sp, CkSp(sp) + stq ra, CkRa(sp) +.set reorder + PROLOGUE_END +//++ +// +// VOID +// KiUserCallbackDispatcher ( +// VOID +// ) +// +// Routine Description: +// +// This routine is entered on a callout from kernel mode to execute a +// user mode callback function. All arguments for this function have +// been placed on the stack. +// +// Arguments: +// +// (sp + 16) - Supplies a value of zero for alignment. +// +// (sp + 24) - Supplies the API number of the callback function that is +// executed. +// +// (sp + 32) - Supplies a pointer to the input buffer. +// +// (sp + 40) - Supplies the input buffer length. +// +// Return Value: +// +// This function returns to kernel mode. +// +//-- + + ALTERNATE_ENTRY(KiUserCallbackDispatcher) + + ldl a0, CkBuffer(sp) // get input buffer address + ldl a1, CkLength(sp) // get input buffer length + ldl t0, CkApiNumber(sp) // get API number + GET_THREAD_ENVIRONMENT_BLOCK // get TEB in v0 + ldl t5, TePeb(v0) // get PEB in t5 + ldl t2, PeKernelCallbackTable(t5) // get address of callback table + s4addl t0, t2, t3 // get address of callback + ldl t4, 0(t3) // get callback pointer + jsr ra, (t4) // call specified function + +// +// If a return from the callback function occurs, then the output buffer +// address and length are returned as NULL. +// + + bis zero,zero,a0 // set zero buffer address + bis zero,zero,a1 // set zero buffer length + bis v0, zero, a2 // set completion status + bsr ra, ZwCallbackReturn // return to kernel mode + +// +// Unsuccessful completion after attempting to return to kernel mode. Use +// the return status as the exception code, set noncontinuable exception and +// attempt to raise another exception. Note there is no return from raise +// status. +// + + bis v0, zero, s0 // save status value +10: bis s0, zero, a0 // set status value + bsr ra, RtlRaiseStatus // raise exception + br zero, 10b // loop on return + + .end KiUserCallbackDispatch + + SBTTL("User Exception Dispatcher") +//++ +// +// The following code is never executed. Its purpose is to support unwinding +// through the call to the exception dispatcher. +// +// When reverse executed, this prologue will restore all integer registers, +// rather than just the non-volatile registers. This is necessary for proper +// unwinding through the call to the exception dispatcher when non-standard +// calls have been used in frames at or above the exception frame. Non-leaf +// functions using a non-standard call are allowed to save the return address +// register in another integer register instead of on the stack. +// +//-- + + NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrameLength, zero); + + .set noreorder + .set noat + stq sp, CxIntSp(sp) // save stack pointer + stq ra, CxIntRa(sp) // save return address + stq ra, CxFir(sp) // set continuation address + + stq v0, CxIntV0(sp) // save integer register v0 + stq t0, CxIntT0(sp) // save integer registers t0 - t6 + stq t1, CxIntT1(sp) // + stq t2, CxIntT2(sp) // + stq t3, CxIntT3(sp) // + stq t4, CxIntT4(sp) // + stq t5, CxIntT5(sp) // + stq t6, CxIntT6(sp) // + stq t7, CxIntT7(sp) // + + stq s0, CxIntS0(sp) // save integer registers s0 - s5 + stq s1, CxIntS1(sp) // + stq s2, CxIntS2(sp) // + stq s3, CxIntS3(sp) // + stq s4, CxIntS4(sp) // + stq s5, CxIntS5(sp) // + stq fp, CxIntFp(sp) // save integer register fp + + stq a0, CxIntA0(sp) // save integer registers a0 - a5 + stq a1, CxIntA1(sp) // + stq a2, CxIntA2(sp) // + stq a3, CxIntA3(sp) // + stq a4, CxIntA4(sp) // + stq a5, CxIntA5(sp) // + + stq t8, CxIntT8(sp) // save integer registers t8 - t11 + stq t9, CxIntT9(sp) // + stq t10, CxIntT10(sp) // + stq t11, CxIntT11(sp) // + + stq t12, CxIntT12(sp) // save integer register t12 + stq AT, CxIntAt(sp) // save integer register AT + stq gp, CxIntGp(sp) // save integer register gp + + stt f2, CxFltF2(sp) // save floating registers f2 - f9 + stt f3, CxFltF3(sp) // + stt f4, CxFltF4(sp) // + stt f5, CxFltF5(sp) // + stt f6, CxFltF6(sp) // + stt f7, CxFltF7(sp) // + stt f8, CxFltF8(sp) // + stt f9, CxFltF9(sp) // + + mov sp, fp // set frame pointer + .set at + .set reorder + + PROLOGUE_END + +//++ +// +// VOID +// KiUserExceptionDispatcher ( +// IN PEXCEPTION_RECORD ExceptionRecord, +// IN PCONTEXT ContextRecord +// ) +// +// Routine Description: +// +// This routine is entered on return from kernel mode to dispatch a user +// mode exception. If a frame based handler handles the exception, then +// the execution is continued. Otherwise last chance processing is performed. +// +// Arguments: +// +// s0 - Supplies a pointer to an exception record. +// +// s1 - Supplies a pointer to a context frame. +// +// fp - Supplies the same value as sp and is used as a frame pointer. +// +// Return Value: +// +// None. +// +//-- + +// +// N.B. This function is not called in the typical way. Instead of a normal +// subroutine call to the nested entry point above, the alternate entry point +// address below is stuffed into the Fir address of the trap frame. Thus when +// the kernel returns from the trap, the following code is executed directly. +// + + ALTERNATE_ENTRY(KiUserExceptionDispatcher) + + mov s0, a0 // set address of exception record + mov s1, a1 // set address of context frame + bsr ra, RtlDispatchException // attempt to dispatch the exception + +// +// If the return status is TRUE, then the exception was handled and execution +// should be continued with the NtContinue service in case the context was +// changed. If the return status is FALSE, then the exception was not handled +// and NtRaiseException is called to perform last chance exception processing. +// + + beq v0, 10f // if eq [false], perform last chance processing + +// +// Continue execution. +// + + mov s1, a0 // set address of context frame + ldil a1, FALSE // set test alert argument false + bsr ra, ZwContinue // execute system service to continue + br zero, 20f // join common code + +// +// Last chance processing. +// + +10: mov s0, a0 // set address of exception record + mov s1, a1 // set address of context frame + ldil a2, FALSE // set first chance argument false + bsr ra, ZwRaiseException // perform last chance processing + +// +// Common code for unsuccessful completion of the continue or last chance +// service. Use the return status (which is now in v0) as the exception code, +// set noncontinuable exception and attempt to raise another exception. Note +// the stack grows and eventually this loop will end. +// + +20: lda sp, -ExceptionRecordLength(sp) // allocate exception record + mov sp, a0 // get address of actual record + stl v0, ErExceptionCode(a0) // set exception code + ldil t0, EXCEPTION_NONCONTINUABLE // set noncontinuable flag + stl t0, ErExceptionFlags(a0) // store exception flags + stl s0, ErExceptionRecord(a0) // set associated exception record + stl zero, ErNumberParameters(a0) // set number of parameters + bsr ra, RtlRaiseException // raise exception + br zero, 20b // loop on error + + .end KiUserExceptionDispatch + +//++ +// +// NTSTATUS +// KiRaiseUserExceptionDispatcher ( +// IN NTSTATUS ExceptionCode +// ) +// +// Routine Description: +// +// This routine is entered on return from kernel mode to raise a user +// mode exception. +// +// Arguments: +// +// v0 - Supplies the status code to be raised. +// +// Return Value: +// +// ExceptionCode +// +//-- + +// +// N.B. This function is not called in the typical way. Instead of a normal +// subroutine call to the nested entry point above, the alternate entry point +// address below is stuffed into the Fir address of the trap frame. Thus when +// the kernel returns from the trap, the following code is executed directly. +// + + .struct 0 +RaiseRa: .space 8 // saved return address +RaiseV0: .space 8 // saved S0 +RaiseExr: .space ExceptionRecordLength // exception record for RtlRaiseException +RaiseFrameLength: // length of handler frame + + NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero) + lda sp, -RaiseFrameLength(sp) // allocate stack frame + stq ra, RaiseRa(sp) // save return address + PROLOGUE_END + + stq v0, RaiseV0(sp) // save function return status + stl v0, ErExceptionCode+RaiseExr(sp) // set exception code + stl zero, ErExceptionFlags+RaiseExr(sp) // set exception flags + stl zero, ErExceptionRecord+RaiseExr(sp) // set exception record + stl ra, ErExceptionAddress+RaiseExr(sp) // set exception address + stl zero, ErNumberParameters+RaiseExr(sp) + + lda a0, RaiseExr(sp) // set argument to RtlRaiseException + bsr ra, RtlRaiseException // attempt to raise the exception + + ldq v0, RaiseV0(sp) // return status + + ldq ra, RaiseRa(sp) // restore ra + lda sp, RaiseFrameLength(sp) // deallocate stack frame + ret zero, (ra) // return + + .end KiRaiseUserExceptionDispatch diff --git a/private/ntos/rtl/alpha/tzermem.c b/private/ntos/rtl/alpha/tzermem.c new file mode 100644 index 000000000..c56f1620a --- /dev/null +++ b/private/ntos/rtl/alpha/tzermem.c @@ -0,0 +1,79 @@ +/*++ + +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + tzermem.c + +Abstract: + + This module implements a test of the operation of the RtlZeroMemory + function by running an exhaustive test of every case of string offset + and length up to and a little beyond one 32-byte cache line. This + represents several thousand test cases. It is assumed any bugs that + exist will be found within this range. If only the error count summary + is desired, type "tzermem > nul" instead. + +Author: + + Thomas Van Baak (tvb) 13-Jan-1993 + +Environment: + + User mode. + +Revision History: + +--*/ + +#include +#include +#include +#include "localrtl.h" + +#define BUFFER_SIZE (MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN) + +UCHAR Buffer1[BUFFER_SIZE]; +UCHAR Buffer2[BUFFER_SIZE]; + +void +_CRTAPI1 +main() +{ + ULONG ErrorCount; + ULONG Length; + ULONG Offset; + ULONG Result; + ULONG TestCases; + + fprintf(stderr, "Testing RtlZeroMemory\n"); + ErrorCount = 0; + TestCases = 0; + + for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) { + for (Length = 0; Length <= MAX_LENGTH; Length += 1) { + + FillPattern(Buffer1, BUFFER_SIZE); + FillPattern(Buffer2, BUFFER_SIZE); + LocalZeroMemory(&Buffer1[Offset], Length); + RtlZeroMemory(&Buffer2[Offset], Length); + + Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE); + + TestCases += 1; + if (Result != BUFFER_SIZE) { + ErrorCount += 1; + + printf("ERROR: Offset = %d, Length = %d\n", Offset, Length); + printf("Buffers differ starting at byte %d:\n", Result); + printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1); + printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2); + printf("\n"); + } + } + } + + fprintf(stderr, "Test of RtlZeroMemory completed: "); + fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount); +} diff --git a/private/ntos/rtl/alpha/unwindr.c b/private/ntos/rtl/alpha/unwindr.c new file mode 100644 index 000000000..ff8c4a562 --- /dev/null +++ b/private/ntos/rtl/alpha/unwindr.c @@ -0,0 +1,871 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation +Copyright (c) 1993 Digital Equipment Corporation + +Module Name: + + unwindr.c + +Abstract: + + This module implements two alternate versions of the unwind function + required by Alpha AXP during the GEM compiler transition period. The + code is adapted from RtlUnwind (exdsptch.c). These functions can be + deleted if GEM uses scope table based structured exception handling + and can materialize virtual frame pointers. + +Author: + + Thomas Van Baak (tvb) 18-Nov-1992 + +Environment: + + Any mode. + +Revision History: + +--*/ + +#include "ntrtlp.h" + +// +// Define local macros. +// +// Raise noncontinuable exception with associated exception record. +// + +#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \ + EXCEPTION_RECORD ExceptionRecordn; \ + \ + ExceptionRecordn.ExceptionCode = Status; \ + ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \ + ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \ + ExceptionRecordn.NumberParameters = 0; \ + RtlRaiseException(&ExceptionRecordn); \ + } + +// +// The low 2 bits of ExceptionHandler are flags bits and not part of the +// exception handler address. +// + +#define IS_HANDLER_DEFINED(FunctionEntry) \ + (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0) + +#if DBG + +// +// Maintain a short history of PC's for malformed function table errors. +// + +#define PC_HISTORY_DEPTH 4 + +// +// Definition of global flag to debug/validate exception handling. +// See ntrtlalp.h for the bit definitions in this flag word. +// + +ULONG RtlDebugFlags; + +#endif + +#define Virtual VirtualFramePointer +#define Real RealFramePointer + +VOID +RtlUnwindRfp ( + IN PVOID TargetRealFrame OPTIONAL, + IN PVOID TargetIp OPTIONAL, + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN PVOID ReturnValue + ) + +/*++ + +Routine Description: + + This function initiates an unwind of procedure call frames. The machine + state at the time of the call to unwind is captured in a context record + and the unwinding flag is set in the exception flags of the exception + record. If the TargetRealFrame parameter is not specified, then the exit + unwind flag is also set in the exception flags of the exception record. + A backward scan through the procedure call frames is then performed to + find the target of the unwind operation. + + As each frame is encountered, the PC where control left the corresponding + function is determined and used to lookup exception handler information + in the runtime function table built by the linker. If the respective + routine has an exception handler, then the handler is called. + + This function is identical to RtlUnwind except that the TargetRealFrame + parameter is the real frame pointer instead of the virtual frame pointer. + +Arguments: + + TargetRealFrame - Supplies an optional pointer to the call frame that is + the target of the unwind. If this parameter is not specified, then an + exit unwind is performed. + + TargetIp - Supplies an optional instruction address that specifies the + continuation address of the unwind. This address is ignored if the + target frame parameter is not specified. + + ExceptionRecord - Supplies an optional pointer to an exception record. + + ReturnValue - Supplies a value that is to be placed in the integer + function return register just before continuing execution. + +Return Value: + + None. + +--*/ + +{ + + CONTEXT ContextRecord1; + CONTEXT ContextRecord2; + ULONG ControlPc; +#if DBG + ULONG ControlPcHistory[PC_HISTORY_DEPTH]; + ULONG ControlPcHistoryIndex = 0; +#endif + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + FRAME_POINTERS EstablisherFrame; + ULONG ExceptionFlags; + EXCEPTION_RECORD ExceptionRecord1; +#if DBG + LONG FrameDepth = 0; +#endif + PRUNTIME_FUNCTION FunctionEntry; + ULONG HighLimit; + BOOLEAN InFunction; + ULONG LastPc; + ULONG LowLimit; + +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("\nRtlUnwindRfp(TargetRealFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n", + TargetRealFrame, TargetIp, ReturnValue); + } +#endif + // + // Get current stack limits, capture the current context, virtually + // unwind to the caller of this routine, get the initial PC value, and + // set the unwind target address. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + RtlCaptureContext(&ContextRecord1); + ControlPc = (ULONG)ContextRecord1.IntRa; + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + LastPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + + ControlPc = LastPc; + ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp; + + // + // If an exception record is not specified, then build a local exception + // record for use in calling exception handlers during the unwind operation. + // + + if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { + ExceptionRecord = &ExceptionRecord1; + ExceptionRecord1.ExceptionCode = STATUS_UNWIND; + ExceptionRecord1.ExceptionRecord = NULL; + ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc; + ExceptionRecord1.NumberParameters = 0; + } + + // + // If the target frame of the unwind is specified, then a normal unwind + // is being performed. Otherwise, an exit unwind is being performed. + // + + ExceptionFlags = EXCEPTION_UNWINDING; + if (ARGUMENT_PRESENT(TargetRealFrame) == FALSE) { + ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND; + } + + // + // Scan backward through the call frame hierarchy and call exception + // handlers until the target frame of the unwind is reached. + // + + do { +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwindRfp: Loop: FrameDepth = %d, Rfp = %lx, sp = %lx, ControlPc = %lx\n", + FrameDepth, EstablisherFrame.Real, ContextRecord1.IntSp, ControlPc); + FrameDepth -= 1; + } +#endif + + // + // Lookup the function table entry using the point at which control + // left the procedure. + // + + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + + // + // If there is a function table entry for the routine, then copy the + // context record, virtually unwind to the caller of the current + // routine to obtain the real frame pointer of the establisher and + // check if there is an exception handler for the frame. + // + + if (FunctionEntry != NULL) { + RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT)); + LastPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + + // + // If the real and virtual frame pointers are not within the + // specified stack limits, the frame pointers are unaligned, or + // the target frame is below the real frame and an exit unwind is + // not being performed, then raise the exception STATUS_BAD_STACK. + // Otherwise, check to determine if the current routine has an + // exception handler. + // + + if ((EstablisherFrame.Real < LowLimit) || + (EstablisherFrame.Virtual > HighLimit) || + (EstablisherFrame.Real > EstablisherFrame.Virtual) || + ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) && + ((ULONG)TargetRealFrame < EstablisherFrame.Real)) || + ((EstablisherFrame.Virtual & 0xF) != 0) || + ((EstablisherFrame.Real & 0xF) != 0)) { +#if DBG + DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n"); + DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n", + EstablisherFrame.Virtual, EstablisherFrame.Real); + DbgPrint(" TargetRealFrame = %08lx\n", TargetRealFrame); + if ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) && + ((ULONG)TargetRealFrame < EstablisherFrame.Real)) { + DbgPrint(" TargetRealFrame is below EstablisherFrame.Real!\n"); + } + DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n", + (ULONG)ContextRecord2.IntSp); + DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n", + LowLimit, HighLimit); + DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n", + LastPc, ControlPc); + DbgPrint(" Now raising STATUS_BAD_STACK exception.\n"); +#endif + RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord); + + } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) { +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlUnwindRfp: ExceptionHandler = %lx, HandlerData = %lx\n", + FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData); +} +#endif + + // + // The frame has an exception handler. + // + // The control PC, establisher frame pointer, the address + // of the function table entry, and the address of the + // context record are all stored in the dispatcher context. + // This information is used by the unwind linkage routine + // and can be used by the exception handler itself. + // + // A linkage routine written in assembler is used to actually + // call the actual exception handler. This is required by the + // exception handler that is associated with the linkage + // routine so it can have access to two sets of dispatcher + // context when it is called. + // + + DispatcherContext.ControlPc = ControlPc; + DispatcherContext.FunctionEntry = FunctionEntry; + DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual; + DispatcherContext.ContextRecord = &ContextRecord2; + + // + // Call the exception handler. + // + + do { + + // + // If the establisher frame is the target of the unwind + // operation, then set the target unwind flag. + // + + if ((ULONG)TargetRealFrame == EstablisherFrame.Real) { + ExceptionFlags |= EXCEPTION_TARGET_UNWIND; + } + + ExceptionRecord->ExceptionFlags = ExceptionFlags; + + // + // Set the specified return value in case the exception + // handler directly continues execution. + // + + ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue; +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwindRfp: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc); + } +#endif + Disposition = + RtlpExecuteHandlerForUnwind(ExceptionRecord, + EstablisherFrame.Virtual, + &ContextRecord2, + &DispatcherContext, + FunctionEntry->ExceptionHandler); +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwindRfp: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition); + } +#endif + + // + // Clear target unwind and collided unwind flags. + // + + ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND | + EXCEPTION_TARGET_UNWIND); + + // + // Case on the handler disposition. + // + + switch (Disposition) { + + // + // The disposition is to continue the search. + // + // Continue the search for a handler or continue + // execution. + // + + case ExceptionContinueSearch : + break; + + // + // The disposition is collided unwind. + // + // Set the target of the current unwind to the context + // record of the previous unwind, virtually unwind to + // the caller of the old routine, and reexecute the + // exception handler from the collided frame with the + // collided unwind flag set in the exception record. + // + + case ExceptionCollidedUnwind : + ControlPc = DispatcherContext.ControlPc; + FunctionEntry = DispatcherContext.FunctionEntry; + RtlMoveMemory(&ContextRecord1, + DispatcherContext.ContextRecord, + sizeof(CONTEXT)); + + ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp; + RtlMoveMemory(&ContextRecord2, + &ContextRecord1, + sizeof(CONTEXT)); + + ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; + LastPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + break; + + // + // All other disposition values are invalid. + // + // Raise invalid disposition exception. + // + + default : + RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); + } + + } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0); + } + + } else { + + // + // Set point at which control left the previous routine. + // + + LastPc = (ULONG)ContextRecord1.IntRa - 4; + + // + // If the next control PC is the same as the old control PC, then + // the function table is not correctly formed. + // + + if (LastPc == ControlPc) { +#if DBG + ULONG Count; + DbgPrint("\n****** Warning - malformed function table (unwind).\n"); + DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc); + for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) { + if (ControlPcHistoryIndex > 0) { + ControlPcHistoryIndex -= 1; + ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH]; + DbgPrint(", %08lx", ControlPc); + } + } + DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n"); + DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n"); +#endif + RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); + } + } + + // + // Set point at which control left the previous routine. + // + +#if DBG + ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc; + ControlPcHistoryIndex += 1; +#endif + ControlPc = LastPc; + + } while ((EstablisherFrame.Real < HighLimit) && + (EstablisherFrame.Real != (ULONG)TargetRealFrame)); + + // + // If the establisher stack pointer is equal to the target frame + // pointer, then continue execution. Otherwise, an exit unwind was + // performed or the target of the unwind did not exist and the + // debugger and subsystem are given a second chance to handle the + // unwind. + // + + if (EstablisherFrame.Real == (ULONG)TargetRealFrame) { + ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue; +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("RtlUnwindRfp: finished unwinding, and calling RtlpRestoreContext\n"); + } +#endif + RtlpRestoreContext(&ContextRecord2); + + } else { +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("RtlUnwindRfp: finished unwinding, but calling ZwRaiseException\n"); + } +#endif + ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE); + } +} + +VOID +RtlUnwindReturn ( + IN PVOID TargetFrame, + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL, + IN PVOID ReturnValue + ) + +/*++ + +Routine Description: + + This function initiates an unwind of procedure call frames. The machine + state at the time of the call to unwind is captured in a context record + and the unwinding flag is set in the exception flags of the exception + record. A backward scan through the procedure call frames is then + performed to find the target of the unwind operation. When the target + frame is reached, a return is made to the caller of the target frame + with the return value specified by the return value parameter. + + As each frame is encountered, the PC where control left the corresponding + function is determined and used to lookup exception handler information + in the runtime function table built by the linker. If the respective + routine has an exception handler, then the handler is called. + + This function is identical to RtlUnwind except control resumes in the + caller of the target frame, not in the target frame itself. + +Arguments: + + TargetFrame - Supplies an optional pointer to the call frame that is the + target of the unwind. + + ExceptionRecord - Supplies an optional pointer to an exception record. + + ReturnValue - Supplies a value that is to be placed in the integer + function return register just before continuing execution. + +Return Value: + + None. + +--*/ + +{ + + CONTEXT ContextRecord1; + CONTEXT ContextRecord2; + ULONG ControlPc; +#if DBG + ULONG ControlPcHistory[PC_HISTORY_DEPTH]; + ULONG ControlPcHistoryIndex = 0; +#endif + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + FRAME_POINTERS EstablisherFrame; + ULONG ExceptionFlags; + EXCEPTION_RECORD ExceptionRecord1; +#if DBG + LONG FrameDepth = 0; +#endif + PRUNTIME_FUNCTION FunctionEntry; + ULONG HighLimit; + BOOLEAN InFunction; + ULONG LastPc; + ULONG LowLimit; + +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("\nRtlUnwindReturn(TargetFrame = %lx,, ReturnValue = %lx)\n", + TargetFrame, ReturnValue); + } +#endif + // + // Get current stack limits, capture the current context, virtually + // unwind to the caller of this routine, get the initial PC value, and + // set the unwind target address. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + RtlCaptureContext(&ContextRecord1); + ControlPc = (ULONG)ContextRecord1.IntRa; + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + LastPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + + ControlPc = LastPc; + ContextRecord1.Fir = 0; + + // + // If an exception record is not specified, then build a local exception + // record for use in calling exception handlers during the unwind operation. + // + + if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) { + ExceptionRecord = &ExceptionRecord1; + ExceptionRecord1.ExceptionCode = STATUS_UNWIND; + ExceptionRecord1.ExceptionRecord = NULL; + ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc; + ExceptionRecord1.NumberParameters = 0; + } + + // + // A target frame of the unwind is specified so a normal unwind is + // being performed. + // + + ExceptionFlags = EXCEPTION_UNWINDING; + + // + // Scan backward through the call frame hierarchy and call exception + // handlers until the target frame of the unwind is reached. + // + + do { +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwindReturn: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n", + FrameDepth, ContextRecord1.IntSp, ControlPc); + FrameDepth -= 1; + } +#endif + + // + // Lookup the function table entry using the point at which control + // left the procedure. + // + + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + + // + // If there is a function table entry for the routine, then copy the + // context record, virtually unwind to the caller of the current + // routine to obtain the virtual frame pointer of the establisher and + // check if there is an exception handler for the frame. + // + + if (FunctionEntry != NULL) { + RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT)); + LastPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + + // + // If the virtual frame pointer is not within the specified stack + // limits, the virtual frame pointer is unaligned, or the target + // frame is below the virtual frame and an exit unwind is not being + // performed, then raise the exception STATUS_BAD_STACK. Otherwise, + // check to determine if the current routine has an exception + // handler. + // + + if ((EstablisherFrame.Virtual < LowLimit) || + (EstablisherFrame.Virtual > HighLimit) || + ((ULONG)TargetFrame < EstablisherFrame.Virtual) || + ((EstablisherFrame.Virtual & 0xF) != 0)) { +#if DBG + DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n"); + DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n", + EstablisherFrame.Virtual, EstablisherFrame.Real); + DbgPrint(" TargetFrame = %08lx\n", TargetFrame); + if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && + ((ULONG)TargetFrame < EstablisherFrame.Virtual)) { + DbgPrint(" TargetFrame is below EstablisherFrame!\n"); + } + DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n", + (ULONG)ContextRecord2.IntSp); + DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n", + LowLimit, HighLimit); + DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n", + LastPc, ControlPc); + DbgPrint(" Now raising STATUS_BAD_STACK exception.\n"); +#endif + RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord); + + } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) { +#if DBG + if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) { + DbgPrint("RtlUnwindReturn: ExceptionHandler = %lx, HandlerData = %lx\n", + FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData); +} +#endif + + // + // The frame has an exception handler. + // + // The control PC, establisher frame pointer, the address + // of the function table entry, and the address of the + // context record are all stored in the dispatcher context. + // This information is used by the unwind linkage routine + // and can be used by the exception handler itself. + // + // A linkage routine written in assembler is used to actually + // call the actual exception handler. This is required by the + // exception handler that is associated with the linkage + // routine so it can have access to two sets of dispatcher + // context when it is called. + // + + DispatcherContext.ControlPc = ControlPc; + DispatcherContext.FunctionEntry = FunctionEntry; + DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual; + DispatcherContext.ContextRecord = &ContextRecord2; + + // + // Call the exception handler. + // + + do { + + // + // If the establisher frame is the target of the unwind + // operation, then set the target unwind flag. + // + + if ((ULONG)TargetFrame == EstablisherFrame.Virtual) { + ExceptionFlags |= EXCEPTION_TARGET_UNWIND; + } + + ExceptionRecord->ExceptionFlags = ExceptionFlags; + + // + // Set the specified return value in case the exception + // handler directly continues execution. + // + + ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue; +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwindReturn: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc); + } +#endif + Disposition = + RtlpExecuteHandlerForUnwind(ExceptionRecord, + EstablisherFrame.Virtual, + &ContextRecord2, + &DispatcherContext, + FunctionEntry->ExceptionHandler); +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) { + DbgPrint("RtlUnwindReturn: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition); + } +#endif + + // + // Clear target unwind and collided unwind flags. + // + + ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND | + EXCEPTION_TARGET_UNWIND); + + // + // Case on the handler disposition. + // + + switch (Disposition) { + + // + // The disposition is to continue the search. + // + // Continue the search for a handler or continue + // execution. + // + + case ExceptionContinueSearch : + break; + + // + // The disposition is collided unwind. + // + // Set the target of the current unwind to the context + // record of the previous unwind, virtually unwind to + // the caller of the old routine, and reexecute the + // exception handler from the collided frame with the + // collided unwind flag set in the exception record. + // + + case ExceptionCollidedUnwind : + ControlPc = DispatcherContext.ControlPc; + FunctionEntry = DispatcherContext.FunctionEntry; + RtlMoveMemory(&ContextRecord1, + DispatcherContext.ContextRecord, + sizeof(CONTEXT)); + + ContextRecord1.Fir = 0; + RtlMoveMemory(&ContextRecord2, + &ContextRecord1, + sizeof(CONTEXT)); + + ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; + LastPc = RtlVirtualUnwind(ControlPc, + FunctionEntry, + &ContextRecord1, + &InFunction, + &EstablisherFrame, + NULL); + break; + + // + // All other disposition values are invalid. + // + // Raise invalid disposition exception. + // + + default : + RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord); + } + + } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0); + } + + } else { + + // + // Set point at which control left the previous routine. + // + + LastPc = (ULONG)ContextRecord1.IntRa - 4; + + // + // If the next control PC is the same as the old control PC, then + // the function table is not correctly formed. + // + + if (LastPc == ControlPc) { +#if DBG + ULONG Count; + DbgPrint("\n****** Warning - malformed function table (unwind).\n"); + DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc); + for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) { + if (ControlPcHistoryIndex > 0) { + ControlPcHistoryIndex -= 1; + ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH]; + DbgPrint(", %08lx", ControlPc); + } + } + DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n"); + DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n"); +#endif + RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); + } + } + + // + // Set point at which control left the previous routine. + // + +#if DBG + ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc; + ControlPcHistoryIndex += 1; +#endif + ControlPc = LastPc; + + } while ((EstablisherFrame.Virtual < HighLimit) && + (EstablisherFrame.Virtual != (ULONG)TargetFrame)); + + // + // If the establisher stack pointer is equal to the target frame pointer, + // then continue execution at the point where the call to the target frame + // was made. Otherwise the target of the unwind did not exist and the + // debugger and subsystem are given a second chance to handle the unwind. + // + + if ((ULONG)ContextRecord1.IntSp == (ULONG)TargetFrame) { + ContextRecord1.IntV0 = (ULONGLONG)(LONG)ReturnValue; + + // + // Set the continuation address to the address after the point where + // control left the previous frame and entered the target frame. + // + + ContextRecord1.Fir = (ULONGLONG)(LONG)LastPc + 4; + +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("RtlUnwindReturn: finished unwinding, and calling RtlpRestoreContext\n"); + } +#endif + RtlpRestoreContext(&ContextRecord1); + + } else { +#if DBG + if (RtlDebugFlags & RTL_DBG_UNWIND) { + DbgPrint("RtlUnwindReturn: finished unwinding, but calling ZwRaiseException\n"); + } +#endif + ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE); + } +} diff --git a/private/ntos/rtl/alpha/xcptmisc.s b/private/ntos/rtl/alpha/xcptmisc.s new file mode 100644 index 000000000..45141b0d5 --- /dev/null +++ b/private/ntos/rtl/alpha/xcptmisc.s @@ -0,0 +1,501 @@ +// TITLE("Miscellaneous Exception Handling") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// Copyright (c) 1992 Digital Equipment Corporation +// +// Module Name: +// +// xcptmisc.s +// +// Abstract: +// +// This module implements miscellaneous routines that are required to +// support exception handling. Functions are provided to call an exception +// handler for an exception, call an exception handler for unwinding, call +// an exception filter, call a termination handler, and get the caller's +// stack limits. +// +// Author: +// +// David N. Cutler (davec) 12-Sep-1990 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +// Thomas Van Baak (tvb) 7-May-1992 +// +// Adapted for Alpha AXP. +// +//-- + +#include "ksalpha.h" + +// +// Define call frame for calling exception handlers. +// + + .struct 0 +CfRa: .space 8 // saved return address +CfA3: .space 8 // save area for argument a3 + .space 0 * 8 // 16-byte stack alignment +CfFrameLength: // length of stack frame + + SBTTL("Execute Handler for Exception") +//++ +// +// EXCEPTION_DISPOSITION +// RtlpExecuteHandlerForException ( +// IN PEXCEPTION_RECORD ExceptionRecord, +// IN ULONG EstablisherFrame, +// IN OUT PCONTEXT ContextRecord, +// IN OUT PDISPATCHER_CONTEXT DispatcherContext, +// IN PEXCEPTION_ROUTINE ExceptionRoutine +// ) +// +// Routine Description: +// +// This function allocates a call frame, stores the establisher frame +// pointer in the frame, establishes an exception handler, and then calls +// the specified exception handler as an exception handler. If a nested +// exception occurs, then the exception handler of this function is called +// and the establisher frame pointer is returned to the exception dispatcher +// via the dispatcher context parameter. If control is returned to this +// routine, then the frame is deallocated and the disposition status is +// returned to the exception dispatcher. +// +// Arguments: +// +// ExceptionRecord (a0) - Supplies a pointer to an exception record. +// +// EstablisherFrame (a1) - Supplies the frame pointer of the establisher +// of the exception handler that is to be called. +// +// ContextRecord (a2) - Supplies a pointer to a context record. +// +// DispatcherContext (a3) - Supplies a pointer to the dispatcher context +// record. +// +// ExceptionRoutine (a4) - Supplies a pointer to the exception handler that +// is to be called. +// +// Return Value: +// +// The disposition value returned by the specified exception handler is +// returned as the function value. +// +//-- + +// +// N.B. This function specifies its own private exception handler. +// + + EXCEPTION_HANDLER(RtlpExceptionHandler) + + NESTED_ENTRY(RtlpExecuteHandlerForException, CfFrameLength, zero) + + lda sp, -CfFrameLength(sp) // allocate stack frame + stq ra, CfRa(sp) // save return address + + PROLOGUE_END + +// +// Save the address of the dispatcher context record in our stack frame so +// that our own exception handler (not the one we're calling) can retrieve it. +// + + stq a3, CfA3(sp) // save address of dispatcher context + +// +// Now call the exception handler and return its return value as ours. +// + + bic a4, 3, a4 // clear low-order bits (IEEE mode) + jsr ra, (a4) // call exception handler + + ldq ra, CfRa(sp) // restore return address + lda sp, CfFrameLength(sp) // deallocate stack frame + ret zero, (ra) // return + + .end RtlpExecuteHandlerForException + + SBTTL("Local Exception Handler") +//++ +// +// EXCEPTION_DISPOSITION +// RtlpExceptionHandler ( +// IN PEXCEPTION_RECORD ExceptionRecord, +// IN ULONG EstablisherFrame, +// IN OUT PCONTEXT ContextRecord, +// IN OUT PDISPATCHER_CONTEXT DispatcherContext +// ) +// +// Routine Description: +// +// This function is called when a nested exception occurs. Its function +// is to retrieve the establisher frame pointer from its establisher's +// call frame, store this information in the dispatcher context record, +// and return a disposition value of nested exception. +// +// Arguments: +// +// ExceptionRecord (a0) - Supplies a pointer to an exception record. +// +// EstablisherFrame (a1) - Supplies the frame pointer of the establisher +// of this exception handler. +// +// ContextRecord (a2) - Supplies a pointer to a context record. +// +// DispatcherContext (a3) - Supplies a pointer to the dispatcher context +// record. +// +// Return Value: +// +// A disposition value ExceptionNestedException is returned if an unwind +// is not in progress. Otherwise a value of ExceptionContinueSearch is +// returned. +// +//-- + + LEAF_ENTRY(RtlpExceptionHandler) + + ldl t0, ErExceptionFlags(a0) // get exception flags + and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress + bne t0, 10f // if neq, unwind in progress + +// +// Unwind is not in progress - return nested exception disposition. +// + +// +// Convert the given establisher virtual frame pointer (a1) to a real frame +// pointer (the value of a1 minus CfFrameLength) and retrieve the pointer to +// the dispatcher context that earlier was stored in the stack frame. +// + + ldq t0, -CfFrameLength + CfA3(a1) // get dispatcher context address + + ldl t1, DcEstablisherFrame(t0) // copy the establisher frame pointer + stl t1, DcEstablisherFrame(a3) // to current dispatcher context + + ldil v0, ExceptionNestedException // set disposition value + ret zero, (ra) // return + +// +// Unwind is in progress - return continue search disposition. +// + +10: ldil v0, ExceptionContinueSearch // set disposition value + ret zero, (ra) // return + + .end RtlpExceptionHandler + + SBTTL("Execute Handler for Unwind") +//++ +// +// EXCEPTION_DISPOSITION +// RtlpExecuteHandlerForUnwind ( +// IN PEXCEPTION_RECORD ExceptionRecord, +// IN PVOID EstablisherFrame, +// IN OUT PCONTEXT ContextRecord, +// IN OUT PVOID DispatcherContext, +// IN PEXCEPTION_ROUTINE ExceptionRoutine +// ) +// +// Routine Description: +// +// This function allocates a call frame, stores the establisher frame +// pointer and the context record address in the frame, establishes an +// exception handler, and then calls the specified exception handler as +// an unwind handler. If a collided unwind occurs, then the exception +// handler of this function is called and the establisher frame pointer +// and context record address are returned to the unwind dispatcher via +// the dispatcher context parameter. If control is returned to this routine, +// then the frame is deallocated and the disposition status is returned to +// the unwind dispatcher. +// +// Arguments: +// +// ExceptionRecord (a0) - Supplies a pointer to an exception record. +// +// EstablisherFrame (a1) - Supplies the frame pointer of the establisher +// of the exception handler that is to be called. +// +// ContextRecord (a2) - Supplies a pointer to a context record. +// +// DispatcherContext (a3) - Supplies a pointer to the dispatcher context +// record. +// +// ExceptionRoutine (a4) - Supplies a pointer to the exception handler that +// is to be called. +// +// Return Value: +// +// The disposition value returned by the specified exception handler is +// returned as the function value. +// +//-- + +// +// N.B. This function specifies its own private exception handler. +// + + EXCEPTION_HANDLER(RtlpUnwindHandler) + + NESTED_ENTRY(RtlpExecuteHandlerForUnwind, CfFrameLength, zero) + + lda sp, -CfFrameLength(sp) // allocate stack frame + stq ra, CfRa(sp) // save return address + + PROLOGUE_END + +// +// Save the address of the dispatcher context record in our stack frame so +// that our own exception handler (not the one we're calling) can retrieve it. +// + + stq a3, CfA3(sp) // save address of dispatcher context + +// +// Now call the exception handler and return its return value as our return +// value. +// + + bic a4, 3, a4 // clear low-order bits (IEEE mode) + jsr ra, (a4) // call exception handler + + ldq ra, CfRa(sp) // restore return address + lda sp, CfFrameLength(sp) // deallocate stack frame + ret zero, (ra) // return + + .end RtlpExecuteHandlerForUnwind + + SBTTL("Local Unwind Handler") +//++ +// +// EXCEPTION_DISPOSITION +// RtlpUnwindHandler ( +// IN PEXCEPTION_RECORD ExceptionRecord, +// IN PVOID EstablisherFrame, +// IN OUT PCONTEXT ContextRecord, +// IN OUT PVOID DispatcherContext +// ) +// +// Routine Description: +// +// This function is called when a collided unwind occurs. Its function +// is to retrieve the establisher dispatcher context, copy it to the +// current dispatcher context, and return a disposition value of nested +// unwind. +// +// Arguments: +// +// ExceptionRecord (a0) - Supplies a pointer to an exception record. +// +// EstablisherFrame (a1) - Supplies the frame pointer of the establisher +// of this exception handler. +// +// ContextRecord (a2) - Supplies a pointer to a context record. +// +// DispatcherContext (a3) - Supplies a pointer to the dispatcher context +// record. +// +// Return Value: +// +// A disposition value ExceptionCollidedUnwind is returned if an unwind is +// in progress. Otherwise, a value of ExceptionContinueSearch is returned. +// +//-- + + LEAF_ENTRY(RtlpUnwindHandler) + + ldl t0, ErExceptionFlags(a0) // get exception flags + and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress + beq t0, 10f // if eq, unwind not in progress + +// +// Unwind is in progress - return collided unwind disposition. +// + +// +// Convert the given establisher virtual frame pointer (a1) to a real frame +// pointer (the value of a1 minus CfFrameLength) and retrieve the pointer to +// the dispatcher context that earlier was stored in the stack frame. +// + + ldq t0, -CfFrameLength + CfA3(a1) // get dispatcher context address + + ldl t1, DcControlPc(t0) // copy the entire dispatcher + ldl t2, DcFunctionEntry(t0) // context of the establisher + ldl t3, DcEstablisherFrame(t0) // frame... + ldl t4, DcContextRecord(t0) // + + stl t1, DcControlPc(a3) // to the current dispatcher + stl t2, DcFunctionEntry(a3) // context (it's four words + stl t3, DcEstablisherFrame(a3) // long). + stl t4, DcContextRecord(a3) // + + ldil v0, ExceptionCollidedUnwind // set disposition value + ret zero, (ra) // return + +// +// Unwind is not in progress - return continue search disposition. +// + +10: ldil v0, ExceptionContinueSearch // set disposition value + ret zero, (ra) // return + + .end RtlpUnwindHandler + + SBTTL("Execute Exception Filter") +//++ +// +// ULONG +// RtlpExecuteExceptionFilter ( +// PEXCEPTION_POINTERS ExceptionPointers, +// EXCEPTION_FILTER ExceptionFilter, +// ULONG EstablisherFrame +// ) +// +// Routine Description: +// +// This function sets the static link and transfers control to the specified +// exception filter routine. +// +// Arguments: +// +// ExceptionPointers (a0) - Supplies a pointer to the exception pointers +// structure. +// +// ExceptionFilter (a1) - Supplies the address of the exception filter +// routine. +// +// EstablisherFrame (a2) - Supplies the establisher frame pointer. +// +// Return Value: +// +// The value returned by the exception filter routine. +// +//-- + + LEAF_ENTRY(RtlpExecuteExceptionFilter) + +// +// The protocol for calling exception filters used by the acc C-compiler is +// that the uplevel frame pointer is passed in register v0 and the pointer +// to the exception pointers structure is passed in register a0. The Gem +// compiler expects the static link in t0. Here we do both. +// + + mov a2, v0 // set static link + mov a2, t0 // set alternate static link + jmp zero, (a1) // transfer control to exception filter + + .end RtlpExecuteExceptionFilter + + SBTTL("Execute Termination Handler") +//++ +// +// VOID +// RtlpExecuteTerminationHandler ( +// BOOLEAN AbnormalTermination, +// TERMINATION_HANDLER TerminationHandler, +// ULONG EstablisherFrame +// ) +// +// Routine Description: +// +// This function sets the static link and transfers control to the specified +// termination handler routine. +// +// Arguments: +// +// AbnormalTermination (a0) - Supplies a boolean value that determines +// whether the termination is abnormal. +// +// TerminationHandler (a1) - Supplies the address of the termination handler +// routine. +// +// EstablisherFrame (a2) - Supplies the establisher frame pointer. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlpExecuteTerminationHandler) + +// +// The protocol for calling termination handlers used by the acc C-compiler +// is that the uplevel frame pointer is passed in register v0 and the boolean +// abnormal termination value is passed in register a0. The Gem compiler +// expects the static link in t0. Here we do both. +// + + mov a2, v0 // set static link + mov a2, t0 // set alternate static link + jmp zero, (a1) // transfer control to termination handler + + .end RtlpExecuteTerminationHandler + + SBTTL("Get Stack Limits") +//++ +// +// VOID +// RtlpGetStackLimits ( +// OUT PULONG LowLimit, +// OUT PULONG HighLimit +// ) +// +// Routine Description: +// +// This function returns the current stack limits based on the current +// processor mode. +// +// Arguments: +// +// LowLimit (a0) - Supplies a pointer to a variable that is to receive +// the low limit of the stack. +// +// HighLimit (a1) - Supplies a pointer to a variable that is to receive +// the high limit of the stack. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlpGetStackLimits) +#if defined(NTOS_KERNEL_RUNTIME) + +// +// Current mode is kernel - compute stack limits. +// + + GET_INITIAL_KERNEL_STACK // get initial kernel stack in v0 + + mov v0, t1 // copy high limit of kernel stack + GET_CURRENT_THREAD // get current thread in v0 + ldl t2, ThStackLimit(v0) // get low limit of kernel stack +#else + +// +// Current mode is user - get stack limits from the TEB. +// + + GET_THREAD_ENVIRONMENT_BLOCK // get address of TEB in v0 + + ldl t1, TeStackBase(v0) // get high limit of user stack + ldl t2, TeStackLimit(v0) // get low limit of user stack +#endif + + stl t2, 0(a0) // store low stack limit + stl t1, 0(a1) // store high stack limit + ret zero, (ra) // return + + .end RtlpGetStackLimits -- cgit v1.2.3