diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/rtl/mips | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/rtl/mips')
-rw-r--r-- | private/ntos/rtl/mips/chandler.c | 220 | ||||
-rw-r--r-- | private/ntos/rtl/mips/chkstk.s | 88 | ||||
-rw-r--r-- | private/ntos/rtl/mips/context.c | 296 | ||||
-rw-r--r-- | private/ntos/rtl/mips/debugstb.s | 271 | ||||
-rw-r--r-- | private/ntos/rtl/mips/exdsptch.c | 2072 | ||||
-rw-r--r-- | private/ntos/rtl/mips/getcalr.c | 179 | ||||
-rw-r--r-- | private/ntos/rtl/mips/largeint.s | 947 | ||||
-rw-r--r-- | private/ntos/rtl/mips/lzntmips.s | 1325 | ||||
-rw-r--r-- | private/ntos/rtl/mips/nlssup.c | 109 | ||||
-rw-r--r-- | private/ntos/rtl/mips/ntrtlmip.h | 41 | ||||
-rw-r--r-- | private/ntos/rtl/mips/stringsp.c | 122 | ||||
-rw-r--r-- | private/ntos/rtl/mips/trampoln.s | 522 | ||||
-rw-r--r-- | private/ntos/rtl/mips/xcptmisc.s | 356 | ||||
-rw-r--r-- | private/ntos/rtl/mips/xxcaptur.s | 314 | ||||
-rw-r--r-- | private/ntos/rtl/mips/xxmvmem.s | 1440 |
15 files changed, 8302 insertions, 0 deletions
diff --git a/private/ntos/rtl/mips/chandler.c b/private/ntos/rtl/mips/chandler.c new file mode 100644 index 000000000..9c84d6218 --- /dev/null +++ b/private/ntos/rtl/mips/chandler.c @@ -0,0 +1,220 @@ +/*++ + +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: + +--*/ + +#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 + ); + +VOID +__C_ExecuteTerminationHandler ( + BOOLEAN AbnormalTermination, + TERMINATION_HANDLER TerminationHandler, + ULONG EstablisherFrame + ); + +EXCEPTION_DISPOSITION +__C_specific_handler ( + IN PEXCEPTION_RECORD ExceptionRecord, + IN PVOID EstablisherFrame, + IN OUT PCONTEXT ContextRecord, + IN OUT PDISPATCHER_CONTEXT DispatcherContext + ) + +/*++ + +Routine Description: + + This function scans the scope tables associated with the specified + procedure and calls exception and termination handlers as necessary. + +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 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); + + // + // 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. + // are called. + // + + if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) { + + // + // Scan the scope table and call the appropriate exception filter + // routines. + // + + ExceptionPointers.ExceptionRecord = ExceptionRecord; + ExceptionPointers.ContextRecord = ContextRecord; + 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. Otherwise, + // continue the search for an exception filter. + // + + if (Value < 0) { + return ExceptionContinueExecution; + + } else if (Value > 0) { + 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 = 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 termination handlers. + // + // 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. + // + + if (ScopeTable->ScopeRecord[Index].JumpTarget != 0) { + if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) { + break; + } + + } else { + DispatcherContext->ControlPc = + ScopeTable->ScopeRecord[Index].EndAddress + 4; + TerminationHandler = + (TERMINATION_HANDLER)ScopeTable->ScopeRecord[Index].HandlerAddress; + __C_ExecuteTerminationHandler(TRUE, + TerminationHandler, + (ULONG)EstablisherFrame); + } + } + } + } + } + + // + // Continue search for exception or termination handlers. + // + + return ExceptionContinueSearch; +} diff --git a/private/ntos/rtl/mips/chkstk.s b/private/ntos/rtl/mips/chkstk.s new file mode 100644 index 000000000..6de5018b7 --- /dev/null +++ b/private/ntos/rtl/mips/chkstk.s @@ -0,0 +1,88 @@ +// TITLE("Runtime Stack Checking") +//++ +// +// Copyright (c) 1991 Microsoft Corporation +// +// Module Name: +// +// chkstk.s +// +// Abstract: +// +// This module implements runtime stack checking. +// +// Author: +// +// David N. Cutler (davec) 14-Mar-1991 +// +// Environment: +// +// User mode. +// +// Revision History: +// +//-- + +#include "ksmips.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 attempt to expand the stack. If the +// attempt succeeds, then another page is committed. Otherwise, a stack +// overflow exception is raised. It is the responsiblity of the caller to +// handle this exception. +// +// N.B. This routine is called using a calling sequence that assumes that +// all registers are preserved. +// +// Arguments: +// +// Allocation (t8) - Supplies the size of the allocation on the stack. +// +// Return Value: +// +// None. +// +//-- + + NESTED_ENTRY(_RtlCheckStack, 0, ra) + + sw t7,0(sp) // save temporary register + sw t8,4(sp) // save allocation size + sw t9,8(sp) // save temporary register + + PROLOGUE_END + + .set noreorder + .set noat + li t9,UsPcr // get address of user PCR + lw t9,PcTeb(t9) // get address of environment block + subu t8,sp,t8 // compute new bottom of stack + lw t9,TeStackLimit(t9) // get low stack address + sltu t7,t8,t9 // new stack address within limits? + beq zero,t7,20f // if eq, stack within limits + li t7,~(PAGE_SIZE - 1) // set address mask + and t8,t8,t7 // round down new stack address +10: subu t9,t9,PAGE_SIZE // compute next address to check + bne t8,t9,10b // if ne, more pages to probe + sw zero,0(t9) // check stack address +20: lw t7,0(sp) // restore temporary register + lw t8,4(sp) // restore allocation size + j ra // return + lw t9,8(sp) // restore temporary register + .set at + .set reorder + + .end _RtlCheckStack diff --git a/private/ntos/rtl/mips/context.c b/private/ntos/rtl/mips/context.c new file mode 100644 index 000000000..ef9b521c9 --- /dev/null +++ b/private/ntos/rtl/mips/context.c @@ -0,0 +1,296 @@ +/*++ + +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. + +--*/ + +#include <ntos.h> + +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: + + Context - Supplies a pointer to a context record that is to be initialized. + + 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 & 0x7) != 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. + // + + Context->XIntZero = 0; + Context->XIntAt = 1; + Context->XIntV0 = 2; + Context->XIntV1 = 3; + Context->XIntA0 = 4; + Context->XIntA1 = 5; + Context->XIntA2 = 6; + Context->XIntA3 = 7; + Context->XIntT0 = 8; + Context->XIntT1 = 9; + Context->XIntT2 = 10; + Context->XIntT3 = 11; + Context->XIntT4 = 12; + Context->XIntT5 = 13; + Context->XIntT6 = 14; + Context->XIntT7 = 15; + Context->XIntS0 = 16; + Context->XIntS1 = 17; + Context->XIntS2 = 18; + Context->XIntS3 = 19; + Context->XIntS4 = 20; + Context->XIntS5 = 21; + Context->XIntS6 = 22; + Context->XIntS7 = 23; + Context->XIntT8 = 24; + Context->XIntT9 = 25; + Context->XIntS8 = 30; + Context->XIntLo = 0; + Context->XIntHi = 0; + + // + // Initialize the floating point registers to contain zero in their upper + // half and the integer value of their register number in the lower half. + // + + Context->FltF0 = 0; + Context->FltF1 = 0; + Context->FltF2 = 2; + Context->FltF3 = 0; + Context->FltF4 = 4; + Context->FltF5 = 0; + Context->FltF6 = 6; + Context->FltF7 = 0; + Context->FltF8 = 8; + Context->FltF9 = 0; + Context->FltF10 = 10; + Context->FltF11 = 0; + Context->FltF12 = 12; + Context->FltF13 = 0; + Context->FltF14 = 14; + Context->FltF15 = 0; + Context->FltF16 = 16; + Context->FltF17 = 0; + Context->FltF18 = 18; + Context->FltF19 = 0; + Context->FltF20 = 20; + Context->FltF21 = 0; + Context->FltF22 = 22; + Context->FltF23 = 0; + Context->FltF24 = 24; + Context->FltF25 = 0; + Context->FltF26 = 26; + Context->FltF27 = 0; + Context->FltF28 = 28; + Context->FltF29 = 0; + Context->FltF30 = 30; + Context->FltF31 = 0; + Context->Fsr = 0; + + // + // Initialize the control registers. + // + // N.B. The register gp is estabished at thread startup by the loader. + // + + Context->XIntGp = 0; + Context->XIntSp = (LONG)InitialSp; + Context->XIntRa = 1; + Context->Fir = (ULONG)InitialPc; + Context->Psr = 0; + Context->ContextFlags = CONTEXT_FULL; + + // + // Set the initial context of the thread in a machine specific way. + // + + Context->XIntA0 = (LONG)Parameter; + Context->XIntSp -= KTRAP_FRAME_ARGUMENTS; +} + +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 - s7). + +Arguments: + + Process - Supplies an open handle to the target process. + + Thread - Supplies an open handle to the target thread within the target + process. + + CallSize - 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 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 NewSp; + + if (ArgumentCount > 8) { + 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 cuurent 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.XIntV0 = (LONG)STATUS_ALERTED; + } + + // + // Pass the parameters to the other thread via the non-volatile registers + // s0 - s7. The context record is passed on the stack of the target thread. + // + + NewSp = (ULONG)(Context.XIntSp - sizeof(CONTEXT)); + Status = NtWriteVirtualMemory(Process, + (PVOID)NewSp, + &Context, + sizeof(CONTEXT), + NULL); + + if (NT_SUCCESS(Status) == FALSE) { + if (AlreadySuspended == FALSE) { + NtResumeThread(Thread, NULL); + } + + return Status; + } + + Context.XIntSp = (LONG)NewSp; + if (PassContext) { + Context.XIntS0 = (LONG)NewSp; + RtlMoveMemory(&Context.XIntS1, Arguments, ArgumentCount * sizeof(ULONG)); + + } else { + RtlMoveMemory(&Context.XIntS0, Arguments, ArgumentCount * sizeof(ULONG)); + } + + // + // Set the address of the target code into FIR and set the thread context + // to cause the target procedure to be executed. + // + + Context.Fir = (ULONG)CallSite;; + Status = NtSetContextThread(Thread, &Context); + if (AlreadySuspended == FALSE) { + NtResumeThread(Thread, NULL); + } + + return Status; +} diff --git a/private/ntos/rtl/mips/debugstb.s b/private/ntos/rtl/mips/debugstb.s new file mode 100644 index 000000000..4a305eaa1 --- /dev/null +++ b/private/ntos/rtl/mips/debugstb.s @@ -0,0 +1,271 @@ +// 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 trap r31,r29,r0 instruction with a special value in +// R31. The simulator decodes this trap instruction and dispatches to the +// correct piece of code in the simulator based on the value in R31. See +// the simscal.c source file in the simulator source directory. +// +// Author: +// +// Steven R. Wood (stevewo) 3-Aug-1989 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +//-- + +#include "ksmips.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_BREAKPOINT + j ra + + .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: +// +// None. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(DbgBreakPointWithStatus) + + ALTERNATE_ENTRY(RtlpBreakWithStatusInstruction) + break DEBUG_STOP_BREAKPOINT + j ra + + .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 DEBUG_STOP_BREAKPOINT + j ra + + .end DbgUserBreakPoint + +//++ +// +// 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) + + lhu a3,StrMaximumLength(a1) // set maximum length of input string + lw a2,StrBuffer(a1) // set address of input string + lhu a1,StrLength(a0) // set length of output string + lw a0,StrBuffer(a0) // set address of output string + break DEBUG_PROMPT_BREAKPOINT // execute a debug prompt breakpoint + j ra // return + + .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_BREAKPOINT + j 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_BREAKPOINT + j ra + + .end DebugUnLoadImageSymbols + +#endif + +//++ +// +// 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) + + lhu a1,StrLength(a0) // set length of output string + lw a0,StrBuffer(a0) // set address of output string + break DEBUG_PRINT_BREAKPOINT // execute a debug print breakpoint + j ra // return + + .end DebugPrint + +#endif diff --git a/private/ntos/rtl/mips/exdsptch.c b/private/ntos/rtl/mips/exdsptch.c new file mode 100644 index 000000000..9c9b4d3b6 --- /dev/null +++ b/private/ntos/rtl/mips/exdsptch.c @@ -0,0 +1,2072 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + exdsptch.c + +Abstract: + + This module implements the dispatching of exception and the unwinding of + procedure call frames. + +Author: + + David N. Cutler (davec) 11-Sep-1990 + +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); \ + } + +// +// Define stack register and zero register numbers. +// + +#define RA 0x1f // integer register 31 +#define SP 0x1d // integer register 29 +#define ZERO 0x0 // integer register 0 + +// +// Define saved register masks. +// + +#define SAVED_FLOATING_MASK 0xfff00000 // saved floating registers +#define SAVED_INTEGER_MASK 0xf3ffff02 // saved integer registers + +// +// Define private function prototypes. +// + +VOID +RtlpRestoreContext ( + IN PCONTEXT Context, + IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL + ); + +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, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ); + +ULONG +RtlpVirtualUnwind32 ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PULONG EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ); + + +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 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. 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; + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + ULONG EstablisherFrame; + ULONG ExceptionFlags; + PRUNTIME_FUNCTION FunctionEntry; + ULONG Index; + BOOLEAN InFunction; + ULONG HighLimit; + 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. + // + + RtlpGetStackLimits(&LowLimit, &HighLimit); + RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT)); + ControlPc = ContextRecord1.Fir; + ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE; + NestedFrame = 0; + + // + // 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 { + + // + // 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 | 1, + 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 < LowLimit) || (EstablisherFrame > HighLimit) || + ((EstablisherFrame & 0x7) != 0)) { + ExceptionFlags |= EXCEPTION_STACK_INVALID; + break; + + } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { + + // + // 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; + DispatcherContext.ContextRecord = ContextRecord; + ExceptionRecord->ExceptionFlags = ExceptionFlags; + + // + // If requested log exception. + // + + if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) { + Index = RtlpLogExceptionHandler(ExceptionRecord, + ContextRecord, + ControlPc, + FunctionEntry, + sizeof(RUNTIME_FUNCTION)); + } + + Disposition = + RtlpExecuteHandlerForException(ExceptionRecord, + EstablisherFrame, + ContextRecord, + &DispatcherContext, + FunctionEntry->ExceptionHandler); + + 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) { + 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 { + 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.XIntRa - 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) { + break; + } + } + + // + // Set point at which control left the previous routine. + // + + ControlPc = NextPc; + } while ((ULONG)ContextRecord1.XIntSp < HighLimit); + + // + // Set final exception flags and return exception not handled. + // + + ExceptionRecord->ExceptionFlags = ExceptionFlags; + 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; + USHORT i; + + // + // Search for the image that includes the specified PC value. + // + + ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase); + + // + // 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 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 { + + // + // The capability exists for more than one function entry + // to map to the same function. This permits a function to + // have discontiguous code segments described by separate + // function table entries. If the ending prologue address + // is not within the limits of the begining and ending + // address of the function able entry, then the prologue + // ending address is the address of a function table entry + // that accurately describes the ending prologue address. + // + + if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) || + (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) { + FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress; + } + + return FunctionEntry; + } + } + } + } + + // + // A function table entry for the specified PC was not found. + // + + 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. + +--*/ + +{ + + 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; + ULONG 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.XIntRa - 4); + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + NextPc = RtlVirtualUnwind(ControlPc | 1, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + ContextRecord.Fir = 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. + +--*/ + +{ + + 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; + ULONG 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.XIntRa - 4); + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + NextPc = RtlVirtualUnwind(ControlPc | 1, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + ContextRecord.Fir = 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. + +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. + + ContextRecord - Supplies a pointer to a context record that can be used + to store context druing the unwind operation. + +Return Value: + + None. + +--*/ + +{ + + ULONG ControlPc; + DISPATCHER_CONTEXT DispatcherContext; + EXCEPTION_DISPOSITION Disposition; + ULONG EstablisherFrame; + ULONG ExceptionFlags; + EXCEPTION_RECORD ExceptionRecord1; + PRUNTIME_FUNCTION FunctionEntry; + BOOLEAN InFunction; + ULONG HighLimit; + ULONG LowLimit; + ULONG NextPc; + + // + // 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->XIntRa - 4); + FunctionEntry = RtlLookupFunctionEntry(ControlPc); + NextPc = RtlVirtualUnwind(ControlPc | 1, + FunctionEntry, + ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + ControlPc = NextPc; + ContextRecord->Fir = (ULONG)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 { + + // + // 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, + &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 < LowLimit) || (EstablisherFrame > HighLimit) || + ((ARGUMENT_PRESENT(TargetFrame) != FALSE) && + ((ULONG)TargetFrame < EstablisherFrame)) || + ((EstablisherFrame & 0x7) != 0)) { + RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord); + + } else if ((FunctionEntry->ExceptionHandler != NULL) && InFunction) { + + // + // 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; + 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) { + ExceptionFlags |= EXCEPTION_TARGET_UNWIND; + } + + ExceptionRecord->ExceptionFlags = ExceptionFlags; + + // + // Set the specified return value in case the exception + // handler directly continues execution. + // + + ContextRecord->XIntV0 = (LONG)ReturnValue; + Disposition = + RtlpExecuteHandlerForUnwind(ExceptionRecord, + EstablisherFrame, + ContextRecord, + &DispatcherContext, + FunctionEntry->ExceptionHandler); + + // + // 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 != (ULONG)TargetFrame) { + NextPc = RtlVirtualUnwind(ControlPc | 1, + 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 = (ULONG)TargetIp; + ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND; + EstablisherFrame = 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 { + + // + // If the target frame has not been reached, then virtually unwind to the + // caller of the current routine and update the context record. + // + + if (EstablisherFrame != (ULONG)TargetFrame) { + NextPc = RtlVirtualUnwind(ControlPc | 1, + FunctionEntry, + ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + } + } + + } else { + + // + // Set point at which control left the previous routine. + // + + NextPc = (ULONG)(ContextRecord->XIntRa - 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) { + RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); + } + } + + // + // Set point at which control left the previous routine. + // + // N.B. Make sure the address is in the delay slot of the jal + // to prevent the boundary condition of the return address + // being at the front of a try body. + // + + ControlPc = NextPc; + + } while ((EstablisherFrame < HighLimit) && + (EstablisherFrame != (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 == (ULONG)TargetFrame) { + ContextRecord->XIntV0 = (LONG)ReturnValue; + RtlpRestoreContext(ContextRecord, ExceptionRecord); + + } else { + ZwRaiseException(ExceptionRecord, ContextRecord, FALSE); + } +} + +ULONG +RtlVirtualUnwind ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PULONG EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ) + +/*++ + +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 routine handles 64-bit context records. + +Arguments: + + ControlPc - Supplies the address where control left the specified + function. + + N.B. The low order bit of this argument is used to denote the + context record type. If the low order bit is clear, then + the context record contains 32-bit information. Otherwise, + it contains 64-bit information. + + 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. + +--*/ + +{ + + ULONG Address; + ULONG DecrementOffset; + ULONG DecrementRegister; + PULONG FloatingRegister; + ULONG Function; + MIPS_INSTRUCTION Instruction; + PULONGLONG IntegerRegister; + ULONG NextPc; + LONG Offset; + ULONG Opcode; + ULONG Rd; + BOOLEAN RestoredRa; + BOOLEAN RestoredSp; + ULONG Rs; + ULONG Rt; + + // + // If the low order bit of the control PC is clear, then the context + // record format is 32-bit. Otherwise, the context record format is + // 64-bits. + // + + if ((ControlPc & 1) == 0) { + return RtlpVirtualUnwind32(ControlPc, + FunctionEntry, + ContextRecord, + InFunction, + EstablisherFrame, + ContextPointers); + + } else { + + // + // Set the base address of the integer and floating register arrays. + // + + FloatingRegister = &ContextRecord->FltF0; + IntegerRegister = &ContextRecord->XIntZero; + + // + // If the instruction at the point where control left the specified + // function is a return, then any saved registers have been restored + // with the possible exception of the stack pointer and the control + // PC is not considered to be in the function (i.e., an epilogue). + // + + ControlPc &= ~1; + if (*((PULONG)ControlPc) == JUMP_RA) { + *InFunction = FALSE; + Instruction.Long = *((PULONG)ControlPc + 1); + Opcode = Instruction.i_format.Opcode; + Offset = Instruction.i_format.Simmediate; + Rd = Instruction.r_format.Rd; + Rs = Instruction.i_format.Rs; + Rt = Instruction.i_format.Rt; + Function = Instruction.r_format.Function; + + // + // If the opcode is an add immediate unsigned op and both the + // source and destination registers are SP, then add the signed + // offset value to SP. Otherwise, if the opcode is a special op, + // the operation is an add unsigned, and the source and destination + // registers are both SP, then add the register specified by Rd to + // SP. + // + + if ((Opcode == ADDIU_OP) && (Rt == SP) && (Rs == SP)) { + IntegerRegister[SP] += Offset; + + } else if ((Opcode == SPEC_OP) && (Function == ADDU_OP) && + (Rd == SP) && (Rs == SP)) { + IntegerRegister[SP] += IntegerRegister[Rt]; + } + + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + return (ULONG)ContextRecord->XIntRa; + } + + // + // If the address where control left the specified function is outside + // the limits 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., it is within the prologue). + // + + if ((ControlPc < FunctionEntry->BeginAddress) || + (ControlPc >= FunctionEntry->PrologEndAddress)) { + *InFunction = TRUE; + ControlPc = FunctionEntry->PrologEndAddress; + + } else { + *InFunction = FALSE; + } + + // + // Scan backward through the prologue and reload callee registers that + // were stored. + // + + DecrementRegister = 0; + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + NextPc = (ULONG)(ContextRecord->XIntRa - 4); + RestoredRa = FALSE; + RestoredSp = FALSE; + while (ControlPc > FunctionEntry->BeginAddress) { + + // + // Get instruction value, decode fields, case of opcode value, and + // reverse store operations. + // + + ControlPc -= 4; + Instruction.Long = *((PULONG)ControlPc); + Opcode = Instruction.i_format.Opcode; + Offset = Instruction.i_format.Simmediate; + Rd = Instruction.r_format.Rd; + Rs = Instruction.i_format.Rs; + Rt = Instruction.i_format.Rt; + Address = (ULONG)(Offset + IntegerRegister[Rs]); + if (Opcode == SW_OP) { + + // + // Store word. + // + // If the base register is SP and the source register is an + // integer register, then reload the register value. + // + + if (Rs == SP) { + IntegerRegister[Rt] = *((PLONG)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, this + // is an interrupt or exception and the return PC should be + // biased by 4. Otherwise, if the destination register is + // SP and this is the first time that SP is being restored, + // then set the establisher frame pointer. + // + + if (Rt == RA) { + if (RestoredRa == FALSE) { + NextPc = (ULONG)(ContextRecord->XIntRa - 4); + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + + } else if (Rt == SP) { + if (RestoredSp == FALSE) { + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + 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->XIntegerContext[Rt] = (PULONGLONG)Address; + } + } + + } else if (Opcode == SD_OP) { + + // + // Store double. + // + // If the base register is SP and the source register is an + // integer register, then reload the register value. + // + + if (Rs == SP) { + IntegerRegister[Rt] = *((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, this + // is an interrupt or exception and the return PC should be + // biased by 4. Otherwise, if the destination register is + // SP and this is the first time that SP is being restored, + // then set the establisher frame pointer. + // + + if (Rt == RA) { + if (RestoredRa == FALSE) { + NextPc = (ULONG)(ContextRecord->XIntRa - 4); + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + + } else if (Rt == SP) { + if (RestoredSp == FALSE) { + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + RestoredSp = TRUE; + } + } + + // + // If a context pointer record is specified, then record + // the address where the destination register contents + // are stored. + // + // N.B. The low order bit of the address is set to indicate + // a store double operation. + // + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->XIntegerContext[Rt] = (PLONGLONG)((ULONG)Address | 1); + } + } + + } else if (Opcode == SWC1_OP) { + + // + // Store word coprocessor 1. + // + // If the base register is SP and the source register is a + // floating register, then reload the register value. + // + + if (Rs == SP) { + FloatingRegister[Rt] = *((PULONG)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[Rt] = (PULONG)Address; + } + } + + } else if (Opcode == SDC1_OP) { + + // + // Store double coprocessor 1. + // + // If the base register is SP and the source register is a + // floating register, then reload the register and the next + // register values. + // + + if (Rs == SP) { + FloatingRegister[Rt] = *((PULONG)Address); + FloatingRegister[Rt + 1] = *((PULONG)(Address + 4)); + + // + // If a context pointer record is specified, then record + // the address where the destination registers contents + // are stored. + // + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->FloatingContext[Rt] = (PULONG)Address; + ContextPointers->FloatingContext[Rt + 1] = (PULONG)(Address + 4); + } + } + + } else if (Opcode == ADDIU_OP) { + + // + // Add immediate unsigned. + // + // If both the source and destination registers are SP, then + // a standard stack allocation was performed and the signed + // displacement value should be subtracted from SP. Otherwise, + // if the destination register is the decrement register and + // the source register is zero, then add the decrement value + // to SP. + // + + if ((Rs == SP) && (Rt == SP)) { + IntegerRegister[SP] -= Offset; + if (RestoredSp == FALSE) { + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + RestoredSp = TRUE; + } + + } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { + IntegerRegister[SP] += Offset; + if (RestoredSp == FALSE) { + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + RestoredSp = TRUE; + } + } + + } else if (Opcode == ORI_OP) { + + // + // Or immediate. + // + // If both the destination and source registers are the decrement + // register, then save the decrement value. Otherwise, if the + // destination register is the decrement register and the source + // register is zero, then add the decrement value to SP. + // + + if ((Rs == DecrementRegister) && (Rt == DecrementRegister)) { + DecrementOffset = (Offset & 0xffff); + + } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { + IntegerRegister[SP] += (Offset & 0xffff); + if (RestoredSp == FALSE) { + *EstablisherFrame = (ULONG)ContextRecord->XIntSp; + RestoredSp = TRUE; + } + } + + } else if (Opcode == SPEC_OP) { + + // + // Special operation. + // + // The real opcode is in the function field of special opcode + // instructions. + // + + Function = Instruction.r_format.Function; + if ((Function == ADDU_OP) || (Function == OR_OP)) { + + // + // Add unsigned or an or operation. + // + // If one of the source registers is ZERO, then the + // operation is a move operation and the destination + // register should be moved to the appropriate source + // register. + // + + if (Rt == ZERO) { + IntegerRegister[Rs] = IntegerRegister[Rd]; + + // + // 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, this an interrupt or exception and the + // return PC should be biased by 4. + // + + if (Rs == RA) { + if (RestoredRa == FALSE) { + NextPc = (ULONG)(ContextRecord->XIntRa - 4); + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + } + + } else if (Rs == ZERO) { + IntegerRegister[Rt] = IntegerRegister[Rd]; + + // + // 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, this an interrupt or exception and the + // return PC should be biased by 4. + // + + if (Rt == RA) { + if (RestoredRa == FALSE) { + NextPc = (ULONG)(ContextRecord->XIntRa - 4); + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + } + } + + } else if (Function == SUBU_OP) { + + // + // Subtract unsigned. + // + // If the destination register is SP and the source register + // is SP, then a stack allocation greater than 32kb has been + // performed and source register number of the decrement must + // be saved for later use. + // + + if ((Rd == SP) && (Rs == SP)) { + DecrementRegister = Rt; + } + } + + } else if (Opcode == LUI_OP) { + + // + // Load upper immediate. + // + // If the destination register is the decrement register, then + // compute the decrement value, add it from SP, and clear the + // decrement register number. + // + + if (Rt == DecrementRegister) { + DecrementRegister = 0; + IntegerRegister[SP] += (LONG)(DecrementOffset + (Offset << 16)); + if (RestoredSp == FALSE) { + *EstablisherFrame = (ULONG)(ContextRecord->XIntSp); + RestoredSp = TRUE; + } + } + } + } + + // + // Make sure that integer register zero is really zero. + // + + ContextRecord->XIntZero = 0; + return NextPc; + } +} + +ULONG +RtlpVirtualUnwind32 ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PULONG EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ) + +/*++ + +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 routine handles 32-bit context records. + +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. + +--*/ + +{ + + ULONG Address; + ULONG DecrementOffset; + ULONG DecrementRegister; + PULONG FloatingRegister; + ULONG Function; + MIPS_INSTRUCTION Instruction; + PULONG IntegerRegister; + ULONG NextPc; + LONG Offset; + ULONG Opcode; + ULONG Rd; + BOOLEAN RestoredRa; + BOOLEAN RestoredSp; + ULONG Rs; + ULONG Rt; + + // + // Set the base address of the integer and floating register arrays. + // + + FloatingRegister = &ContextRecord->FltF0; + IntegerRegister = &ContextRecord->IntZero; + + // + // If the instruction at the point where control left the specified + // function is a return, then any saved registers have been restored + // with the possible exception of the stack pointer and the control + // PC is not considered to be in the function (i.e., an epilogue). + // + + if (*((PULONG)ControlPc) == JUMP_RA) { + *InFunction = FALSE; + Instruction.Long = *((PULONG)ControlPc + 1); + Opcode = Instruction.i_format.Opcode; + Offset = Instruction.i_format.Simmediate; + Rd = Instruction.r_format.Rd; + Rs = Instruction.i_format.Rs; + Rt = Instruction.i_format.Rt; + Function = Instruction.r_format.Function; + + // + // If the opcode is an add immediate unsigned op and both the source + // and destination registers are SP, then add the signed offset value + // to SP. Otherwise, if the opcode is a special op, the operation is + // an add unsigned, and the source and destination registers are both + // SP, then add the register specified by Rd to SP. + // + + if ((Opcode == ADDIU_OP) && (Rt == SP) && (Rs == SP)) { + IntegerRegister[SP] += Offset; + + } else if ((Opcode == SPEC_OP) && (Function == ADDU_OP) && + (Rd == SP) && (Rs == SP)) { + IntegerRegister[SP] += IntegerRegister[Rt]; + } + + *EstablisherFrame = ContextRecord->IntSp; + return ContextRecord->IntRa; + } + + // + // If the address where control left the specified function is outside + // the limits 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., it is within the prologue). + // + + if ((ControlPc < FunctionEntry->BeginAddress) || + (ControlPc >= FunctionEntry->PrologEndAddress)) { + *InFunction = TRUE; + ControlPc = FunctionEntry->PrologEndAddress; + + } else { + *InFunction = FALSE; + } + + // + // Scan backward through the prologue and reload callee registers that + // were stored. + // + + DecrementRegister = 0; + *EstablisherFrame = ContextRecord->IntSp; + NextPc = ContextRecord->IntRa - 4; + RestoredRa = FALSE; + RestoredSp = FALSE; + while (ControlPc > FunctionEntry->BeginAddress) { + + // + // Get instruction value, decode fields, case of opcode value, and + // reverse store operations. + // + + ControlPc -= 4; + Instruction.Long = *((PULONG)ControlPc); + Opcode = Instruction.i_format.Opcode; + Offset = Instruction.i_format.Simmediate; + Rd = Instruction.r_format.Rd; + Rs = Instruction.i_format.Rs; + Rt = Instruction.i_format.Rt; + Address = Offset + IntegerRegister[Rs]; + if (Opcode == SW_OP) { + + // + // Store word. + // + // If the base register is SP and the source register is an + // integer register, then reload the register value. + // + + if (Rs == SP) { + IntegerRegister[Rt] = *((PULONG)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, this + // is an interrupt or exception and the return PC should be + // biased by 4. Otherwise, if the destination register is + // SP and this is the first time that SP is being restored, + // then set the establisher frame pointer. + // + + if (Rt == RA) { + if (RestoredRa == FALSE) { + NextPc = ContextRecord->IntRa - 4; + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + + } else if (Rt == SP) { + if (RestoredSp == FALSE) { + *EstablisherFrame = 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->XIntegerContext[Rt] = (PULONGLONG)Address; + } + } + + } else if (Opcode == SWC1_OP) { + + // + // Store word coprocessor 1. + // + // If the base register is SP and the source register is a + // floating register, then reload the register value. + // + + if (Rs == SP) { + FloatingRegister[Rt] = *((PULONG)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[Rt] = (PULONG)Address; + } + } + + } else if (Opcode == SDC1_OP) { + + // + // Store double coprocessor 1. + // + // If the base register is SP and the source register is a + // floating register, then reload the register and the next + // register values. + // + + if (Rs == SP) { + FloatingRegister[Rt] = *((PULONG)Address); + FloatingRegister[Rt + 1] = *((PULONG)(Address + 4)); + + // + // If a context pointer record is specified, then record + // the address where the destination registers contents + // are stored. + // + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->FloatingContext[Rt] = (PULONG)Address; + ContextPointers->FloatingContext[Rt + 1] = (PULONG)(Address + 4); + } + } + + } else if (Opcode == ADDIU_OP) { + + // + // Add immediate unsigned. + // + // If both the source and destination registers are SP, then + // a standard stack allocation was performed and the signed + // displacement value should be subtracted from SP. Otherwise, + // if the destination register is the decrement register and + // the source register is zero, then add the decrement value + // to SP. + // + + if ((Rs == SP) && (Rt == SP)) { + IntegerRegister[SP] -= Offset; + if (RestoredSp == FALSE) { + *EstablisherFrame = ContextRecord->IntSp; + RestoredSp = TRUE; + } + + } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { + IntegerRegister[SP] += Offset; + if (RestoredSp == FALSE) { + *EstablisherFrame = ContextRecord->IntSp; + RestoredSp = TRUE; + } + } + + } else if (Opcode == ORI_OP) { + + // + // Or immediate. + // + // If both the destination and source registers are the decrement + // register, then save the decrement value. Otherwise, if the + // destination register is the decrement register and the source + // register is zero, then add the decrement value to SP. + // + + if ((Rs == DecrementRegister) && (Rt == DecrementRegister)) { + DecrementOffset = (Offset & 0xffff); + + } else if ((Rt == DecrementRegister) && (Rs == ZERO)) { + IntegerRegister[SP] += (Offset & 0xffff); + if (RestoredSp == FALSE) { + *EstablisherFrame = ContextRecord->IntSp; + RestoredSp = TRUE; + } + } + + } else if (Opcode == SPEC_OP) { + + // + // Special operation. + // + // The real opcode is in the function field of special opcode + // instructions. + // + + Function = Instruction.r_format.Function; + if ((Function == ADDU_OP) || (Function == OR_OP)) { + + // + // Add unsigned or an or operation. + // + // If one of the source registers is ZERO, then the + // operation is a move operation and the destination + // register should be moved to the appropriate source + // register. + // + + if (Rt == ZERO) { + IntegerRegister[Rs] = IntegerRegister[Rd]; + + // + // 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, this an interrupt or exception and the + // return PC should be biased by 4. + // + + if (Rs == RA) { + if (RestoredRa == FALSE) { + NextPc = ContextRecord->IntRa - 4; + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + } + + } else if (Rs == ZERO) { + IntegerRegister[Rt] = IntegerRegister[Rd]; + + // + // 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, this an interrupt or exception and the + // return PC should be biased by 4. + // + + if (Rt == RA) { + if (RestoredRa == FALSE) { + NextPc = ContextRecord->IntRa - 4; + RestoredRa = TRUE; + + } else { + NextPc += 4; + } + } + } + + } else if (Function == SUBU_OP) { + + // + // Subtract unsigned. + // + // If the destination register is SP and the source register + // is SP, then a stack allocation greater than 32kb has been + // performed and source register number of the decrement must + // be saved for later use. + // + + if ((Rd == SP) && (Rs == SP)) { + DecrementRegister = Rt; + } + } + + } else if (Opcode == LUI_OP) { + + // + // Load upper immediate. + // + // If the destination register is the decrement register, then + // compute the decrement value, add it from SP, and clear the + // decrement register number. + // + + if (Rt == DecrementRegister) { + DecrementRegister = 0; + IntegerRegister[SP] += (DecrementOffset + (Offset << 16)); + if (RestoredSp == FALSE) { + *EstablisherFrame = ContextRecord->IntSp; + RestoredSp = TRUE; + } + } + } + } + + // + // Make sure that integer register zero is really zero. + // + + ContextRecord->IntZero = 0; + return NextPc; +} + +ULONG +RtlpVirtualUnwind ( + IN ULONG ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN PCONTEXT ContextRecord, + OUT PBOOLEAN InFunction, + OUT PULONG EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ) + +/*++ + +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 | 1, + FunctionEntry, + &LocalContext, + InFunction, + EstablisherFrame, + ContextPointers); +} diff --git a/private/ntos/rtl/mips/getcalr.c b/private/ntos/rtl/mips/getcalr.c new file mode 100644 index 000000000..1b9df34fa --- /dev/null +++ b/private/ntos/rtl/mips/getcalr.c @@ -0,0 +1,179 @@ +/*++ + +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 + +--*/ +#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 routine that called the routine + that called this routine, and 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 A and B 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. + +--*/ + +{ + + CONTEXT ContextRecord; + ULONG EstablisherFrame; + PRUNTIME_FUNCTION FunctionEntry; + BOOLEAN InFunction; + ULONG NextPc; + ULONG HighLimit, LowLimit; + + // + // 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.XIntRa; + + // + // 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). + // + + 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 | 1, + 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.XIntSp < 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 | 1, + 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.XIntSp < 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 | 1, + FunctionEntry, + &ContextRecord, + &InFunction, + &EstablisherFrame, + NULL); + + *CallersCallersPc = (PVOID)NextPc; + } + } + } + + return; +} + +USHORT +RtlCaptureStackBackTrace( + IN ULONG FramesToSkip, + IN ULONG FramesToCapture, + OUT PVOID *BackTrace, + OUT PULONG BackTraceHash + ) +{ + return 0; +} diff --git a/private/ntos/rtl/mips/largeint.s b/private/ntos/rtl/mips/largeint.s new file mode 100644 index 000000000..d051d2fc7 --- /dev/null +++ b/private/ntos/rtl/mips/largeint.s @@ -0,0 +1,947 @@ +// TITLE("Large Integer Arithmetic") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// +// Module Name: +// +// largeint.s +// +// Abstract: +// +// This module implements routines for performing extended integer +// arithmtic. +// +// Author: +// +// David N. Cutler (davec) 18-Apr-1990 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +//-- + +#include "ksmips.h" + + 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. +// +// Arguments: +// +// Addend1 (a2, a3) - Supplies the first addend value. +// +// Addend2 (4 * 4(sp), 4 * 5(sp)) - Supplies the second addend value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerAdd) + + lw t0,4 * 4(sp) // get low part of addend2 value + lw t1,4 * 5(sp) // get high part of addend2 value + addu t0,t0,a2 // add low parts of large integer + addu t1,t1,a3 // add high parts of large integer + sltu t2,t0,a2 // generate carry from low part + addu t1,t1,t2 // add carry to high part + sw t0,0(a0) // store low part of result + sw t1,4(a0) // store high part of result + move v0,a0 // set function return register + j ra // return + + .end RtlLargeIntegerAdd + + SBTTL("Convert Long to Large Integer") +//++ +// +// LARGE_INTEGER +// RtlConvertLongToLargeInteger ( +// IN LONG SignedInteger +// ) +// +// Routine Description: +// +// This function converts the a signed integer to a signed large integer +// and returns the result. +// +// Arguments: +// +// SignedInteger (a1) - Supplies the value to convert. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlConvertLongToLargeInteger) + + sra a2,a1,31 // compute high part of result + sw a1,0(a0) // store low part of result + sw a2,4(a0) // store high part of result + move v0,a0 // set function return register + j ra // return + + .end RtlConvertLongToLargeInteger + + SBTTL("Convert Ulong to Large Integer") +//++ +// +// LARGE_INTEGER +// RtlConvertUlongToLargeInteger ( +// IN LONG UnsignedInteger +// ) +// +// Routine Description: +// +// This function converts the an unsigned integer to a signed large +// integer and returns the result. +// +// Arguments: +// +// UnsignedInteger (a1) - Supplies the value to convert. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlConvertUlongToLargeInteger) + + sw a1,0(a0) // store low part of result + sw zero,4(a0) // store high part of result + move v0,a0 // set function return register + j 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 an signed integer and +// returns a signed large integer result. +// +// Arguments: +// +// Multiplicand (a1) - Supplies the multiplicand value. +// +// Multiplier (a2) - Supplies the multiplier value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlEnlargedIntegerMultiply) + + mult a1,a2 // multiply longword value + mflo t0 // get low 32-bits of result + mfhi t1 // get high 32-bits of result + sw t0,0(a0) // set low part of result + sw t1,4(a0) // set high part of result + move v0,a0 // set function return register + j ra // return + + .end RtlEnlargedIntegerMultiply) + + 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 (a1) - Supplies the multiplicand value. +// +// Multiplier (a2) - Supplies the multiplier value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlEnlargedUnsignedMultiply) + + multu a1,a2 // multiply longword value + mflo t0 // get low 32-bits of result + mfhi t1 // get high 32-bits of result + sw t0,0(a0) // set low part of result + sw t1,4(a0) // set high part of result + move v0,a0 // set function return register + j ra // return + + .end RtlEnlargedUnsignedMultiply) + + SBTTL("Enlarged Unsigned Divide") +//++ +// +// ULONG +// RtlEnlargedUnsignedDivide ( +// IN ULARGE_INTEGER Dividend, +// IN ULONG Divisor, +// IN PULONG Remainder. +// ) +// +// Routine Description: +// +// This function divides an unsigned large integer by an unsigned long +// and returns the resultant quotient and optionally the remainder. +// +// N.B. It is assumed that no overflow will occur. +// +// Arguments: +// +// Dividend (a0, a1) - Supplies the dividend value. +// +// Divisor (a2) - Supplies the divisor value. +// +// Remainder (a3) - Supplies an optional pointer to a variable that +// receives the remainder. +// +// Return Value: +// +// The unsigned long integer quotient is returned as the function value. +// +//-- + + LEAF_ENTRY(RtlEnlargedUnsignedDivide) + + sltu v1,a1,a2 // check if overflow will occur + beq zero,a2,20f // if eq, attempted division by zero + dsll a1,a1,32 // left justify hihg part of dividend + beq zero,v1,30f // if eq, overflow will occur + dsll a0,a0,32 // zero extend low part of dividend + dsrl a0,a0,32 // + or a0,a0,a1 // merge high and low part of dividend + ddivu a0,a2 // compute quotient value + mflo v0 // set quotient value + beq zero,a3,10f // if eq, remainder not requested + mfhi a0 // load remainder + sw a0,0(a3) // store longword remainder +10: j ra // return + + +20: break DIVIDE_BY_ZERO_BREAKPOINT // attempted division by zero + j ra // + +30: break DIVIDE_OVERFLOW_BREAKPOINT // division value overflows result + j ra // + + .end RtlEnlargedUnsignedDivide + + SBTTL("Extended Large Integer Divide") +//++ +// +// LARGE_INTEGER +// RtlExtendedLargeIntegerDivide ( +// IN LARGE_INTEGER Dividend, +// IN ULONG Divisor, +// IN PULONG Remainder. +// ) +// +// Routine Description: +// +// This function divides an unsigned large integer by an unsigned long +// and returns the resultant quotient and optionally the remainder. +// +// Arguments: +// +// Dividend (a2, a3) - Supplies the dividend value. +// +// Divisor (4 * 4(sp)) - Supplies the divisor value. +// +// Remainder (4 * 5(sp)- Supplies an optional pointer to a variable +// that receives the remainder. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlExtendedLargeIntegerDivide) + + .set noreorder + .set noat + move v0,a0 // set function return register + lw a1,4 * 4(sp) // get divisor value + lw t0,4 * 5(sp) // get address to store remainder + beq zero,a1,30f // if eq, attempted division by zero + li t1,63 // set loop count + move t2,zero // clear partial remainder +10: sra t3,t2,31 // replicate partial remainder high bit + sll t2,t2,1 // shift next dividend bit + srl t4,a3,31 // into the partial remainder + or t2,t2,t4 // + sll a3,a3,1 // double left shift dividend + srl t4,a2,31 // + or a3,a3,t4 // + sltu t4,t2,a1 // check if partial remainder less + subu t4,t4,1 // convert to 0 or -1 + or t4,t4,t3 // merge with partial remainder high bit + and t5,t4,a1 // select divisor or 0 + sll a2,a2,1 // + subu a2,a2,t4 // merge quotient bit + subu t2,t2,t5 // subtract out divisor + bne zero,t1,10b // if ne, more iterations to go + subu t1,t1,1 // decrement iteration count + beq zero,t0,20f // if eq, remainder not requested + sw a2,0(a0) // store low part of quotient + sw t2,0(t0) // store longword remainder +20: j ra // return + sw a3,4(a0) // store high part of quotient + .set at + .set reorder + +30: break DIVIDE_BY_ZERO_BREAKPOINT // attempted division by zero + j ra // + + .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 (a2, a3) - Supplies the dividend value. +// +// MagicDivisor (4 * 4(sp), 4 * 5(sp)) - Supplies the magic divisor value +// which is a 64-bit multiplicative reciprocal. +// +// Shiftcount (4 * 6(sp)) - Supplies the right shift adjustment value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlExtendedMagicDivide) + + move t0,a2 // assume dividend is positive + move t1,a3 // + bgez a3,10f // if gez, positive dividend + subu t0,zero,t0 // negate low part of dividend + subu t1,zero,t1 // negate high part of dividend + sltu t2,zero,t0 // set borrow from high part + subu t1,t1,t2 // subtract out out borrow +10: lw a1,4 * 4(sp) // get low part of magic dividor + lw t2,4 * 5(sp) // get high part of magic divisor + lbu v0,4 * 6(sp) // get shift count + +// +// Compute low 32-bits of dividend times low 32-bits of divisor. +// + + multu t0,a1 // + mfhi t3 // save high 32-bits of product + +// +// Compute low 32-bits of dividend time high 32-bits of divisor. +// + + multu t0,t2 // + mflo t4 // save low 32-bits of product + mfhi t5 // save high 32-bits of product + +// +// Compute high 32-bits of dividend times low 32-bits of divisor. +// + + multu t1,a1 // + mflo t6 // save loow 32-bits of product + mfhi t7 // save high 32-bits of product + +// +// Compute high 32-bits of dividend times high 32-bits of divisor. +// + + multu t1,t2 // + mflo t8 // save low 32-bits of product + mfhi t9 // save high 32-bits of product + +// +// Add partial results to form high 64-bits of result. +// + + addu t0,t3,t4 // + sltu t1,t0,t4 // generate carry + addu t0,t0,t6 // + sltu t2,t0,t6 // generate carry + addu t2,t1,t2 // combine carries + addu t1,t2,t5 // + sltu t2,t1,t5 // generate carry + addu t1,t1,t7 // + sltu t3,t1,t7 // generate carry + addu t2,t2,t3 // combine carries + addu t1,t1,t8 // + sltu t3,t1,t8 // generate carry + addu t2,t2,t3 // combine carries + addu t2,t2,t9 // + +// +// Right shift the result by the specified shift count and negate result +// if necessary. +// + + li v1,32 // compute left shift count + subu v1,v1,v0 // + bgtz v1,20f // if gtz, shift less that 32-bits + +// +// Shift count is greater than or equal 32 bits - high half of result is zero, +// low half is the high half shifted right by remaining count. +// + + move t1,zero // set high half of result + srl t0,t2,v0 // set low half of result + b 30f // + +// +// Shift count is less than 32-bits - high half of result is the high half +// of product shifted right by count, low half of result is the shifted out +// bits of the high half combined with the rigth shifted low half of the +// product. +// + +20: srl t0,t1,v0 // shift low half right count bits + srl t1,t2,v0 // shift high half right count bits + beq zero,v0,30f // if eq, no more shifts necessary + sll t2,t2,v1 // isolate shifted out bits of high half + or t0,t0,t2 // combine bits for low half of result + +// +// Negate result if neccessary. +// + +30: bgez a3,40f // if gez, positive result + subu t0,zero,t0 // negate low half of result + subu t1,zero,t1 // negate high half of result + beq zero,t0,40f // if eq, negation complete + subu t1,t1,1 // convert high part to ones complement +40: sw t0,0(a0) // store low half of result + sw t1,4(a0) // store high half of result + move v0,a0 // set function return register + j ra // return + + .end RtlExtendedMagicDivide + + SBTTL("Large Integer Divide") +//++ +// +// LARGE_INTEGER +// RtlLargeIntegerDivide ( +// IN LARGE_INTEGER Dividend, +// IN LARGE_INTEGER Divisor, +// IN PLARGE_INTEGER Remainder. +// ) +// +// Routine Description: +// +// This function divides an unsigned large integer by an unsigned +// large and returns the resultant quotient and optionally the remainder. +// +// Arguments: +// +// Dividend (a2, a3) - Supplies the dividend value. +// +// Divisor (4 * 4(sp), 4 * 5(sp)) - Supplies the divisor value. +// +// Remainder (4 * 6(sp)- Supplies an optional pointer to a variable +// that receives the remainder. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerDivide) + + .set noreorder + .set noat + move v0,a0 // set function return register + lw a1,4 * 4(sp) // get low part of divisor + lw t0,4 * 5(sp) // get high part of divisor + lw t1,4 * 6(sp) // get address to store remainder + or v1,t0,a1 // combine low and high parts + beq zero,v1,60f // if eq, attempted division by zero + li t2,63 // set loop count + move t3,zero // clear partial remainder + move t4,zero // +10: sll t4,t4,1 // shift next dividend bit + srl t5,t3,31 // into the partial remainder + or t4,t4,t5 // + sll t3,t3,1 // + srl t5,a3,31 // + or t3,t3,t5 // + sll a3,a3,1 // double left shift dividend + srl t5,a2,31 // + or a3,a3,t5 // + sltu t5,t4,t0 // check if partial remainder less + beq zero,t5,20f // if eq, partial remainder not less + sll a2,a2,1 // + bne zero,t2,10b // if ne, more iterations to go + subu t2,t2,1 // decrement iteration count + beq zero,t1,50f // if eq, remainder not requested + sw a2,0(a0) // store low part of quotient + sw t3,0(t1) // store large integer remainder + sw t4,4(t1) // + j ra // return + sw a3,4(a0) // store high part of quotient + +20: bne t0,t4,30f // if ne, partial remainder greater + sltu t5,t3,a1 // check is partial remainder less + bne zero,t5,40f // if ne, partial remainder less + nop // +30: or a2,a2,1 // merge quotient bit + subu t4,t4,t0 // subtract out divisor high part + sltu t5,t3,a1 // set borrow from high part + subu t4,t4,t5 // subtract borrow from high part + subu t3,t3,a1 // subtract out divisor low +40: bne zero,t2,10b // if ne, more iterations to go + subu t2,t2,1 // decrement iteration count + beq zero,t1,50f // if eq, remainder not requested + sw a2,0(a0) // store low part of quotient + sw t3,0(t1) // store large integer remainder + sw t4,4(t1) // +50: j ra // return + sw a3,4(a0) // store high part of quotient + .set at + .set reorder + +60: break DIVIDE_BY_ZERO_BREAKPOINT // attempted division by zero + j ra // + + .end RtlLargeIntegerDivide + + 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. +// +// Arguments: +// +// Multiplicand (a2, a3) - Supplies the multiplicand value. +// +// Multiplier (4 * 4(sp)) - Supplies the multiplier value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlExtendedIntegerMultiply) + + lw a1,4 * 4(sp) // get multiplier value + xor t9,a1,a3 // compute sign of result + move t0,a1 // assume multiplier positive + bgez a1,10f // if gez, positive multiplier + subu t0,zero,t0 // negate multiplier +10: move t1,a2 // assume multiplicand positive + move t2,a3 // + bgez a3,20f // if gez, positive multiplicand + subu t1,zero,t1 // negate multiplicand + subu t2,zero,t2 // + sltu t3,zero,t1 // compute borrow from high part + subu t2,t2,t3 // subtract out borrow + +// +// Compute low 32-bits of multiplier times the low 32-bit of multiplicand. +// + +20: multu t0,t1 // + mflo t4 // save low 32-bits of product + mfhi t5 // save high 32-bits of product + +// +// Compute low 32-bits of multiplier times the high 32-bits of multiplicand. +// + + multu t0,t2 // + mflo t6 // save low 32-bits of product + mfhi t7 // save high 32-bits of product + +// +// Add partial results to form high 64-bits of result. +// + + addu t5,t5,t6 // + sltu t3,t5,t6 // generate carry + addu t6,t3,t7 // + +// +// Negate result if neccessary. +// + + bgez t9,40f // if gez, positive result + subu t4,zero,t4 // negate low half of result + subu t5,zero,t5 // negate high half of result + subu t6,zero,t6 // negate extended part of result + beq zero,t4,30f // if eq, negation complete + subu t5,t5,1 // convert high part to ones complement + subu t6,t6,1 // convert extended part to ones complement + b 40f // + +30: beq zero,t5,40f // if eq, negation complete + subu t6,t6,1 // convert extended part to ones complement + +// +// Store final result. +// + +40: sw t4,0(a0) // store low half of result + sw t5,4(a0) // store high half of result + move v0,a0 // set function return register + j ra // return + + .end RtlExtendedIntegerMultiply + + 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. +// +// Arguments: +// +// Subtrahend (a2, a3) - Supplies the subtrahend value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerNegate) + + subu a2,zero,a2 // negate low part of subtrahend + subu a3,zero,a3 // negate high part of subtrahend + sltu t0,zero,a2 // compute borrow from high part + subu a3,a3,t0 // subtract borrow from high part + sw a2,0(a0) // store low part of result + sw a3,4(a0) // store high part of result + move v0,a0 // set function return register + j ra // return + + .end RtlLargeIntegerNegate + + 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. +// +// Arguments: +// +// Minuend (a2, a3) - Supplies the minuend value. +// +// Subtrahend (4 * 4(sp), 4 * 5(sp)) - Supplies the subtrahend value. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerSubtract) + + lw a1,4 * 4(sp) // get low part of subtrahend + lw t2,4 * 5(sp) // get high part of subtrahend + subu t0,a2,a1 // subtract low parts + subu t1,a3,t2 // subtract high parts + sltu t3,a2,a1 // generate borrow from high part + subu t1,t1,t3 // subtract borrow + sw t0,0(a0) // store low part of result + sw t1,4(a0) // store high part of result + move v0,a0 // set function return register + j ra // return + + .end RtlLargeIntegerSubtract + + 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. +// +// N.B. No test is made for significant bits shifted out of the result. +// +// Arguments: +// +// LargeInteger (a2, a3) - Supplies the large integer to be shifted. +// +// ShiftCount (4 * 4(sp)) - Supplies the left shift count. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerShiftLeft) + + lbu a1,4 * 4(sp) // get shift count + move v0,a0 // set function return register + and a1,a1,0x3f // truncate shift count mod 64 + +// +// Left shift the operand by the specified shift count. +// + + li v1,32 // compute right shift count + subu v1,v1,a1 // + bgtz v1,10f // if gtz, shift less that 32-bits + +// +// Shift count is greater than or equal 32 bits - low half of result is zero, +// high half is the low half shifted left by remaining count. +// + + sll a3,a2,a1 // set high half of result + sw zero,0(a0) // store low part of reuslt + sw a3,4(a0) // store high part of result + j ra // return + +// +// Shift count is less than 32-bits - high half of result is the high half +// of operand shifted left by count combined with the low half of the operand +// shifted right, low half of result is the low half shifted left. +// + +10: sll a3,a3,a1 // shift high half left count bits + beq zero,a1,20f // if eq, no more shifts necessary + srl t0,a2,v1 // isolate shifted out bits of low half + sll a2,a2,a1 // shift low half left count bits + or a3,a3,t0 // combine bits for high half of result +20: sw a2,0(a0) // store low part of reuslt + sw a3,4(a0) // store high part of result + j 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 (a2, a3) - Supplies the large integer to be shifted. +// +// ShiftCount (4 * 4(sp)) - Supplies the right shift count. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerShiftRight) + + lbu a1,4 * 4(sp) // get shift count + move v0,a0 // set function return register + and a1,a1,0x3f // truncate shift count mod 64 + +// +// Right shift the operand by the specified shift count. +// + + li v1,32 // compute left shift count + subu v1,v1,a1 // + bgtz v1,10f // if gtz, shift less that 32-bits + +// +// Shift count is greater than or equal 32 bits - high half of result is +// zero, low half is the high half shifted right by remaining count. +// + + srl a2,a3,a1 // set low half of result + sw a2,0(a0) // store low part of reuslt + sw zero,4(a0) // store high part of result + j ra // return + +// +// Shift count is less than 32-bits - high half of result is the high half +// of operand shifted right by count, low half of result is the shifted out +// bits of the high half combined with the right shifted low half of the +// operand. +// + +10: srl a2,a2,a1 // shift low half right count bits + beq zero,a1,20f // if eq, no more shifts necessary + sll t0,a3,v1 // isolate shifted out bits of high half + srl a3,a3,a1 // shift high half right count bits + or a2,a2,t0 // combine bits for low half of result +20: sw a2,0(a0) // store low part of reuslt + sw a3,4(a0) // store high part of result + j ra // return + + .end RtlLargeIntegerShiftRight + + 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 (a1, a2) - Supplies the large integer to be shifted. +// +// ShiftCount (a3) - Supplies the right shift count. +// +// Return Value: +// +// The large integer result is stored at the address supplied by a0. +// +//-- + + LEAF_ENTRY(RtlLargeIntegerArithmeticShift) + + lbu a1,4 * 4(sp) // get shift count + move v0,a0 // set function return register + and a1,a1,0x3f // truncate shift count mod 64 + +// +// Right shift the operand by the specified shift count. +// + + li v1,32 // compute left shift count + subu v1,v1,a1 // + bgtz v1,10f // if gtz, shift less that 32-bits + +// +// Shift count is greater than or equal 32 bits - high half of result is +// zero, low half is the high half shifted right by remaining count. +// + + sra a2,a3,a1 // set low half of result + sra a3,a3,31 // set high half of result + sw a2,0(a0) // store low part of reuslt + sw a3,4(a0) // store high part of result + j ra // return + +// +// Shift count is less than 32-bits - high half of result is the high half +// of operand shifted right by count, low half of result is the shifted out +// bits of the high half combined with the right shifted low half of the +// operand. +// + +10: srl a2,a2,a1 // shift low half right count bits + beq zero,a1,20f // if eq, no more shifts necessary + sll t0,a3,v1 // isolate shifted out bits of high half + sra a3,a3,a1 // shift high half right count bits + or a2,a2,t0 // combine bits for low half of result +20: sw a2,0(a0) // store low part of reuslt + sw a3,4(a0) // store high part of result + j ra // return + + .end RtlLargeIntegerArithmeticShift diff --git a/private/ntos/rtl/mips/lzntmips.s b/private/ntos/rtl/mips/lzntmips.s new file mode 100644 index 000000000..b97699bc2 --- /dev/null +++ b/private/ntos/rtl/mips/lzntmips.s @@ -0,0 +1,1325 @@ +// TITLE("LZ Decompression") +//++ +// +// Copyright (c) 1994 Microsoft Corporation +// +// Module Name: +// +// lznt1m.s +// +// Abstract: +// +// This module implements the decompression engine needed +// to support file system compression. +// +// Author: +// +// Mark Enstrom (marke) 21-Nov-1994 +// +// Environment: +// +// Any mode. +// +// Revision History: +// +//-- + +#include "ksmips.h" + + + +// #define FORMAT412 0 +// #define FORMAT511 1 +// #define FORMAT610 2 +// #define FORMAT79 3 +// #define FORMAT88 4 +// #define FORMAT97 5 +// #define FORMAT106 6 +// #define FORMAT115 7 +// #define FORMAT124 8 +// +// 4/12 5/11 6/10 7/9 8/8 9/7 10/6 11/5 12/4 +// +// ULONG FormatMaxLength[] = { 4098, 2050, 1026, 514, 258, 130, 66, 34, 18 }; +// ULONG FormatMaxDisplacement[] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096 }; +// +// width table for LZ length and offset encoding +// + + + SBTTL("LZNT1DecompressChunk") +//++ +// +// 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. +// +// The following code takes advantage of three distinct observations. +// First, literal tokens occur at least twice as often as copy tokens. +// This argues for having a "fall-through" being the case where a literal +// token is found. We structure the main decomposition loop in eight +// pieces where the first piece is a sequence of literal-test fall-throughs +// and the remainder are a copy token followed by 7,6,...,0 literal-test +// fall-throughs. Each test examines a particular bit in the tag byte +// and jumps to the relevant code piece. +// +// The second observation involves performing bounds checking only +// when needed. Bounds checking the compressed buffer need only be done +// when fetching the tag byte. If there is not enough room left in the +// input for a tag byte and 8 (worst case) copy tokens, a branch is made +// to a second loop that handles a byte-by-byte "safe" copy to finish +// up the decompression. Similarly, at the head of the loop a check is +// made to ensure that there is enough room in the output buffer for 8 +// literal bytes. If not enough room is left, then the second loop is +// used. Finally, after performing each copy, the output-buffer check +// is made as well since a copy may take the destination pointer +// arbitrarily close to the end of the destination. +// +// The third observation is an examination of CPU time while disk +// decompression is in progress. CPU utilization is only less than +// 25% peak. This means this routine should be written to minimize +// latency instead of bandwidth. For this reason, taken branches are +// avoided at the cost of code size and loop unrolling is not done. +// +// Arguments: +// +// a0 - UncompressedBuffer - Pointer to start of destination buffer +// a1 - EndOfUncompressedBufferPlus1 - One byte beyond uncompressed buffer +// a2 - CompressedBuffer - Pointer to buffer of compressed data (this pointer +// has been adjusted to point past the chunk header) +// a3 - EndOfCompressedBufferPlus1 - One byte beyond compressed buffer +// (sp) - FinalUncompressedChunkSize - return bytes written to +// UncompressedBuffer +// +// Return Value: +// +// None +// +//-- + + .struct 0 +LzS0: .space 4 // saved internal register s0 +LzRA: .space 4 // saved internal register ra + .space 4*2 // fill to keep stack 16-byte aligned +LzFrameLength: // Length of Stack frame + .space 4*4 // parameter 0-3 space +LzFinal:.space 4 // argument FinalUncompressedChunkSize + + + NESTED_ENTRY(LZNT1DecompressChunk, LzFrameLength, zero) + + subu sp,sp,LzFrameLength + + sw s0,LzS0(sp) + sw ra,LzRA(sp) + + PROLOGUE_END + +// +// make copy of UncompressedBuffer for +// current output pointer +// + + move t4,a0 + +// +// Initialize variables used in keeping track of the +// LZ Copy Token format. t9 is used to store the maximum +// displacement for each phase of LZ decoding +// (see explanation of format in LZNT1.c). This displacement +// is added to the start of the CompressedBuffer address +// so that a boundary crossing can be detected. +// + + li t9,0x10 // t9 = Max Displacement for LZ + addu t8,t9,a0 // t8 = Format boundary + li t7,0xffff >> 4 // t7 = length mask + li t6,12 // t6 = offset shift count + +// +// Initialize variables to track safe copy limits for +// CompressedBuffer and UncopmressedBuffer. This allows +// execution of the quick Flag check below without +// checking for crossing the end of either buffer. +// From CompressedBuffer, one input pass includes 1 flag byte +// and up to 8 two byte copy tokens ( 1+2*8). +// To the un-compressed buffer, 8 literal bytes may be written, +// any copy-token bits set will cause an explicit length check +// in the LzCopy section +// + + subu v0,a1,8 // safe end of UncompressedBuffer + subu v1,a3,1+2*8 // safe end of CompressedBuffer + +Top: + +// +// make sure safe copy can be performed for at least 8 literal bytes +// + + bgt a2,v1,SafeCheckStart // safe check + bgt t4,v0,SafeCheckStart // safe check + lbu s0,0(a2) // load flag byte + +// +// fall-through for copying 8 bytes. +// + + + sll t0,s0,31-0 // shift proper flag bit into sign bit + lbu t1,1(a2) // load literal or CopyToken[0] + bltz t0,LzCopy0 // if sign bit is set, go to copy routine + sb t1,0(t4) // store literal byte to dst + + sll t0,s0,31-1 // shift proper flag bit into sign bit + lbu t1,2(a2) // load literal or CopyToken[0] + bltz t0,LzCopy1 // if sign bit is set, go to copy routine + sb t1,1(t4) // store literal byte to dst + + sll t0,s0,31-2 // shift proper flag bit into sign bit + lbu t1,3(a2) // load literal or CopyToken[0] + bltz t0,LzCopy2 // if sign bit is set, go to copy routine + sb t1,2(t4) // store literal byte to dst + + sll t0,s0,31-3 // shift proper flag bit into sign bit + lbu t1,4(a2) // load literal or CopyToken[0] + bltz t0,LzCopy3 // if sign bit is set, go to copy routine + sb t1,3(t4) // store literal byte to dst + + sll t0,s0,31-4 // shift proper flag bit into sign bit + lbu t1,5(a2) // load literal or CopyToken[0] + bltz t0,LzCopy4 // if sign bit is set, go to copy routine + sb t1,4(t4) // store literal byte to dst + + sll t0,s0,31-5 // shift proper flag bit into sign bit + lbu t1,6(a2) // load literal or CopyToken[0] + bltz t0,LzCopy5 // if sign bit is set, go to copy routine + sb t1,5(t4) // store literal byte to dst + + sll t0,s0,31-6 // shift proper flag bit into sign bit + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + + addu t4,t4,8 + b Top + + + + +LzCopy0: + +// +// LzCopy0 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,2(a2) // load second byte of copy token + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + ble t4,v0,10f // skip if still in safe boundry + li t5,7 // seven bits left in current flag byte + addu a2,a2,2 // Make a2 point to next src byte + srl s0,s0,1 // shift flag byte into next position + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy. Continue flag check at position 1 +// + + subu t4,t4,1 // unbias output pointer + + sll t0,s0,31-1 // rotate flag bit into sign position + lbu t1,2(a2) // load literal or CopyToken[0] + bltz t0,LzCopy1 // if sign bit is set, go to copy routine + sb t1,1(t4) // store literal byte to dst + + + sll t0,s0,31-2 // shift proper flag bit into sign bit + lbu t1,3(a2) // load literal or CopyToken[0] + bltz t0,LzCopy2 // if sign bit is set, go to copy routine + sb t1,2(t4) // store literal byte to dst + + sll t0,s0,31-3 // shift proper flag bit into sign bit + lbu t1,4(a2) // load literal or CopyToken[0] + bltz t0,LzCopy3 // if sign bit is set, go to copy routine + sb t1,3(t4) // store literal byte to dst + + sll t0,s0,31-4 // shift proper flag bit into sign bit + lbu t1,5(a2) // load literal or CopyToken[0] + bltz t0,LzCopy4 // if sign bit is set, go to copy routine + sb t1,4(t4) // store literal byte to dst + + sll t0,s0,31-5 // shift proper flag bit into sign bit + lbu t1,6(a2) // load literal or CopyToken[0] + bltz t0,LzCopy5 // if sign bit is set, go to copy routine + sb t1,5(t4) // store literal byte to dst + + sll t0,s0,31-6 // shift proper flag bit into sign bit + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + + + +LzCopy1: + +// +// LzCopy1 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,3(a2) // load second byte of copy token + addu t4,t4,1 // mov t4 to point to byte 1 + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + ble t4,v0,10f // skip if still in safe boundry + li t5,6 // six bits left in current flag byte + addu a2,a2,3 // Make a2 point to next src byte + srl s0,s0,2 // shift flag byte into position + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy. Continue flag check at position 2 +// + + subu t4,t4,2 // un-bias input pointer + + sll t0,s0,31-2 // rotate flag into position for sign check + lbu t1,3(a2) // load literal or CopyToken[0] + bltz t0,LzCopy2 // if sign bit is set, go to copy routine + sb t1,2(t4) // store literal byte to dst + + sll t0,s0,31-3 // shift proper flag bit into sign bit + lbu t1,4(a2) // load literal or CopyToken[0] + bltz t0,LzCopy3 // if sign bit is set, go to copy routine + sb t1,3(t4) // store literal byte to dst + + sll t0,s0,31-4 // shift proper flag bit into sign bit + lbu t1,5(a2) // load literal or CopyToken[0] + bltz t0,LzCopy4 // if sign bit is set, go to copy routine + sb t1,4(t4) // store literal byte to dst + + sll t0,s0,31-5 // shift proper flag bit into sign bit + lbu t1,6(a2) // load literal or CopyToken[0] + bltz t0,LzCopy5 // if sign bit is set, go to copy routine + sb t1,5(t4) // store literal byte to dst + + sll t0,s0,31-6 // shift proper flag bit into sign bit + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + + +LzCopy2: + +// +// LzCopy2 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,4(a2) // load second byte of copy token + addu t4,t4,2 // mov t4 to point to byte 1 + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + ble t4,v0,10f // skip if still in safe boundry + li t5,5 // five bits left in current flag byte + addu a2,a2,4 // Make a2 point to next src byte + srl s0,s0,3 // shift flag byte into positin + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy +// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here) +// + + subu t4,t4,3 // un-bias output pointer + + sll t0,s0,31-3 // rotate flag into position for sign check + lbu t1,4(a2) // load literal or CopyToken[0] + bltz t0,LzCopy3 // if sign bit is set, go to copy routine + sb t1,3(t4) // store literal byte to dst + + sll t0,s0,31-4 // shift proper flag bit into sign bit + lbu t1,5(a2) // load literal or CopyToken[0] + bltz t0,LzCopy4 // if sign bit is set, go to copy routine + sb t1,4(t4) // store literal byte to dst + + sll t0,s0,31-5 // shift proper flag bit into sign bit + lbu t1,6(a2) // load literal or CopyToken[0] + bltz t0,LzCopy5 // if sign bit is set, go to copy routine + sb t1,5(t4) // store literal byte to dst + + sll t0,s0,31-6 // shift proper flag bit into sign bit + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + + + +LzCopy3: + +// +// LzCopy3 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,5(a2) // load second byte of copy token + addu t4,t4,3 // mov t4 to point to byte 1 + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + + ble t4,v0,10f // skip if still in safe boundry + li t5,4 // four bits left in current flag byte + addu a2,a2,5 // Make a2 point to next src byte + srl s0,s0,4 // shift flag byte into positin + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy +// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here) +// + + subu t4,t4,4 // un-bias output pointer + + sll t0,s0,31-4 // rotate flag into position for sign check + lbu t1,5(a2) // load literal or CopyToken[0] + bltz t0,LzCopy4 // if sign bit is set, go to copy routine + sb t1,4(t4) // store literal byte to dst + + sll t0,s0,31-5 // shift proper flag bit into sign bit + lbu t1,6(a2) // load literal or CopyToken[0] + bltz t0,LzCopy5 // if sign bit is set, go to copy routine + sb t1,5(t4) // store literal byte to dst + + sll t0,s0,31-6 // shift proper flag bit into sign bit + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + + +LzCopy4: + +// +// LzCopy4 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,6(a2) // load second byte of copy token + addu t4,t4,4 // mov t4 to point to byte 1 + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + ble t4,v0,10f // skip if still in safe boundry + li t5,3 // three bits left in current flag byte + addu a2,a2,6 // Make a2 point to next src byte + srl s0,s0,5 // shift flag byte so that next bit is in positin 0 + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy +// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here) +// + + subu t4,t4,5 // un-bias output pointer + + sll t0,s0,31-5 // rotate flag into position for sign check + lbu t1,6(a2) // load literal or CopyToken[0] + bltz t0,LzCopy5 // if sign bit is set, go to copy routine + sb t1,5(t4) // store literal byte to dst + + sll t0,s0,31-6 // shift proper flag bit into sign bit + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + +LzCopy5: + +// +// LzCopy5 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,7(a2) // load second byte of copy token + addu t4,t4,5 // mov t4 to point to byte 1 + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + ble t4,v0,10f // skip if still in safe boundry + li t5,2 // two bits left in current flag byte + addu a2,a2,7 // Make a2 point to next src byte + srl s0,s0,6 // shift flag byte so that next bit is in positin 0 + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy +// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here) +// + + subu t4,t4,6 // un-bias output pointer + + sll t0,s0,31-6 // rotate flag into position for sign check + lbu t1,7(a2) // load literal or CopyToken[0] + bltz t0,LzCopy6 // if sign bit is set, go to copy routine + sb t1,6(t4) // store literal byte to dst + + sll t0,s0,31-7 // shift proper flag bit into sign bit + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + + +LzCopy6: + +// +// LzCopy6 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + lbu t2,8(a2) // load second byte of copy token + addu t4,t4,6 // mov t4 to point to byte 1 + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + ble t4,v0,10f // skip if still in safe boundry + li t5,1 // one bit left in current flag byte + addu a2,a2,8 // Make a2 point to next src byte + srl s0,s0,7 // shift flag byte into position + b SafeCheckLoop +10: + +// +// adjust t4 back to position it would be if this was a liternal byte +// copy +// continue flag check at position 1 (could duplicate LzQuick switch 1-7 here) +// + + subu t4,t4,7 // un-bias output pointer + + sll t0,s0,31-7 // rotate flag into position for sign check + lbu t1,8(a2) // load literal or CopyToken[0] + bltz t0,LzCopy7 // if sign bit is set, go to copy routine + sb t1,7(t4) + + addu a2,a2,9 // inc src addr + addu t4,t4,8 + b Top + + +LzCopy7: + +// +// LzCopy7 +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of current flag byte +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// +// This routine is special since it is for the last bit in the flag +// byte. The InputPointer(a2) and OutputPointer(t4) are biased at +// the top of this segment and don't need to be biased again +// +// + + lbu t2,9(a2) // load second byte of copy token + addu t4,t4,7 // mov t4 to point to byte 7 + addu a2,a2,10 // a2 points to next actual src byte + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length (End Address) + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess + +// +// if t4 > Safe end of uncomressed buffer, then jump to the +// safe (slow) routine to do safety check before every load/store +// + + bgt t4,v0,SafeCheckStart // branch to safe-copy setup + +// +// t4 and a2 are alreadt corrected +// jump back tostart of quick loop +// + + b Top + + + +// +// Near the end of either compressed or uncompressed buffers, +// check buffer limits before any load or store +// + +SafeCheckStart: + + beq a2,a3,LzSuccess // check for end of CompressedBuffer + lbu s0,0(a2) // load next flag byte + addu a2,a2,1 // inc src addr to literal/CopyFlag[0] + li t5,8 // loop count + +SafeCheckLoop: + + beq a2,a3,LzSuccess // check for end of CompressedBuffer + beq t4,a1,LzSuccess // check for end of UncompressedBuffer + sll t0,s0,31 // shift flag bit into sign bit + + lbu t1,0(a2) // load literal or CopyToken[0] + addu a2,a2,1 // inc CompressedBuffer adr + bltz t0,LzSafeCopy // if sign bit, go to safe copy routine + + sb t1,0(t4) // store literal byte + addu t4,t4,1 // inc UncompressedBuffer + +SafeCheckReentry: + + srl s0,s0,1 // move next bit into position + addu t5,t5,-1 + bne t5,zero,SafeCheckLoop // check for more bits in flag byte + + b SafeCheckStart // get next flag byte + + +LzSafeCopy: + +// +// LzSafeCopy +// +// t1 - CopyToken[0] +// a2 - CompressedBuffer address of CopyToken[1] +// t4 - UncomressedBuffer address at start of flag byte check +// s0 - Flag byte +// +// load copy token, (first byte already loaded in delay slot), +// then combine into a 16 bit field +// + + + beq a2,a3,LzCompressError // check for end of CompressedBuffer, error case + lbu t2,0(a2) // load second byte of copy token + addu a2,a2,1 // fix-up src addr for return to switch + sll t2,t2,8 // shift second byte into high 16 + or t2,t1,t2 // combine + +// +// Check for a breach of the format boundary. +// + + subu t0,t8,t4 // if t4 < t8 then + bltzal t0,LzAdjustBoundary // branch to boundry adjust and link return + +// +// Extract offset and length from copy token +// + + and t0,t2,t7 // t0 = length from field + addu t0,t0,3 // t0 = real length + srl t1,t2,t6 // t1 = offset + addu t1,t1,1 // t1 = real offset + +// +// Make sure offset doesn't go below start of uncompressed buffer +// + + subu t2,t4,a0 // t2 = current offset into output buffer + bgt t1,t2,LzCompressError // error in compressed data + +// +// check if length will not go up to or beyond actual uncompressed buffer length +// + + addu t2,t4,t0 // CurrentPointer + Length + ble t2,a1,10f // Fix length if it would over-run buffer + + move t2,a1 // new length is end of buffer + +10: + +// +// copy t0 bytes bytes from [t4-t1] to [t4] +// + + subu t3,t4,t1 // t1 = OutputPointer - Offset +20: + lbu t0,0(t3) // load src + sb t0,0(t4) // store to dst + addu t4,t4,1 // inc dst addr + addu t3,t3,1 // inc src addr + bne t4,t2,20b // loop till done + +// +// if t4 = a1, then we are up to the end of the uncompressed buffer. +// return success +// + +30: + beq t4,a1,LzSuccess // Done + + b SafeCheckReentry // Not done yet, continue with flag check + +LzSuccess: + +// +// calculate how many bytes have been moved to the uncompressed +// buffer, then set good return value +// + + lw t0,LzFinal(sp) // address of variable to receive length + subu t1,t4,a0 // bytes stored + sw t1,0(t0) // store length +10: + li v0,STATUS_SUCCESS // STATUS_SUCCESS + +LzComplete: + + lw ra,LzRA(sp) + lw s0,LzS0(sp) + + addu sp,sp,LzFrameLength + + j ra + +// +// fatal error in compressed data format +// + +LzCompressError: + li v0,STATUS_BAD_COMPRESSION_BUFFER + b LzComplete + + +// +// at least one format boundry has been crossed, set up new bouandry +// then jump back to the check routine to make sure new boundry is +// correct +// + +LzAdjustBoundary: + + sll t9,t9,1 // next length boundary + addu t8,t9,a0 // t8 = next offset boundary + srl t7,t7,1 // reduce width of length mask + subu t6,t6,1 // reduce shift count to isolate offset + subu t0,t8,t4 // if t4 < t8 then + bltz t0,LzAdjustBoundary // branch to boundry adjust, re-check + + j ra + + + .end LZNT1DecompressChunk + + diff --git a/private/ntos/rtl/mips/nlssup.c b/private/ntos/rtl/mips/nlssup.c new file mode 100644 index 000000000..4c108a4a7 --- /dev/null +++ b/private/ntos/rtl/mips/nlssup.c @@ -0,0 +1,109 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + nlssup.c + +Abstract: + + This module defines CPU specific routines for converting NLS strings. + +Author: + + Steve Wood (stevewo) 31-Mar-1989 + +Revision History: + + +--*/ + +#include "ntrtlp.h" + +WCHAR +RtlAnsiCharToUnicodeChar( + IN OUT PCHAR *SourceCharacter + ) + +/*++ + +Routine Description: + + This function translates the specified ansi character to unicode and + returns the unicode value. The purpose for this routine is to allow + for character by character ansi to unicode translation. The + translation is done with respect to the current system locale + information. + + +Arguments: + + SourceCharacter - Supplies a pointer to an ansi character pointer. + Through two levels of indirection, this supplies an ansi + character that is to be translated to unicode. After + translation, the ansi character pointer is modified to point to + the next character to be converted. This is done to allow for + dbcs ansi characters. + +Return Value: + + Returns the unicode equivalent of the specified ansi character. + +--*/ + +{ + WCHAR UnicodeCharacter; + + // + // Note that this needs to reference the translation table ! + // + + UnicodeCharacter = **SourceCharacter; + (*SourceCharacter)++; + return UnicodeCharacter; +} + + +VOID +RtlpAnsiPszToUnicodePsz( + IN PCHAR AnsiString, + IN WCHAR *UnicodeString, + IN USHORT AnsiStringLength + ) + +/*++ + +Routine Description: + + This function translates the specified ansi character to unicode and + returns the unicode value. The purpose for this routine is to allow + for character by character ansi to unicode translation. The + translation is done with respect to the current system locale + information. + + +Arguments: + + AnsiString - Supplies a pointer to the ANSI string to convert to unicode. + UnicodeString - Supplies a pointer to a buffer to hold the unicode string + AnsiStringLength - Supplies the length of the ANSI string. + +Return Value: + + None. + +--*/ + +{ + ULONG Index; + PCHAR AnsiChar; + + AnsiChar = AnsiString; + Index = 0; + while(Index < AnsiStringLength ) { + UnicodeString[Index] = RtlAnsiCharToUnicodeChar(&AnsiChar); + Index++; + } + UnicodeString[Index] = UNICODE_NULL; +} diff --git a/private/ntos/rtl/mips/ntrtlmip.h b/private/ntos/rtl/mips/ntrtlmip.h new file mode 100644 index 000000000..cb844325e --- /dev/null +++ b/private/ntos/rtl/mips/ntrtlmip.h @@ -0,0 +1,41 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + ntrtlmip.h + +Abstract: + + MIPS specific parts of ntrtlp.h + +Author: + + David N. Cutler (davec) 19-Apr-90 + +Revision History: + +--*/ + +// +// 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 + ); diff --git a/private/ntos/rtl/mips/stringsp.c b/private/ntos/rtl/mips/stringsp.c new file mode 100644 index 000000000..3cf3f2d5f --- /dev/null +++ b/private/ntos/rtl/mips/stringsp.c @@ -0,0 +1,122 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + stingsup.c + +Abstract: + + This module defines CPU specific routines for manipulating NT strings. + +Author: + + Steve Wood (stevewo) 31-Mar-1989 + +Revision History: + + +--*/ + +#include "nt.h" +#include "ntrtl.h" + + +VOID +RtlInitAnsiString( + OUT PANSI_STRING DestinationString, + IN PSZ SourceString OPTIONAL + ) + +/*++ + +Routine Description: + + The RtlInitAnsiString function initializes an NT counted string. + The DestinationString is initialized to point to the SourceString + and the Length and MaximumLength fields of DestinationString are + initialized to the length of the SourceString, which is zero if + SourceString is not specified. + +Arguments: + + DestinationString - Pointer to the counted string to initialize + + SourceString - Optional pointer to a null terminated string that + the counted string is to point to. + + +Return Value: + + None. + +--*/ + +{ + USHORT Length; + Length = 0; + DestinationString->Length = 0; + DestinationString->Buffer = SourceString; + if (ARGUMENT_PRESENT( SourceString )) { + while (*SourceString++) { + Length++; + } + + DestinationString->Length = Length; + + DestinationString->MaximumLength = (SHORT)(Length+1); + } + else { + DestinationString->MaximumLength = 0; + } +} + + +VOID +RtlInitUnicodeString( + OUT PUNICODE_STRING DestinationString, + IN PWSTR SourceString OPTIONAL + ) + +/*++ + +Routine Description: + + The RtlInitUnicodeString function initializes an NT counted + unicode string. The DestinationString is initialized to point to + the SourceString and the Length and MaximumLength fields of + DestinationString are initialized to the length of the SourceString, + which is zero if SourceString is not specified. + +Arguments: + + DestinationString - Pointer to the counted string to initialize + + SourceString - Optional pointer to a null terminated unicode string that + the counted string is to point to. + + +Return Value: + + None. + +--*/ + +{ + USHORT Length = 0; + DestinationString->Length = 0; + DestinationString->Buffer = SourceString; + if (ARGUMENT_PRESENT( SourceString )) { + while (*SourceString++) { + Length += sizeof(*SourceString); + } + + DestinationString->Length = Length; + + DestinationString->MaximumLength = Length+(USHORT)sizeof(UNICODE_NULL); + } + else { + DestinationString->MaximumLength = 0; + } +} diff --git a/private/ntos/rtl/mips/trampoln.s b/private/ntos/rtl/mips/trampoln.s new file mode 100644 index 000000000..dd5654f11 --- /dev/null +++ b/private/ntos/rtl/mips/trampoln.s @@ -0,0 +1,522 @@ +// TITLE("Trampoline Code For User Mode APC and Exception Dispatching") +//++ +// +// Copyright (c) 1990 Microsoft 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: +// +//-- + +#include "ksmips.h" + +// +// Define length of exception dispatcher stack frame. +// + +#define ExceptionDispatcherFrame (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. +// +//-- + + EXCEPTION_HANDLER(KiUserApcHandler) + + NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero); + + .set noreorder + .set noat + sd sp,CxXIntSp(sp) // save stack pointer + sd ra,CxXIntRa(sp) // save return address + sw ra,CxFir(sp) // save return address + sd s8,CxXIntS8(sp) // save integer register s8 + sd gp,CxXIntGp(sp) // save integer register gp + sd s0,CxXIntS0(sp) // save integer registers s0 - s7 + sd s1,CxXIntS1(sp) // + sd s2,CxXIntS2(sp) // + sd s3,CxXIntS3(sp) // + sd s4,CxXIntS4(sp) // + sd s5,CxXIntS5(sp) // + sd s6,CxXIntS6(sp) // + sd s7,CxXIntS7(sp) // + sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31 + sdc1 f22,CxFltF22(sp) // + sdc1 f24,CxFltF24(sp) // + sdc1 f26,CxFltF26(sp) // + sdc1 f28,CxFltF28(sp) // + sdc1 f30,CxFltF30(sp) // + move s8,sp // 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 provied 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 that address of the function that is to be called. +// +// sp - Supplies a pointer to a context frame. +// +// s8 - Supplies the same value as sp and is used as a frame pointer. +// +// Return Value: +// +// None. +// +//-- + + ALTERNATE_ENTRY(KiUserApcDispatcher) + + jal a3 // call specified APC routine + move a0,s8 // set address of context frame + li a1,1 // set test alert argument true + jal ZwContinue // execute system service to continue + +// +// 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 their is not return from raise +// status. +// + + move s0,v0 // save status value +10: move a0,s0 // set status value + jal RtlRaiseStatus // raise exception + b 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 and when an unwind thought 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 + .space 4 * 4 // argument save area +HdRa: .space 4 // saved return address + .space 3 * 4 // +HandlerFrameLength: // length of handler frame + + NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero) + + subu sp,sp,HandlerFrameLength // allocate stack frame + sw ra,HdRa(sp) // save return address + + PROLOGUE_END + + lw t0,ErExceptionFlags(a0) // get exception flags + and t0,t0,EXCEPTION_UNWIND // check if unwind in progress + beq zero,t0,10f // if eq, no unwind in progress + jal ZwTestAlert // test for alert pending +10: li v0,ExceptionContinueSearch // set disposition value + lw ra,HdRa(sp) // restore return address + addu sp,sp,HandlerFrameLength // deallocate stack frame + j 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 APC dispatcher. +// +//-- + +// EXCEPTION_HANDLER(KiUserCallbackHandler) + + NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero); + + .set noreorder + .set noat + sd sp,CkSp(sp) // save stack pointer + sd ra,CkRa(sp) // save return address + .set at + .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 + ApiNumber) - Supplies the API number of the callback function +// that is executed. +// +// (sp + Buffer) - Supplies a pointer to the input buffer. +// +// (sp + Length) - Supplies the input buffer length. +// +// Return Value: +// +// This function returns to kernel mode. +// +//-- + + + ALTERNATE_ENTRY(KiUserCallbackDispatcher) + + lw a0,CkBuffer(sp) // get input buffer address + lw a1,CkLength(sp) // get input buffer length + lw t0,CkApiNumber(sp) // get API number + li t1,UsPcr // get user PCR page address + lw t1,PcTeb(t1) // get address of TEB + lw t2,TePeb(t1) // get address of PEB + lw t3,PeKernelCallbackTable(t2) // get address of callback table + sll t0,t0,2 // compute address of table entry + addu t3,t3,t0 // + lw t3,0(t3) // get address of callback routine + jal t3 // call specified function + +// +// If a return from the callback function occurs, then the output buffer +// address and length are returned as NULL. +// + + move a0,zero // set zero buffer address + move a1,zero // set zero buffer lenfth + move a2,v0 // set completion status + jal 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 their is not return from raise +// status. +// + +10: move a0,v0 // set status value + jal RtlRaiseStatus // raise exception + b 10b // loop on return + + .end KiUserCallbackDispatch + + SBTTL("User Callback Exception Handler") +//++ +// +// EXCEPTION_DISPOSITION +// KiUserCallbackHandler ( +// 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 a user callback +// routine or one of its dynamic descendents. +// +// 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. +//-- + + NESTED_ENTRY(KiUserCallbackHandler, HandlerFrameLength, zero) + + subu sp,sp,HandlerFrameLength // allocate stack frame + sw ra,HdRa(sp) // save return address + + PROLOGUE_END + + lw t0,ErExceptionFlags(a0) // get exception flags + and t0,t0,EXCEPTION_UNWIND // check if unwind in progress + bne zero,t0,10f // if ne, unwind in progress + li v0,ExceptionContinueSearch // set disposition value + addu sp,sp,HandlerFrameLength // deallocate stack frame + j ra // return + +// +// There is an attempt to unwind through a callback frame. If this were +// allowed, then a kernel callback frame would be abandoned on the kernel +// stack. Force a callback return. +// + +10: lw a2,ErExceptionCode(a0) // get exception code + move a0,zero // set zero buffer address + move a1,zero // set zero buffer lenfth + jal 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. +// + +20: move a0,v0 // set status value + jal RtlRaiseStatus // raise exception + b 20b // loop on return + + .end KiUserCallbackHandler + + SBTTL("User Exception Dispatcher") +//++ +// +// The following code is never executed. Its purpose is to support unwinding +// through the call to the exception dispatcher. +// +//-- + + NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrame, zero); + + .set noreorder + .set noat + sd sp,CxXIntSp(sp) // save stack pointer + sd ra,CxXIntRa(sp) // save return address + sw ra,CxFir(sp) // save return address + sd s8,CxXIntS8(sp) // save integer register s8 + sd gp,CxXIntGp(sp) // save integer register gp + sd s0,CxXIntS0(sp) // save integer registers s0 - s7 + sd s1,CxXIntS1(sp) // + sd s2,CxXIntS2(sp) // + sd s3,CxXIntS3(sp) // + sd s4,CxXIntS4(sp) // + sd s5,CxXIntS5(sp) // + sd s6,CxXIntS6(sp) // + sd s7,CxXIntS7(sp) // + sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31 + sdc1 f22,CxFltF22(sp) // + sdc1 f24,CxFltF24(sp) // + sdc1 f26,CxFltF26(sp) // + sdc1 f28,CxFltF28(sp) // + sdc1 f30,CxFltF30(sp) // + move s8,sp // 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. Else last chance processing is performed. +// +// Arguments: +// +// s0 - Supplies a pointer to an exception record. +// +// s1 - Supplies a pointer to a context frame. +// +// s8 - Supplies the same value as sp and is used as a frame pointer. +// +// Return Value: +// +// None. +// +//-- + + ALTERNATE_ENTRY(KiUserExceptionDispatcher) + + move a0,s0 // set address of exception record + move a1,s1 // set address of context record + jal 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 zero,v0,10f // if eq, perform last chance processing + +// +// Continue execution. +// + + move a0,s1 // set address of context frame + li a1,0 // set test alert argument false + jal ZwContinue // execute system service to continue + b 20f // join common code + +// +// Last chance processing. +// + +10: move a0,s0 // set address of exception record + move a1,s1 // set address of context frame + move a2,zero // set first chance FALSE + jal ZwRaiseException // perform last chance processing + +// +// Common code for nonsuccessful completion of the continue or last chance +// service. Use the return status as the exception code, set noncontinuable +// exception and attempt to raise another exception. Note the stack grows +// and eventually this loop will end. +// + +20: move s1,v0 // save status value +30: subu sp,sp,ExceptionRecordLength + (4 * 4) // allocate exception record + addu a0,sp,4 * 4 // compute address of actual record + sw s1,ErExceptionCode(a0) // set exception code + li t0,EXCEPTION_NONCONTINUABLE // set noncontinuable flag + sw t0,ErExceptionFlags(a0) // + sw s0,ErExceptionRecord(a0) // set associated exception record + sw zero,ErNumberParameters(a0) // set number of parameters + jal RtlRaiseException // raise exception + b 20b // loop of 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. +// +// 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. +// +// Arguments: +// +// v0 - Supplies the status code to be raised. +// +// Return Value: +// +// ExceptionCode +// +//-- + + .struct 0 + .space 4 * 4 // argument save area +RaiseRa:.space 4 // saved return address +RaiseV0:.space 4 // saved S0 +RaiseEr:.space ExceptionRecordLength // exception record for RtlRaiseException +RaiseFrameLength: // length of handler frame + + NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero) + + subu sp,sp,RaiseFrameLength // allocate stack frame + sw ra,RaiseRa(sp) // save return address + + PROLOGUE_END + + sw v0,RaiseV0(sp) // save function return status + sw v0,ErExceptionCode + RaiseEr(sp) // set exception code + sw zero,ErExceptionFlags + RaiseEr(sp) // set exception flags + sw zero,ErExceptionRecord + RaiseEr(sp) // clear exception record + sw ra,ErExceptionAddress + RaiseEr(sp) // set exception address + sw zero,ErNumberParameters+RaiseEr(sp) // set number of parameters + addu a0,sp,RaiseEr // compute exception record address + jal RtlRaiseException // attempt to raise the exception + lw v0,RaiseV0(sp) // restore function status + lw ra,RaiseRa(sp) // restore return address + addu sp,sp,RaiseFrameLength // deallocate stack frame + j ra // return + + .end KiRaiseUserExceptionDispatch diff --git a/private/ntos/rtl/mips/xcptmisc.s b/private/ntos/rtl/mips/xcptmisc.s new file mode 100644 index 000000000..c31a09e24 --- /dev/null +++ b/private/ntos/rtl/mips/xcptmisc.s @@ -0,0 +1,356 @@ +// TITLE("Miscellaneous Exception Handling") +//++ +// +// Copyright (c) 1990 Microsoft 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: +// +//-- + +#include "ksmips.h" + +// +// Define call frame for calling exception handlers. +// + + .struct 0 +CfArg: .space 4 * 4 // argument register save area + .space 3 * 4 // fill for alignment +CfRa: .space 4 // saved return address +CfFrameLength: // length of stack frame +CfA0: .space 4 // caller argument save area +CfA1: .space 4 // +CfA2: .space 4 // +CfA3: .space 4 // +CfExr: .space 4 // address of exception routine + + 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 (4 * 4(sp)) - 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. +// +//-- + + EXCEPTION_HANDLER(RtlpExceptionHandler) + + NESTED_ENTRY(RtlpExecuteHandlerForException, CfFrameLength, zero) + + subu sp,sp,CfFrameLength // allocate stack frame + sw ra,CfRa(sp) // save return address + + PROLOGUE_END + + lw t0,CfExr(sp) // get address of exception routine + sw a3,CfA3(sp) // save address of dispatcher context + jal t0 // call exception exception handler + lw ra,CfRa(sp) // restore return address + addu sp,sp,CfFrameLength // deallocate stack frame + j 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) + + lw t0,ErExceptionFlags(a0) // get exception flags + and t0,t0,EXCEPTION_UNWIND // check if unwind in progress + bne zero,t0,10f // if neq, unwind in progress + +// +// Unwind is not in progress - return nested exception disposition. +// + + lw t0,CfA3 - CfA0(a1) // get dispatcher context address + li v0,ExceptionNestedException // set disposition value + lw t1,DcEstablisherFrame(t0) // copy the establisher frame pointer + sw t1,DcEstablisherFrame(a3) // to current dispatcher context + j ra // return + +// +// Unwind is in progress - return continue search disposition. +// + +10: li v0,ExceptionContinueSearch // set disposition value + j 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 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 (4 * 4(sp)) - 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. +// +//-- + + EXCEPTION_HANDLER(RtlpUnwindHandler) + + NESTED_ENTRY(RtlpExecuteHandlerForUnwind, CfFrameLength, zero) + + subu sp,sp,CfFrameLength // allocate stack frame + sw ra,CfRa(sp) // save return address + + PROLOGUE_END + + lw t0,CfExr(sp) // get address of exception routine + sw a3,CfA3(sp) // save address of dispatcher context + jal t0 // call exception unwind handler + lw ra,CfRa(sp) // restore return address + addu sp,sp,CfFrameLength // deallocate stack frame + j 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) + + lw t0,ErExceptionFlags(a0) // get exception flags + and t0,t0,EXCEPTION_UNWIND // check if unwind in progress + beq zero,t0,10f // if eq, unwind not in progress + +// +// Unwind is in progress - return collided unwind disposition. +// + + lw t0,CfA3 - CfA0(a1) // get dispatcher context address + li v0,ExceptionCollidedUnwind // set disposition value + lw t1,DcControlPc(t0) // Copy the establisher frames' + lw t2,DcFunctionEntry(t0) // dispatcher context to the current + lw t3,DcEstablisherFrame(t0) // dispatcher context + lw t4,DcContextRecord(t0) // + sw t1,DcControlPc(a3) // + sw t2,DcFunctionEntry(a3) // + sw t3,DcEstablisherFrame(a3) // + sw t4,DcContextRecord(a3) // + j ra // return + +// +// Unwind is not in progress - return continue search disposition. +// + +10: li v0,ExceptionContinueSearch // set disposition value + j ra // return + .end RtlpUnwindHandler + + 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) + + li t0,UsPcr // get address of user PCR + bgez sp,10f // if gez, current mode is user + +// +// Current mode is kernel - compute stack limits. +// + + lw t1,KiPcr + PcInitialStack(zero) // get high limit kernel stack + lw t2,KiPcr + PcStackLimit(zero) // get low limit kernel stack + b 20f // finish in commom code + +// +// Current mode is user - get stack limits from the TEB. +// + +10: lw t0,PcTeb(t0) // get address of TEB + lw t2,TeStackLimit(t0) // get low limit of user stack + lw t1,TeStackBase(t0) // get high limit of user stack +20: sw t2,0(a0) // store low stack limit + sw t1,0(a1) // store high stack limit + j ra // return + + .end RtlpGetStackLimits diff --git a/private/ntos/rtl/mips/xxcaptur.s b/private/ntos/rtl/mips/xxcaptur.s new file mode 100644 index 000000000..9adfb4242 --- /dev/null +++ b/private/ntos/rtl/mips/xxcaptur.s @@ -0,0 +1,314 @@ +// TITLE("Capture and Restore Context") +//++ +// +// Copyright (c) 1990 Microsoft 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: +// +//-- + +#include "ksmips.h" + +// +// Define local symbols. +// + +#define UserPsr (1 << PSR_CU1) | (0xff << PSR_INTMASK) | (1 << PSR_UX) | \ + (2 << PSR_KSU) | (1 << PSR_IE) // constant user PSR value + + SBTTL("Capture Context") +//++ +// +// VOID +// RtlCaptureContext ( +// OUT PCONTEXT ContextRecord +// ) +// +// Routine Description: +// +// This function captures the context of the caller in the specified +// context record. +// +// Arguments: +// +// ContextRecord (a0) - Supplies the address of a context record. +// +// Return Value: +// +// None. +// +//-- + + LEAF_ENTRY(RtlCaptureContext) + +// +// Save integer registers zero, at - t9, s8, gp, sp, ra, lo, and hi. +// + + .set noreorder + .set noat + sd zero,CxXIntZero(a0) // + sd AT,CxXIntAt(a0) // + sd v0,CxXIntV0(a0) // + mflo v0 // + sd v1,CxXIntV1(a0) // + mfhi v1 // + sd v0,CxXIntLo(a0) // + sd v1,CxXIntHi(a0) // + sd a0,CxXIntA0(a0) // + sd a1,CxXIntA1(a0) // + sd a2,CxXIntA2(a0) // + sd a3,CxXIntA3(a0) // + sd t0,CxXIntT0(a0) // + sd t1,CxXIntT1(a0) // + sd t2,CxXIntT2(a0) // + sd t3,CxXIntT3(a0) // + sd t4,CxXIntT4(a0) // + sd t5,CxXIntT5(a0) // + sd t6,CxXIntT6(a0) // + sd t7,CxXIntT7(a0) // + sd s0,CxXIntS0(a0) // + sd s1,CxXIntS1(a0) // + sd s2,CxXIntS2(a0) // + sd s3,CxXIntS3(a0) // + sd s4,CxXIntS4(a0) // + sd s5,CxXIntS5(a0) // + sd s6,CxXIntS6(a0) // + sd s7,CxXIntS7(a0) // + sd t8,CxXIntT8(a0) // + sd t9,CxXIntT9(a0) // + sd zero,CxXIntK0(a0) // + sd zero,CxXIntK1(a0) // + sd s8,CxXIntS8(a0) // + sd gp,CxXIntGp(a0) // + sd sp,CxXIntSp(a0) // + sd ra,CxXIntRa(a0) // + +// +// Save floating status and floating registers f0 - f31. +// + + cfc1 v0,fsr // + sdc1 f0,CxFltF0(a0) // + sdc1 f2,CxFltF2(a0) // + sdc1 f4,CxFltF4(a0) // + sdc1 f6,CxFltF6(a0) // + sdc1 f8,CxFltF8(a0) // + sdc1 f10,CxFltF10(a0) // + sdc1 f12,CxFltF12(a0) // + sdc1 f14,CxFltF14(a0) // + sdc1 f16,CxFltF16(a0) // + sdc1 f18,CxFltF18(a0) // + sdc1 f20,CxFltF20(a0) // + sdc1 f22,CxFltF22(a0) // + sdc1 f24,CxFltF24(a0) // + sdc1 f26,CxFltF26(a0) // + sdc1 f28,CxFltF28(a0) // + sdc1 f30,CxFltF30(a0) // + .set at + .set reorder + +// +// Save control information and set context flags. +// + + sw v0,CxFsr(a0) // save floating status + sw ra,CxFir(a0) // set continuation address + li v0,UserPsr // set constant user processor status + bgez sp,10f // if gez, called from user mode + + .set noreorder + .set noat + mfc0 v0,psr // get current processor status + nop // + .set at + .set reorder + +10: sw v0,CxPsr(a0) // set processor status + li t0,CONTEXT_FULL // set context control flags + sw t0,CxContextFlags(a0) // + j ra // return + + .end RtlCaptureContext + + SBTTL("Restore Context") +//++ +// +// VOID +// RtlpRestoreContext ( +// IN PCONTEXT ContextRecord, +// IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL +// ) +// +// Routine Description: +// +// This function restores the context of the caller to the specified +// context. +// +// 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. +// +// ExceptionRecord (a1) - Supplies an optional pointer to an exception +// record. +// +// Return Value: +// +// None. +// +// N.B. There is no return from this routine. +// +//-- + + LEAF_ENTRY(RtlpRestoreContext) + +// +// If an exception record is specified and the exception status is +// STATUS_LONGJUMP, then restore the nonvolatile registers to their +// state at the call to setjmp before restoring the context record. +// + + li t0,STATUS_LONGJUMP // get long jump status code + beq zero,a1,5f // if eq, no exception record + lw t1,ErExceptionCode(a1) // get exception code + lw a2,ErExceptionInformation(a1) // get address of jump buffer + bne t0,t1,5f // if ne, not a long jump + ldc1 f20,JbFltF20(a2) // move floating registers f20 - f31 + ldc1 f22,JbFltF22(a2) // + ldc1 f24,JbFltF24(a2) // + ldc1 f26,JbFltF26(a2) // + ldc1 f28,JbFltF28(a2) // + ldc1 f30,JbFltF30(a2) // + sdc1 f20,CxFltF20(a0) // + sdc1 f22,CxFltF22(a0) // + sdc1 f24,CxFltF24(a0) // + sdc1 f26,CxFltF26(a0) // + sdc1 f28,CxFltF28(a0) // + sdc1 f30,CxFltF30(a0) // + lw s0,JbIntS0(a2) // move integer registers s0 - s8 + lw s1,JbIntS1(a2) // + lw s2,JbIntS2(a2) // + lw s3,JbIntS3(a2) // + lw s4,JbIntS4(a2) // + lw s5,JbIntS5(a2) // + lw s6,JbIntS6(a2) // + lw s7,JbIntS7(a2) // + lw s8,JbIntS8(a2) // + sd s0,CxXIntS0(a0) // + sd s1,CxXIntS1(a0) // + sd s2,CxXIntS2(a0) // + sd s3,CxXIntS3(a0) // + sd s4,CxXIntS4(a0) // + sd s5,CxXIntS5(a0) // + sd s6,CxXIntS6(a0) // + sd s7,CxXIntS7(a0) // + sd s8,CxXIntS8(a0) // + lw v0,JbFir(a2) // move fir and sp + lw v1,JbIntSp(a2) // + sw v0,CxFir(a0) // + sd v0,CxXIntRa(a0) // + sd v1,CxXIntSp(a0) // + +// +// 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. +// + +5: bltz ra,10f // if lt, kernel mode restore + move a1,zero // set test alert argument + jal ZwContinue // continue execution + +// +// Save the address of the context record and contuation address in +// registers t0 and t1. These registers are not restored. +// + +10: move t0,a0 // save context record address + lw t1,CxFir(t0) // get continuation address + +// +// Restore floating status and floating registers f0 - f31. +// + + .set noreorder + .set noat + lw v0,CxFsr(t0) // restore floating status + ctc1 v0,fsr // + ldc1 f0,CxFltF0(t0) // restore floating registers f0 - f31 + ldc1 f2,CxFltF2(t0) // + ldc1 f4,CxFltF4(t0) // + ldc1 f6,CxFltF6(t0) // + ldc1 f8,CxFltF8(t0) // + ldc1 f10,CxFltF10(t0) // + ldc1 f12,CxFltF12(t0) // + ldc1 f14,CxFltF14(t0) // + ldc1 f16,CxFltF16(t0) // + ldc1 f18,CxFltF18(t0) // + ldc1 f20,CxFltF20(t0) // + ldc1 f22,CxFltF22(t0) // + ldc1 f24,CxFltF24(t0) // + ldc1 f26,CxFltF26(t0) // + ldc1 f28,CxFltF28(t0) // + ldc1 f30,CxFltF30(t0) // + +// +// Restore integer registers and continue execution. +// + + ld v0,CxXIntLo(t0) // restore multiply/divide registers + ld v1,CxXIntHi(t0) // + mtlo v0 // + mthi v1 // + ld AT,CxXIntAt(t0) // restore integer registers at - a3 + ld v0,CxXIntV0(t0) // + ld v1,CxXIntV1(t0) // + ld a0,CxXIntA0(t0) // + ld a1,CxXIntA1(t0) // + ld a2,CxXIntA2(t0) // + ld a3,CxXIntA3(t0) // + ld t2,CxXIntT2(t0) // restore integer registers t2 - t7 + ld t3,CxXIntT3(t0) // + ld t4,CxXIntT4(t0) // + ld t5,CxXIntT5(t0) // + ld t6,CxXIntT6(t0) // + ld t7,CxXIntT7(t0) // + ld s0,CxXIntS0(t0) // restore integer registers s0 - s7 + ld s1,CxXIntS1(t0) // + ld s2,CxXIntS2(t0) // + ld s3,CxXIntS3(t0) // + ld s4,CxXIntS4(t0) // + ld s5,CxXIntS5(t0) // + ld s6,CxXIntS6(t0) // + ld s7,CxXIntS7(t0) // + ld t8,CxXIntT8(t0) // restore integer registers t8 and t9 + ld t9,CxXIntT9(t0) // + ld s8,CxXIntS8(t0) // restore integer register s8 + ld gp,CxXIntGp(t0) // restore integer register gp + ld sp,CxXIntSp(t0) // + j t1 // continue execution + ld ra,CxXIntRa(t0) // + .set at + .set reorder + + .end RtlRestoreContext diff --git a/private/ntos/rtl/mips/xxmvmem.s b/private/ntos/rtl/mips/xxmvmem.s new file mode 100644 index 000000000..40e2ddaac --- /dev/null +++ b/private/ntos/rtl/mips/xxmvmem.s @@ -0,0 +1,1440 @@ +// TITLE("Compare, Move, Zero, and Fill Memory Support") +//++ +// +// Copyright (c) 1990 Microsoft Corporation +// +// Module Name: +// +// xxmvmem.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: +// +// David N. Cutler (davec) 11-Apr-1990 +// +// Environment: +// +// User or Kernel mode. +// +// Revision History: +// +//-- + +#include "ksmips.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) + + addu a3,a0,a2 // compute ending address of source1 + move v0,a2 // save length of comparison + and t0,a2,32 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + addu t4,a0,t1 // compute ending block address + beq zero,t1,100f // if eq, no 32-byte block to compare + or t0,a0,a1 // merge and isolate alignment bits + and t0,t0,0x3 // + bne zero,t0,CompareUnaligned // if ne, unalignment comparison + +// +// Compare memory aligned. +// + +CompareAligned: // + + .set noreorder + .set noat +10: lw t0,0(a0) // compare 32-byte block + lw t1,0(a1) // + lw t2,4(a0) // + bne t0,t1,90f // if ne, first word not equal + lw t3,4(a1) // + lw t0,8(a0) // + bne t2,t3,20f // if ne, second word not equal + lw t1,8(a1) // + lw t2,12(a0) // + bne t0,t1,30f // if ne, third word not equal + lw t3,12(a1) // + lw t0,16(a0) // + bne t2,t3,40f // if ne, fourth word not equal + lw t1,16(a1) // + lw t2,20(a0) // + bne t0,t1,50f // if ne, fifth word not equal + lw t3,20(a1) // + lw t0,24(a0) // + bne t2,t3,60f // if ne, sixth word not equal + lw t1,24(a1) // + lw t2,28(a0) // + bne t0,t1,70f // if ne, seventh word not equal + lw t3,28(a1) // + addu a0,a0,32 // advance source1 to next block + bne t2,t3,80f // if ne, eighth word not equal + nop // + bne a0,t4,10b // if ne, more 32-byte blocks to compare + addu a1,a1,32 // update source2 address + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 100f // + +// +// Compare memory unaligned. +// + +CompareUnaligned: // + and t0,a0,0x3 // isolate source1 alignment + bne zero,t0,CompareUnalignedS1 // if ne, source1 unaligned + +// +// Source1 is aligned and Source2 is unaligned. +// + +CompareUnalignedS2: // + + .set noreorder + .set noat +10: lw t0,0(a0) // compare 32-byte block + lwr t1,0(a1) // + lwl t1,3(a1) // + lw t2,4(a0) // + bne t0,t1,90f // if ne, first word not equal + lwr t3,4(a1) // + lwl t3,7(a1) // + lw t0,8(a0) // + bne t2,t3,20f // if ne, second word not equal + lwr t1,8(a1) // + lwl t1,11(a1) // + lw t2,12(a0) // + bne t0,t1,30f // if ne, third word not equal + lwr t3,12(a1) // + lwl t3,15(a1) // + lw t0,16(a0) // + bne t2,t3,40f // if ne, fourth word not equal + lwr t1,16(a1) // + lwl t1,19(a1) // + lw t2,20(a0) // + bne t0,t1,50f // if ne, fifth word not equal + lwr t3,20(a1) // + lwl t3,23(a1) // + lw t0,24(a0) // + bne t2,t3,60f // if ne, sixth word not equal + lwr t1,24(a1) // + lwl t1,27(a1) // + lw t2,28(a0) // + bne t0,t1,70f // if ne, seventh word not equal + lwr t3,28(a1) // + lwl t3,31(a1) // + addu a0,a0,32 // advance source1 to next block + bne t2,t3,80f // if ne, eighth word not equal + nop // + bne a0,t4,10b // if ne, more 32-byte blocks to compare + addu a1,a1,32 // update source2 address + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 100f // + +// +// Source1 is unaligned, check Source2 alignment. +// + +CompareUnalignedS1: // + and t0,a1,0x3 // isolate Source2 alignment + bne zero,t0,CompareUnalignedS1AndS2 // if ne, Source2 unaligned + +// +// Source1 is unaligned and Source2 is aligned. +// + + .set noreorder + .set noat +10: lwr t0,0(a0) // compare 32-byte block + lwl t0,3(a0) // + lw t1,0(a1) // + lwr t2,4(a0) // + lwl t2,7(a0) // + bne t0,t1,90f // if ne, first word not equal + lw t3,4(a1) // + lwr t0,8(a0) // + lwl t0,11(a0) // + bne t2,t3,20f // if ne, second word not equal + lw t1,8(a1) // + lwr t2,12(a0) // + lwl t2,15(a0) // + bne t0,t1,30f // if ne, third word not equal + lw t3,12(a1) // + lwr t0,16(a0) // + lwl t0,19(a0) // + bne t2,t3,40f // if ne, fourth word not equal + lw t1,16(a1) // + lwr t2,20(a0) // + lwl t2,23(a0) // + bne t0,t1,50f // if ne, fifth word not equal + lw t3,20(a1) // + lwr t0,24(a0) // + lwl t0,27(a0) // + bne t2,t3,60f // if ne, sixth word not equal + lw t1,24(a1) // + lwr t2,28(a0) // + lwl t2,31(a0) // + bne t0,t1,70f // if ne, seventh word not equal + lw t3,28(a1) // + addu a0,a0,32 // advance source1 to next block + bne t2,t3,80f // if ne, eighth word not equal + nop // + bne a0,t4,10b // if ne, more 32-byte blocks to compare + addu a1,a1,32 // update source2 address + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 100f // + +// +// Source1 and Source2 are unaligned. +// + +CompareUnalignedS1AndS2: // + + .set noreorder + .set noat +10: lwr t0,0(a0) // compare 32-byte block + lwl t0,3(a0) // + lwr t1,0(a1) // + lwl t1,3(a1) // + lwr t2,4(a0) // + lwl t2,7(a0) // + bne t0,t1,90f // if ne, first word not equal + lwr t3,4(a1) // + lwl t3,7(a1) // + lwr t0,8(a0) // + lwl t0,11(a0) // + bne t2,t3,20f // if ne, second word not equal + lwr t1,8(a1) // + lwl t1,11(a1) // + lwr t2,12(a0) // + lwl t2,15(a0) // + bne t0,t1,30f // if ne, third word not equal + lwr t3,12(a1) // + lwl t3,15(a1) // + lwr t0,16(a0) // + lwl t0,19(a0) // + bne t2,t3,40f // if ne, fourth word not equal + lwr t1,16(a1) // + lwl t1,19(a1) // + lwr t2,20(a0) // + lwl t2,23(a0) // + bne t0,t1,50f // if ne, fifth word not equal + lwr t3,20(a1) // + lwl t3,23(a1) // + lwr t0,24(a0) // + lwl t0,27(a0) // + bne t2,t3,60f // if ne, sixth word not equal + lwr t1,24(a1) // + lwl t1,27(a1) // + lwr t2,28(a0) // + lwl t2,31(a0) // + bne t0,t1,70f // if ne, seventh word not equal + lwr t3,28(a1) // + lwl t3,31(a1) // + addu a0,a0,32 // advance source1 to next block + bne t2,t3,80f // if ne, eighth word not equal + nop // + bne a0,t4,10b // if ne, more 32-byte blocks to compare + addu a1,a1,32 // update source2 address + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 100f // + +// +// Adjust source1 and source2 pointers dependent on position of miscompare in +// block. +// + +20: addu a0,a0,4 // mismatch on second word + addu a1,a1,4 // + b 90f // + +30: addu a0,a0,8 // mismatch on third word + addu a1,a1,8 // + b 90f // + +40: addu a0,a0,12 // mistmatch on fourth word + addu a1,a1,12 // + b 90f // + +50: addu a0,a0,16 // mismatch on fifth word + addu a1,a1,16 // + b 90f // + +60: addu a0,a0,20 // mismatch on sixth word + addu a1,a1,20 // + b 90f // + +70: addu a0,a0,24 // mismatch on seventh word + addu a1,a1,24 // + b 90f // + +80: subu a0,a0,4 // mismatch on eighth word + addu a1,a1,28 // +90: subu a2,a3,a0 // compute remaining bytes + +// +// Compare 1-byte blocks. +// + +100: addu t2,a0,a2 // compute ending block address + beq zero,a2,120f // if eq, no bytes to zero +110: lb t0,0(a0) // compare 1-byte block + lb t1,0(a1) // + addu a1,a1,1 // advance pointers to next block + bne t0,t1,120f // if ne, byte not equal + addu a0,a0,1 // + bne a0,t2,110b // if ne, more 1-byte block to zero + +120: subu t0,a3,a0 // compute number of bytes not compared + subu v0,v0,t0 // compute number of byte that matched + j ra // return + + .end RtlCompareMemory + + SBTTL("Equal Memory") +//++ +// +// ULONG +// RtlEqualMemory ( +// IN PVOID Source1, +// IN PVOID Source2, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function compares two blocks of memory for equality. +// +// 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: +// +// If all bytes in the source strings match, then a value of TRUE is +// returned. Otherwise, FALSE is returned. +// +//-- + + LEAF_ENTRY(RtlEqualMemory) + + li v0,FALSE // set return value FALSE + addu a3,a0,a2 // compute ending address of source1 + and t0,a2,16 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + addu t4,a0,t1 // compute ending block address + beq zero,t1,20f // if eq, no 16-byte block to compare + or t0,a0,a1 // merge and isolate alignment bits + and t0,t0,0x3 // + bne zero,t0,EqualUnaligned // if ne, unalignment comparison + +// +// Compare memory aligned. +// + +EqualAligned: // + + .set noreorder + .set noat +10: lw t0,0(a0) // compare 16-byte block + lw t1,0(a1) // + lw t2,4(a0) // + bne t0,t1,50f // if ne, first word not equal + lw t3,4(a1) // + lw t0,8(a0) // + bne t2,t3,50f // if ne, second word not equal + lw t1,8(a1) // + lw t2,12(a0) // + bne t0,t1,50f // if ne, third word not equal + lw t3,12(a1) // + bne t2,t3,50f // if ne, eighth word not equal + addu a0,a0,16 // advance source1 to next block + bne a0,t4,10b // if ne, more blocks to compare + addu a1,a1,16 // advance source2 to next block + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 20f // + +// +// Compare memory unaligned. +// + +EqualUnaligned: // + and t0,a0,0x3 // isolate source1 alignment + bne zero,t0,EqualUnalignedS1 // if ne, source1 unaligned + +// +// Source1 is aligned and Source2 is unaligned. +// + +EqualUnalignedS2: // + + .set noreorder + .set noat +10: lw t0,0(a0) // compare 16-byte block + lwr t1,0(a1) // + lwl t1,3(a1) // + lw t2,4(a0) // + bne t0,t1,50f // if ne, first word not equal + lwr t3,4(a1) // + lwl t3,7(a1) // + lw t0,8(a0) // + bne t2,t3,50f // if ne, second word not equal + lwr t1,8(a1) // + lwl t1,11(a1) // + lw t2,12(a0) // + bne t0,t1,50f // if ne, third word not equal + lwr t3,12(a1) // + lwl t3,15(a1) // + bne t2,t3,50f // if ne, fourth word not equal + addu a0,a0,16 // advance source1 to next block + bne a0,t4,10b // if ne, more blocks to compare + addu a1,a1,16 // advance source2 to next block + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 20f // + +// +// Source1 is unaligned, check Source2 alignment. +// + +EqualUnalignedS1: // + and t0,a1,0x3 // isolate Source2 alignment + bne zero,t0,EqualUnalignedS1AndS2 // if ne, Source2 unaligned + +// +// Source1 is unaligned and Source2 is aligned. +// + + .set noreorder + .set noat +10: lwr t0,0(a0) // compare 16-byte block + lwl t0,3(a0) // + lw t1,0(a1) // + lwr t2,4(a0) // + lwl t2,7(a0) // + bne t0,t1,50f // if ne, first word not equal + lw t3,4(a1) // + lwr t0,8(a0) // + lwl t0,11(a0) // + bne t2,t3,50f // if ne, second word not equal + lw t1,8(a1) // + lwr t2,12(a0) // + lwl t2,15(a0) // + bne t0,t1,50f // if ne, third word not equal + lw t3,12(a1) // + bne t2,t3,50f // if ne, fourth word not equal + addu a0,a0,16 // advance source1 to next block + bne a0,t4,10b // if ne, more blocks to compare + addu a1,a1,16 // advance source2 to next block + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + b 20f // + +// +// Source1 and Source2 are unaligned. +// + +EqualUnalignedS1AndS2: // + + .set noreorder + .set noat +10: lwr t0,0(a0) // compare 16-byte block + lwl t0,3(a0) // + lwr t1,0(a1) // + lwl t1,3(a1) // + lwr t2,4(a0) // + lwl t2,7(a0) // + bne t0,t1,50f // if ne, first word not equal + lwr t3,4(a1) // + lwl t3,7(a1) // + lwr t0,8(a0) // + lwl t0,11(a0) // + bne t2,t3,50f // if ne, second word not equal + lwr t1,8(a1) // + lwl t1,11(a1) // + lwr t2,12(a0) // + lwl t2,15(a0) // + bne t0,t1,50f // if ne, third word not equal + lwr t3,12(a1) // + lwl t3,15(a1) // + bne t2,t3,50f // if ne, fourth word not equal + addu a0,a0,16 // advance source1 to next block + bne a0,t4,10b // if ne, more blocks to compare + addu a1,a1,16 // advance source2 to next block + .set at + .set reorder + + subu a2,a3,a0 // compute remaining bytes + +// +// Compare 1-byte blocks. +// + +20: addu t2,a0,a2 // compute ending block address + beq zero,a2,40f // if eq, no bytes to zero +30: lb t0,0(a0) // compare 1-byte block + lb t1,0(a1) // + addu a1,a1,1 // advance pointers to next block + bne t0,t1,50f // if ne, byte not equal + addu a0,a0,1 // + bne a0,t2,30b // if ne, more 1-byte block to zero +40: li v0,TRUE // set return value TRUE + +50: j ra // return + + .end RtlEqualMemory + + SBTTL("Move Memory") +//++ +// +// PVOID +// RtlMoveMemory ( +// IN PVOID Destination, +// IN PVOID Source, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function moves memory either forward or backward, aligned or +// unaligned, in 32-byte blocks, followed by 4-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: +// +// The Destination address is returned as the function value. +// +// N.B. The C runtime entry points memmove and memcpy are equivalent to +// RtlMoveMemory htus alternate entry points are provided for these +// routines. +//-- + + LEAF_ENTRY(RtlMoveMemory) + + ALTERNATE_ENTRY(memcpy) + ALTERNATE_ENTRY(memmove) + + move v0,a0 // set return value + +// +// 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. +// + +10: bgeu a1,a0,MoveForward // if geu, no overlap possible + addu t0,a1,a2 // compute source ending address + bgtu t0,a0,MoveBackward // if gtu, source and destination overlap + +// +// Move memory forward aligned and unaligned. +// + +MoveForward: // + sltu t0,a2,8 // check if less than eight bytes + bne zero,t0,50f // if ne, less than eight bytes to move + xor t0,a0,a1 // compare alignment bits + and t0,t0,0x7 // isolate alignment comparison + bne zero,t0,MoveForwardUnaligned // if ne, incompatible alignment + +// +// Move memory forward aligned. +// + +MoveForwardAligned: // + subu t0,zero,a0 // compute bytes until aligned + and t0,t0,0x7 // isolate residual byte count + subu a2,a2,t0 // reduce number of bytes to move + beq zero,t0,10f // if eq, already aligned + ldr t1,0(a1) // move unaligned bytes + sdr t1,0(a0) // + addu a0,a0,t0 // align destination address + addu a1,a1,t0 // align source address + +// +// Check for 32-byte blocks to move. +// + +10: and t0,a2,32 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + addu t8,a0,t1 // compute ending block address + beq zero,t1,30f // if eq, no 32-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Check for odd number of 32-byte blocks to move. +// + + and t0,t1,1 << 5 // test if even number of 32-byte blocks + beq zero,t0,20f // if eq, even number of 32-byte blocks + +// +// Move one 32-byte block quadword aligned. +// + + .set noreorder + ld t0,0(a1) // move 32-byte block + ld t1,8(a1) // + ld t2,16(a1) // + ld t3,24(a1) // + sd t0,0(a0) // + sd t1,8(a0) // + sd t2,16(a0) // + sd t3,24(a0) // + addu a0,a0,32 // advance pointers to next block + beq a0,t8,30f // if eq, end of block + addu a1,a1,32 // + .set reorder + +// +// Move 64-byte blocks quadword aligned. +// + + .set noreorder +20: ld t0,0(a1) // move 64-byte block + ld t1,8(a1) // + ld t2,16(a1) // + ld t3,24(a1) // + ld t4,32(a1) // + ld t5,40(a1) // + ld t6,48(a1) // + ld t7,56(a1) // + sd t0,0(a0) // + sd t1,8(a0) // + sd t2,16(a0) // + sd t3,24(a0) // + sd t4,32(a0) // + sd t5,40(a0) // + sd t6,48(a0) // + sd t7,56(a0) // + addu a0,a0,64 // advance pointers to next block + bne a0,t8,20b // if ne, more 64-byte blocks to zero + addu a1,a1,64 // + .set reorder + +// +// Check for 4-byte blocks to move. +// + +30: and t0,a2,4 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + addu t2,a0,t1 // compute ending block address + beq zero,t1,50f // if eq, no 4-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Move 4-byte block. +// + + .set noreorder +40: lw t0,0(a1) // move 4-byte block + addu a0,a0,4 // advance pointers to next block + sw t0,-4(a0) // + bne a0,t2,40b // if ne, more 4-byte blocks to zero + addu a1,a1,4 // + .set reorder + +// +// Move 1-byte blocks. +// + +50: addu t2,a0,a2 // compute ending block address + beq zero,a2,70f // if eq, no bytes to zero + + .set noreorder +60: lb t0,0(a1) // move 1-byte block + addu a0,a0,1 // advance pointers to next block + sb t0,-1(a0) // + bne a0,t2,60b // if ne, more 1-byte block to zero + addu a1,a1,1 // + .set reorder + +70: j ra // return + +// +// Move memory forward unaligned. +// + +MoveForwardUnaligned: // + subu t0,zero,a0 // compute bytes until aligned + and t0,t0,0x7 // isolate residual byte count + subu a2,a2,t0 // reduce number of bytes to move + beq zero,t0,10f // if eq, already aligned + ldr t1,0(a1) // move unaligned bytes + ldl t1,7(a1) // + sdr t1,0(a0) // + addu a0,a0,t0 // align destination address + addu a1,a1,t0 // update source address + +// +// Check for 32-byte blocks to move. +// + +10: and t0,a2,32 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + addu t8,a0,t1 // compute ending block address + beq zero,t1,30f // if eq, no 32-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Check for odd number of 32-byte blocks to move. +// + + and t0,t1,1 << 5 // test if even number of 32-byte blocks + beq zero,t0,20f // if eq, even number of 32-byte blocks + +// +// Move one 32-byte block quadword aligned. +// + + .set noreorder + ldr t0,0(a1) // move 32-byte block + ldl t0,7(a1) // + ldr t1,8(a1) // + ldl t1,15(a1) // + ldr t2,16(a1) // + ldl t2,23(a1) // + ldr t3,24(a1) // + ldl t3,31(a1) // + sd t0,0(a0) // + sd t1,8(a0) // + sd t2,16(a0) // + sd t3,24(a0) // + addu a0,a0,32 // advance pointers to next block + beq a0,t8,30f // if eq, end of block + addu a1,a1,32 // + .set reorder + +// +// Move 64-byte block. +// + + .set noreorder +20: ldr t0,0(a1) // move 64-byte block + ldl t0,7(a1) // + ldr t1,8(a1) // + ldl t1,15(a1) // + ldr t2,16(a1) // + ldl t2,23(a1) // + ldr t3,24(a1) // + ldl t3,31(a1) // + ldr t4,32(a1) // + ldl t4,39(a1) // + ldr t5,40(a1) // + ldl t5,47(a1) // + ldr t6,48(a1) // + ldl t6,55(a1) // + ldr t7,56(a1) // + ldl t7,63(a1) // + sd t0,0(a0) // + sd t1,8(a0) // + sd t2,16(a0) // + sd t3,24(a0) // + sd t4,32(a0) // + sd t5,40(a0) // + sd t6,48(a0) // + sd t7,56(a0) // + addu a0,a0,64 // advance pointers to next block + bne a0,t8,20b // if ne, more 32-byte blocks to zero + addu a1,a1,64 // + .set reorder + +// +// Check for 4-byte blocks to move. +// + +30: and t0,a2,4 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + addu t2,a0,t1 // compute ending block address + beq zero,t1,50f // if eq, no 4-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Move 4-byte block. +// + + .set noreorder +40: lwr t0,0(a1) // move 4-byte block + lwl t0,3(a1) // + addu a0,a0,4 // advance pointers to next block + sw t0,-4(a0) // + bne a0,t2,40b // if ne, more 4-byte blocks to zero + addu a1,a1,4 // + .set reorder + +// +// Move 1-byte blocks. +// + +50: addu t2,a0,a2 // compute ending block address + beq zero,a2,70f // if eq, no bytes to zero + + .set noreorder +60: lb t0,0(a1) // move 1-byte block + addu a0,a0,1 // advance pointers to next block + sb t0,-1(a0) // + bne a0,t2,60b // if ne, more 1-byte block to zero + addu a1,a1,1 // + .set reorder + +70: j ra // return + +// +// Move memory backward. +// + +MoveBackward: // + addu a0,a0,a2 // compute ending destination address + addu a1,a1,a2 // compute ending source address + sltu t0,a2,8 // check if less than eight bytes + bne zero,t0,50f // if ne, less than eight bytes to move + xor t0,a0,a1 // compare alignment bits + and t0,t0,0x7 // isolate alignment comparison + bne zero,t0,MoveBackwardUnaligned // if ne, incompatible alignment + +// +// Move memory backward aligned. +// + +MoveBackwardAligned: // + and t0,a0,0x7 // isolate residual byte count + subu a2,a2,t0 // reduce number of bytes to move + beq zero,t0,10f // if eq, already aligned + ldl t1,-1(a1) // move unaligned bytes + sdl t1,-1(a0) // + subu a0,a0,t0 // align destination address + subu a1,a1,t0 // align source address + +// +// Check for 32-byte blocks to move. +// + +10: and t0,a2,32 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + subu t8,a0,t1 // compute ending block address + beq zero,t1,30f // if eq, no 32-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Check for odd number of 32-byte blocks to move. +// + + and t0,t1,1 << 5 // test if even number of 32-byte blocks + beq zero,t0,20f // if eq, even number of 32-byte blocks + +// +// Move one 32-byte block quadword aligned. +// + + .set noreorder + ld t0,-8(a1) // move 32-byte block + ld t1,-16(a1) // + ld t2,-24(a1) // + ld t3,-32(a1) // + sd t0,-8(a0) // + sd t1,-16(a0) // + sd t2,-24(a0) // + sd t3,-32(a0) // + subu a0,a0,32 // advance pointers to next block + beq a0,t8,30f // if eq, end of block + subu a1,a1,32 // + .set reorder + +// +// Move 64-byte blocks quadword aligned. +// + + .set noreorder +20: ld t0,-8(a1) // move 64-byte block + ld t1,-16(a1) // + ld t2,-24(a1) // + ld t3,-32(a1) // + ld t4,-40(a1) // + ld t5,-48(a1) // + ld t6,-56(a1) // + ld t7,-64(a1) // + sd t0,-8(a0) // + sd t1,-16(a0) // + sd t2,-24(a0) // + sd t3,-32(a0) // + sd t4,-40(a0) // + sd t5,-48(a0) // + sd t6,-56(a0) // + sd t7,-64(a0) // + subu a0,a0,64 // advance pointers to next block + bne a0,t8,20b // if ne, more 64-byte blocks to zero + subu a1,a1,64 // + .set reorder + +// +// Check for 4-byte blocks to move. +// + +30: and t0,a2,4 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + subu t2,a0,t1 // compute ending block address + beq zero,t1,50f // if eq, no 4-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Move 4-byte block. +// + + .set noreorder +40: lw t0,-4(a1) // move 4-byte block + subu a0,a0,4 // advance pointers to next block + sw t0,0(a0) // + bne a0,t2,40b // if ne, more 4-byte blocks to zero + subu a1,a1,4 // + .set reorder + +// +// Move 1-byte blocks. +// + +50: subu t2,a0,a2 // compute ending block address + beq zero,a2,70f // if eq, no bytes to zero + + .set noreorder +60: lb t0,-1(a1) // move 1-byte block + subu a0,a0,1 // advance pointers to next block + sb t0,0(a0) // + bne a0,t2,60b // if ne, more 1-byte block to zero + subu a1,a1,1 // + .set reorder + +70: j ra // return + +// +// Move memory backward unaligned. +// + +MoveBackwardUnaligned: // + and t0,a0,0x7 // isolate residual byte count + subu a2,a2,t0 // reduce number of bytes to move + beq zero,t0,10f // if eq, already aligned + ldl t1,-1(a1) // move unaligned bytes + ldr t1,-8(a1) // + sdl t1,-1(a0) // + subu a0,a0,t0 // align destination address + subu a1,a1,t0 // update source address + +// +// Check for 32-byte blocks to move. +// + +10: and t0,a2,32 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + subu t8,a0,t1 // compute ending block address + beq zero,t1,30f // if eq, no 32-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Check for odd number of 32-byte blocks to move. +// + + and t0,t1,1 << 5 // test if even number of 32-byte blocks + beq zero,t0,20f // if eq, even number of 32-byte blocks + +// +// Move one 32-byte block. +// + + .set noreorder + ldr t0,-8(a1) // move 32-byte block + ldl t0,-1(a1) // + ldr t1,-16(a1) // + ldl t1,-9(a1) // + ldr t2,-24(a1) // + ldl t2,-17(a1) // + ldr t3,-32(a1) // + ldl t3,-25(a1) // + sd t0,-8(a0) // + sd t1,-16(a0) // + sd t2,-24(a0) // + sd t3,-32(a0) // + subu a0,a0,32 // advance pointers to next block + beq a0,t8,30f // if eq, end of block + subu a1,a1,32 // + .set reorder + +// +// Move 32-byte block. +// + + .set noreorder +20: ldr t0,-8(a1) // move 32-byte block + ldl t0,-1(a1) // + ldr t1,-16(a1) // + ldl t1,-9(a1) // + ldr t2,-24(a1) // + ldl t2,-17(a1) // + ldr t3,-32(a1) // + ldl t3,-25(a1) // + ldr t4,-40(a1) // + ldl t4,-33(a1) // + ldr t5,-48(a1) // + ldl t5,-41(a1) // + ldr t6,-56(a1) // + ldl t6,-49(a1) // + ldr t7,-64(a1) // + ldl t7,-57(a1) // + sd t0,-8(a0) // + sd t1,-16(a0) // + sd t2,-24(a0) // + sd t3,-32(a0) // + sd t4,-40(a0) // + sd t5,-48(a0) // + sd t6,-56(a0) // + sd t7,-64(a0) // + subu a0,a0,64 // advance pointers to next block + bne a0,t8,20b // if ne, more 64-byte blocks to zero + subu a1,a1,64 // + .set reorder + +// +// Check for 4-byte blocks to move. +// + +30: and t0,a2,4 - 1 // isolate residual bytes + subu t1,a2,t0 // subtract out residual bytes + subu t2,a0,t1 // compute ending block address + beq zero,t1,50f // if eq, no 4-byte block to zero + move a2,t0 // set residual number of bytes + +// +// Move 4-byte block. +// + + .set noreorder +40: lwr t0,-4(a1) // move 4-byte block + lwl t0,-1(a1) // + subu a0,a0,4 // advance pointers to next block + sw t0,0(a0) // + bne a0,t2,40b // if ne, more 4-byte blocks to zero + subu a1,a1,4 // + .set reorder + +// +// Move 1-byte blocks. +// + +50: subu t2,a0,a2 // compute ending block address + beq zero,a2,70f // if eq, no bytes to zero + + .set noreorder +60: lb t0,-1(a1) // move 1-byte block + subu a0,a0,1 // advance pointers to next block + sb t0,0(a0) // + bne a0,t2,60b // if ne, more 1-byte block to zero + subu a1,a1,1 // + .set reorder + +70: j ra // return + + .end RtlMoveMemory + + SBTTL("Zero Memory") +//++ +// +// PVOID +// RtlZeroMemory ( +// IN PVOID Destination, +// IN ULONG Length +// ) +// +// Routine Description: +// +// This function zeros memory by first aligning the destination address to +// a longword boundary, and then zeroing 32-byte blocks, followed by 4-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: +// +// The destination address is returned as the function value. +// +//-- + + LEAF_ENTRY(RtlZeroMemory) + + move a2,zero // set fill pattern + b RtlpFillMemory // + + + SBTTL("Fill Memory") +//++ +// +// PVOID +// 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. +// +// Return Value: +// +// The destination address is returned as the function value. +// +//-- + + ALTERNATE_ENTRY(memset) + + move a3,a1 // swap length and fill arguments + move a1,a2 // + move a2,a3 // + + ALTERNATE_ENTRY(RtlFillMemory) + + and a2,a2,0xff // clear excess bits + sll t0,a2,8 // duplicate fill byte + or a2,a2,t0 // generate fill word + sll t0,a2,16 // duplicate fill word + or a2,a2,t0 // generate fill longword + +// +// Fill memory with the pattern specified in register a2. +// + +RtlpFillMemory: // + move v0,a0 // set return value + dsll a2,a2,32 // duplicate pattern to 64-bits + dsrl t0,a2,32 // + or a2,a2,t0 // + subu t0,zero,a0 // compute bytes until aligned + and t0,t0,0x7 // isolate residual byte count + subu t1,a1,t0 // reduce number of bytes to fill + blez t1,60f // if lez, less than 8 bytes to fill + move a1,t1 // set number of bytes to fill + beq zero,t0,10f // if eq, already aligned + sdr a2,0(a0) // fill unaligned bytes + addu a0,a0,t0 // align destination address + +// +// Check for 32-byte blocks to fill. +// + +10: and t0,a1,32 - 1 // isolate residual bytes + subu t1,a1,t0 // subtract out residual bytes + addu t2,a0,t1 // compute ending block address + beq zero,t1,40f // if eq, no 32-byte blocks to fill + move a1,t0 // set residual number of bytes + +// +// Fill 32-byte blocks. +// + + and t0,a0,1 << 2 // check if destintion quadword aligned + beq zero,t0,20f // if eq, yes + sw a2,0(a0) // store destination longword + addu a0,a0,4 // align destination address + addu a1,a1,t1 // recompute bytes to fill + subu a1,a1,4 // reduce count by 4 + b 10b // + +// +// The destination is quadword aligned. +// + +20: and t0,t1,1 << 5 // test if even number of 32-byte blocks + beq zero,t0,30f // if eq, even number of 32-byte blocks + +// +// Fill one 32-byte block. +// + + .set noreorder + .set noat + sd a2,0(a0) // fill 32-byte block + sd a2,8(a0) // + sd a2,16(a0) // + addu a0,a0,32 // advance pointer to next block + beq a0,t2,40f // if ne, no 64-byte blocks to fill + sd a2,-8(a0) // + .set at + .set reorder + +// +// Fill 64-byte block. +// + + .set noreorder + .set noat +30: sd a2,0(a0) // fill 32-byte block + sd a2,8(a0) // + sd a2,16(a0) // + sd a2,24(a0) // + sd a2,32(a0) // + sd a2,40(a0) // + sd a2,48(a0) // + addu a0,a0,64 // advance pointer to next block + bne a0,t2,30b // if ne, more 32-byte blocks to fill + sd a2,-8(a0) // + .set at + .set reorder + +// +// Check for 4-byte blocks to fill. +// + +40: and t0,a1,4 - 1 // isolate residual bytes + subu t1,a1,t0 // subtract out residual bytes + addu t2,a0,t1 // compute ending block address + beq zero,t1,60f // if eq, no 4-byte block to fill + move a1,t0 // set residual number of bytes + +// +// Fill 4-byte blocks. +// + + .set noreorder +50: addu a0,a0,4 // advance pointer to next block + bne a0,t2,50b // if ne, more 4-byte blocks to fill + sw a2,-4(a0) // fill 4-byte block + .set reorder + +// +// Check for 1-byte blocks to fill. +// + +60: addu t2,a0,a1 // compute ending block address + beq zero,a1,80f // if eq, no bytes to fill + +// +// Fill 1-byte blocks. +// + + .set noreorder +70: addu a0,a0,1 // advance pointer to next block + bne a0,t2,70b // if ne, more 1-byte block to fill + sb a2,-1(a0) // fill 1-byte block + .set reorder + +80: j ra // return + + .end RtlZeroMemory + + SBTTL("Fill Memory Ulong") +//++ +// +// PVOID +// RtlFillMemoryUlong ( +// IN PVOID Destination, +// IN ULONG Length, +// IN ULONG Pattern +// ) +// +// Routine Description: +// +// This function fills memory with the specified longowrd pattern by +// filling 32-byte blocks followed by 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: +// +// The destination address is returned as the function value. +// +//-- + + LEAF_ENTRY(RtlFillMemoryUlong) + + move v0,a0 // set function value + srl a1,a1,2 // make sure length is an even number + sll a1,a1,2 // of longwords + dsll a2,a2,32 // duplicate pattern to 64-bits + dsrl t0,a2,32 // + or a2,a2,t0 // + +// +// Check for 32-byte blocks to fill. +// + +10: and t0,a1,32 - 1 // isolate residual bytes + subu t1,a1,t0 // subtract out residual bytes + addu t2,a0,t1 // compute ending block address + beq zero,t1,40f // if eq, no 32-byte blocks to fill + move a1,t0 // set residual number of bytes + +// +// Fill 32-byte blocks. +// + + and t0,a0,1 << 2 // check if destintion quadword aligned + beq zero,t0,20f // if eq, yes + sw a2,0(a0) // store destination longword + addu a0,a0,4 // align destination address + addu a1,a1,t1 // recompute bytes to fill + subu a1,a1,4 // reduce count by 4 + b 10b // + +// +// The destination is quadword aligned. +// + +20: and t0,t1,1 << 5 // test if even number of 32-byte blocks + beq zero,t0,30f // if eq, even number of 32-byte blocks + +// +// Fill one 32-byte block. +// + + .set noreorder + .set noat + sd a2,0(a0) // fill 32-byte block + sd a2,8(a0) // + sd a2,16(a0) // + addu a0,a0,32 // advance pointer to next block + beq a0,t2,40f // if ne, no 64-byte blocks to fill + sd a2,-8(a0) // + .set at + .set reorder + +// +// Fill 64-byte block. +// + + .set noreorder + .set noat +30: sd a2,0(a0) // fill 32-byte block + sd a2,8(a0) // + sd a2,16(a0) // + sd a2,24(a0) // + sd a2,32(a0) // + sd a2,40(a0) // + sd a2,48(a0) // + addu a0,a0,64 // advance pointer to next block + bne a0,t2,30b // if ne, more 32-byte blocks to fill + sd a2,-8(a0) // + .set at + .set reorder + +// +// Check for 4-byte blocks to fill. +// + +40: addu t2,a1,a0 // compute ending block address + beq zero,a1,60f // if eq, no 4-byte block to fill + +// +// Fill 4-byte blocks. +// + + .set noreorder +50: addu a0,a0,4 // advance pointer to next block + bne a0,t2,50b // if ne, more 4-byte blocks to fill + sw a2,-4(a0) // fill 4-byte block + .set reorder + +60: j ra // return + + .end RtlFillMemoryUlong |