summaryrefslogtreecommitdiffstats
path: root/private/ntos/rtl
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/rtl
downloadNT4.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')
-rw-r--r--private/ntos/rtl/acledit.c1970
-rw-r--r--private/ntos/rtl/alpha/capture.s310
-rw-r--r--private/ntos/rtl/alpha/chandler.c297
-rw-r--r--private/ntos/rtl/alpha/chkstk.s131
-rw-r--r--private/ntos/rtl/alpha/context.c330
-rw-r--r--private/ntos/rtl/alpha/debugstb.s269
-rw-r--r--private/ntos/rtl/alpha/exdsptch.c2335
-rw-r--r--private/ntos/rtl/alpha/getcalr.c189
-rw-r--r--private/ntos/rtl/alpha/ghandler.c436
-rw-r--r--private/ntos/rtl/alpha/largeint.s999
-rw-r--r--private/ntos/rtl/alpha/localrtl.c160
-rw-r--r--private/ntos/rtl/alpha/localrtl.h82
-rw-r--r--private/ntos/rtl/alpha/longjmp.s200
-rw-r--r--private/ntos/rtl/alpha/lzntaxp.s485
-rw-r--r--private/ntos/rtl/alpha/mvmem.s1920
-rw-r--r--private/ntos/rtl/alpha/ntcurteb.s57
-rw-r--r--private/ntos/rtl/alpha/ntrtlalp.h123
-rw-r--r--private/ntos/rtl/alpha/setjmp.s148
-rw-r--r--private/ntos/rtl/alpha/tcmpmem.c105
-rw-r--r--private/ntos/rtl/alpha/tfilmem.c79
-rw-r--r--private/ntos/rtl/alpha/tmovmem.c129
-rw-r--r--private/ntos/rtl/alpha/trampoln.s524
-rw-r--r--private/ntos/rtl/alpha/tzermem.c79
-rw-r--r--private/ntos/rtl/alpha/unwindr.c871
-rw-r--r--private/ntos/rtl/alpha/xcptmisc.s501
-rw-r--r--private/ntos/rtl/assert.c82
-rw-r--r--private/ntos/rtl/atom.c911
-rw-r--r--private/ntos/rtl/bitmap.c2975
-rw-r--r--private/ntos/rtl/boot/alpha/sources5
-rw-r--r--private/ntos/rtl/boot/i386/sources9
-rw-r--r--private/ntos/rtl/boot/makefile6
-rw-r--r--private/ntos/rtl/boot/mips/sources4
-rw-r--r--private/ntos/rtl/boot/ppc/sources4
-rw-r--r--private/ntos/rtl/boot/sources49
-rw-r--r--private/ntos/rtl/checksum.c178
-rw-r--r--private/ntos/rtl/cnvint.c455
-rw-r--r--private/ntos/rtl/compress.c1257
-rw-r--r--private/ntos/rtl/debug.c215
-rw-r--r--private/ntos/rtl/dirs27
-rw-r--r--private/ntos/rtl/eballoc.c59
-rw-r--r--private/ntos/rtl/environ.c839
-rw-r--r--private/ntos/rtl/error.c189
-rw-r--r--private/ntos/rtl/eventlog.c914
-rw-r--r--private/ntos/rtl/excptdbg.c171
-rw-r--r--private/ntos/rtl/gen8dot3.c911
-rw-r--r--private/ntos/rtl/generr.c1260
-rw-r--r--private/ntos/rtl/gentable.c1098
-rw-r--r--private/ntos/rtl/handle.c228
-rw-r--r--private/ntos/rtl/heap.c2772
-rw-r--r--private/ntos/rtl/heapdbg.c1883
-rw-r--r--private/ntos/rtl/heapdll.c2732
-rw-r--r--private/ntos/rtl/heappage.c3096
-rw-r--r--private/ntos/rtl/heappage.h229
-rw-r--r--private/ntos/rtl/heappagi.h225
-rw-r--r--private/ntos/rtl/heappriv.h513
-rw-r--r--private/ntos/rtl/i386/context.c263
-rw-r--r--private/ntos/rtl/i386/debug2.asm65
-rw-r--r--private/ntos/rtl/i386/debug3.c121
-rw-r--r--private/ntos/rtl/i386/divlarge.c124
-rw-r--r--private/ntos/rtl/i386/exdsptch.c535
-rw-r--r--private/ntos/rtl/i386/exsup.asm15
-rw-r--r--private/ntos/rtl/i386/forceres.asm23
-rw-r--r--private/ntos/rtl/i386/halvprnt.c407
-rw-r--r--private/ntos/rtl/i386/ioaccess.asm423
-rw-r--r--private/ntos/rtl/i386/largeint.asm855
-rw-r--r--private/ntos/rtl/i386/lzntx86.asm445
-rw-r--r--private/ntos/rtl/i386/movemem.asm659
-rw-r--r--private/ntos/rtl/i386/nlssup.asm146
-rw-r--r--private/ntos/rtl/i386/nlstrans.asm668
-rw-r--r--private/ntos/rtl/i386/ntcurteb.asm79
-rw-r--r--private/ntos/rtl/i386/ntrtl386.h70
-rw-r--r--private/ntos/rtl/i386/raise.asm181
-rw-r--r--private/ntos/rtl/i386/raisests.c65
-rw-r--r--private/ntos/rtl/i386/rtldump.c185
-rw-r--r--private/ntos/rtl/i386/stkwalk.asm221
-rw-r--r--private/ntos/rtl/i386/stringsp.asm175
-rw-r--r--private/ntos/rtl/i386/userdisp.asm324
-rw-r--r--private/ntos/rtl/i386/xcptmisc.asm612
-rw-r--r--private/ntos/rtl/imagedir.c352
-rw-r--r--private/ntos/rtl/ldrreloc.c217
-rw-r--r--private/ntos/rtl/ldrrsrc.c930
-rw-r--r--private/ntos/rtl/lznt1.c2203
-rw-r--r--private/ntos/rtl/message.c460
-rw-r--r--private/ntos/rtl/mips/chandler.c220
-rw-r--r--private/ntos/rtl/mips/chkstk.s88
-rw-r--r--private/ntos/rtl/mips/context.c296
-rw-r--r--private/ntos/rtl/mips/debugstb.s271
-rw-r--r--private/ntos/rtl/mips/exdsptch.c2072
-rw-r--r--private/ntos/rtl/mips/getcalr.c179
-rw-r--r--private/ntos/rtl/mips/largeint.s947
-rw-r--r--private/ntos/rtl/mips/lzntmips.s1325
-rw-r--r--private/ntos/rtl/mips/nlssup.c109
-rw-r--r--private/ntos/rtl/mips/ntrtlmip.h41
-rw-r--r--private/ntos/rtl/mips/stringsp.c122
-rw-r--r--private/ntos/rtl/mips/trampoln.s522
-rw-r--r--private/ntos/rtl/mips/xcptmisc.s356
-rw-r--r--private/ntos/rtl/mips/xxcaptur.s314
-rw-r--r--private/ntos/rtl/mips/xxmvmem.s1440
-rw-r--r--private/ntos/rtl/mkit.cmd22
-rw-r--r--private/ntos/rtl/mp/alpha/sources14
-rw-r--r--private/ntos/rtl/mp/i386/sources17
-rw-r--r--private/ntos/rtl/mp/makefile6
-rw-r--r--private/ntos/rtl/mp/makefile.inc1
-rw-r--r--private/ntos/rtl/mp/mips/sources11
-rw-r--r--private/ntos/rtl/mp/ppc/sources10
-rw-r--r--private/ntos/rtl/mp/sources85
-rw-r--r--private/ntos/rtl/mrcf.c597
-rw-r--r--private/ntos/rtl/nls.c2429
-rw-r--r--private/ntos/rtl/nlsxlat.c2511
-rw-r--r--private/ntos/rtl/ntrtlp.h409
-rw-r--r--private/ntos/rtl/pctohdr.c245
-rw-r--r--private/ntos/rtl/ppc/chandler.c220
-rw-r--r--private/ntos/rtl/ppc/chkstk.s202
-rw-r--r--private/ntos/rtl/ppc/context.c240
-rw-r--r--private/ntos/rtl/ppc/debugstb.s266
-rw-r--r--private/ntos/rtl/ppc/exdsptch.c1196
-rw-r--r--private/ntos/rtl/ppc/getcalr.c189
-rw-r--r--private/ntos/rtl/ppc/jumps.c153
-rw-r--r--private/ntos/rtl/ppc/largeint.s1089
-rw-r--r--private/ntos/rtl/ppc/lzntppc.s1347
-rw-r--r--private/ntos/rtl/ppc/movemem.s2194
-rw-r--r--private/ntos/rtl/ppc/ntrtlppc.h43
-rw-r--r--private/ntos/rtl/ppc/trampoln.s687
-rw-r--r--private/ntos/rtl/ppc/vunwind.c1056
-rw-r--r--private/ntos/rtl/ppc/xcptmisc.s810
-rw-r--r--private/ntos/rtl/prefix.c2401
-rw-r--r--private/ntos/rtl/prodtype.c184
-rw-r--r--private/ntos/rtl/random.c111
-rw-r--r--private/ntos/rtl/recip.c92
-rw-r--r--private/ntos/rtl/registry.c633
-rw-r--r--private/ntos/rtl/regutil.c1553
-rw-r--r--private/ntos/rtl/rtlassig.c498
-rw-r--r--private/ntos/rtl/rtldata.c68
-rw-r--r--private/ntos/rtl/rtlexec.c1378
-rw-r--r--private/ntos/rtl/rxact.c1880
-rw-r--r--private/ntos/rtl/sertl.c5010
-rw-r--r--private/ntos/rtl/splay.c1074
-rw-r--r--private/ntos/rtl/stdtimep.h450
-rw-r--r--private/ntos/rtl/stktrace.c394
-rw-r--r--private/ntos/rtl/string.c779
-rw-r--r--private/ntos/rtl/tacl.c307
-rw-r--r--private/ntos/rtl/tbitmap.c483
-rw-r--r--private/ntos/rtl/time.c1332
-rw-r--r--private/ntos/rtl/tnlsxlat.c101
-rw-r--r--private/ntos/rtl/tprefix.c339
-rw-r--r--private/ntos/rtl/trace.c157
-rw-r--r--private/ntos/rtl/trandom.c86
-rw-r--r--private/ntos/rtl/triangle.c1420
-rw-r--r--private/ntos/rtl/triangle.h325
-rw-r--r--private/ntos/rtl/trtl.c190
-rw-r--r--private/ntos/rtl/tsplay.c184
-rw-r--r--private/ntos/rtl/ttime.c325
-rw-r--r--private/ntos/rtl/ttri.c187
-rw-r--r--private/ntos/rtl/ucli.c51
-rw-r--r--private/ntos/rtl/uexec.c140
-rw-r--r--private/ntos/rtl/uexec1.c50
-rw-r--r--private/ntos/rtl/uexec2.c258
-rw-r--r--private/ntos/rtl/up/alpha/sources14
-rw-r--r--private/ntos/rtl/up/i386/sources17
-rw-r--r--private/ntos/rtl/up/makefile6
-rw-r--r--private/ntos/rtl/up/makefile.inc1
-rw-r--r--private/ntos/rtl/up/mips/sources11
-rw-r--r--private/ntos/rtl/up/ppc/sources10
-rw-r--r--private/ntos/rtl/up/sources83
-rw-r--r--private/ntos/rtl/urtl.c180
-rw-r--r--private/ntos/rtl/user/alpha/sources15
-rw-r--r--private/ntos/rtl/user/i386/sources19
-rw-r--r--private/ntos/rtl/user/makefile6
-rw-r--r--private/ntos/rtl/user/makefile.inc25
-rw-r--r--private/ntos/rtl/user/mips/sources11
-rw-r--r--private/ntos/rtl/user/ppc/sources10
-rw-r--r--private/ntos/rtl/user/sources86
-rw-r--r--private/ntos/rtl/user/theap.c301
-rw-r--r--private/ntos/rtl/utxcpt1.c83
-rw-r--r--private/ntos/rtl/utxcpt2.c398
-rw-r--r--private/ntos/rtl/utxcpt3.c26
-rw-r--r--private/ntos/rtl/utxcpt4.c514
177 files changed, 95455 insertions, 0 deletions
diff --git a/private/ntos/rtl/acledit.c b/private/ntos/rtl/acledit.c
new file mode 100644
index 000000000..3c502274a
--- /dev/null
+++ b/private/ntos/rtl/acledit.c
@@ -0,0 +1,1970 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Acledit.c
+
+Abstract:
+
+ This Module implements the Acl rtl editing functions that are defined in
+ ntseapi.h
+
+Author:
+
+ Gary Kimura (GaryKi) 9-Nov-1989
+
+Environment:
+
+ Pure Runtime Library Routine
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "seopaque.h"
+
+//
+// Define the local macros and procedure for this module
+//
+
+//
+// Return a pointer to the first Ace in an Acl (even if the Acl is empty).
+//
+// PACE_HEADER
+// FirstAce (
+// IN PACL Acl
+// );
+//
+
+#define FirstAce(Acl) ((PVOID)((PUCHAR)(Acl) + sizeof(ACL)))
+
+//
+// Return a pointer to the next Ace in a sequence (even if the input
+// Ace is the one in the sequence).
+//
+// PACE_HEADER
+// NextAce (
+// IN PACE_HEADER Ace
+// );
+//
+
+#define NextAce(Ace) ((PVOID)((PUCHAR)(Ace) + ((PACE_HEADER)(Ace))->AceSize))
+
+
+//
+// Test to determine if two ACLs are of the same revision.
+//
+
+#define RevisionsMatch(Acl,AceRevision) ( (((Acl)->AclRevision == ACL_REVISION2) && ((AceRevision) == ACL_REVISION2)) || \
+ (((Acl)->AclRevision == ACL_REVISION3) && ((AceRevision) == ACL_REVISION3)) \
+ )
+ VOID
+RtlpAddData (
+ IN PVOID From,
+ IN ULONG FromSize,
+ IN PVOID To,
+ IN ULONG ToSize
+ );
+
+VOID
+RtlpDeleteData (
+ IN PVOID Data,
+ IN ULONG RemoveSize,
+ IN ULONG TotalSize
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlpAddData)
+#pragma alloc_text(PAGE,RtlpDeleteData)
+#pragma alloc_text(PAGE,RtlCreateAcl)
+#pragma alloc_text(PAGE,RtlValidAcl)
+#pragma alloc_text(PAGE,RtlQueryInformationAcl)
+#pragma alloc_text(PAGE,RtlSetInformationAcl)
+#pragma alloc_text(PAGE,RtlAddAce)
+#pragma alloc_text(PAGE,RtlDeleteAce)
+#pragma alloc_text(PAGE,RtlGetAce)
+#pragma alloc_text(PAGE,RtlAddAccessAllowedAce)
+#pragma alloc_text(PAGE,RtlAddAccessDeniedAce)
+#pragma alloc_text(PAGE,RtlAddAuditAccessAce)
+#pragma alloc_text(PAGE,RtlFirstFreeAce)
+#endif
+
+
+NTSTATUS
+RtlCreateAcl (
+ IN PACL Acl,
+ IN ULONG AclLength,
+ IN ULONG AclRevision
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes an ACL data structure. After initialization
+ it is an ACL with no ACE (i.e., a deny all access type ACL)
+
+Arguments:
+
+ Acl - Supplies the buffer containing the ACL being initialized
+
+ AclLength - Supplies the length of the ace buffer in bytes
+
+ AclRevision - Supplies the revision for this Acl
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful
+
+ STATUS_BUFFER_TOO_SMALL if the AclLength is too small,
+
+ STATUS_INVALID_PARAMETER if the revision is out of range
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Check to see the size of the buffer is large enough to hold at
+ // least the ACL header
+ //
+
+ if (AclLength < sizeof(ACL)) {
+
+ //
+ // Buffer to small even for the ACL header
+ //
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Check to see if the revision is equal to 2. Later versions
+ // of this procedure might accept more revision levels
+ //
+
+ if (AclRevision != ACL_REVISION2 && AclRevision != ACL_REVISION3) {
+
+ //
+ // Revision not current
+ //
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ if ( AclLength > MAXUSHORT ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Initialize the ACL
+ //
+
+ Acl->AclRevision = (UCHAR)AclRevision; // Used to hardwire ACL_REVISION2 here
+ Acl->Sbz1 = 0;
+ Acl->AclSize = (USHORT) (AclLength & 0xfffc);
+ Acl->AceCount = 0;
+ Acl->Sbz2 = 0;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+BOOLEAN
+RtlValidAcl (
+ IN PACL Acl
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure validates an ACL.
+
+ This involves validating the revision level of the ACL and ensuring
+ that the number of ACEs specified in the AceCount fit in the space
+ specified by the AclSize field of the ACL header.
+
+Arguments:
+
+ Acl - Pointer to the ACL structure to validate.
+
+Return Value:
+
+ BOOLEAN - TRUE if the structure of Acl is valid.
+
+--*/
+
+{
+
+ PVOID FirstFree;
+
+ RTL_PAGED_CODE();
+
+ try {
+
+ //
+ // Check the ACL revision level
+ //
+
+ if (!ValidAclRevision( Acl )) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Check that the length is aligned. The kernel checks this,
+ // so we should too.
+ //
+
+ if ((USHORT) LongAlign(Acl->AclSize) != Acl->AclSize) {
+ return FALSE;
+ }
+ //
+ // Check to see that the Acl is well formed.
+ //
+
+ return ( RtlFirstFreeAce( Acl, &FirstFree ));
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ return FALSE;
+ }
+
+}
+
+
+NTSTATUS
+RtlQueryInformationAcl (
+ IN PACL Acl,
+ OUT PVOID AclInformation,
+ IN ULONG AclInformationLength,
+ IN ACL_INFORMATION_CLASS AclInformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns to the caller information about an ACL. The requested
+ information can be AclRevisionInformation, or AclSizeInformation.
+
+Arguments:
+
+ Acl - Supplies the Acl being examined
+
+ AclInformation - Supplies the buffer to receive the information being
+ requested
+
+ AclInformationLength - Supplies the length of the AclInformation buffer
+ in bytes
+
+ AclInformationClass - Supplies the type of information being requested
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
+ status otherwise
+
+--*/
+
+{
+ PACL_REVISION_INFORMATION RevisionInfo;
+ PACL_SIZE_INFORMATION SizeInfo;
+
+ PVOID FirstFree;
+ NTSTATUS Status;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the ACL revision level
+ //
+
+ if (!ValidAclRevision( Acl )) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Case on the information class being requested
+ //
+
+ switch (AclInformationClass) {
+
+ case AclRevisionInformation:
+
+ //
+ // Make sure the buffer size is correct
+ //
+
+ if (AclInformationLength < sizeof(ACL_REVISION_INFORMATION)) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Get the Acl revision and return
+ //
+
+ RevisionInfo = (PACL_REVISION_INFORMATION)AclInformation;
+ RevisionInfo->AclRevision = Acl->AclRevision;
+
+ break;
+
+ case AclSizeInformation:
+
+ //
+ // Make sure the buffer size is correct
+ //
+
+ if (AclInformationLength < sizeof(ACL_SIZE_INFORMATION)) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Locate the first free spot in the Acl
+ //
+
+ if (!RtlFirstFreeAce( Acl, &FirstFree )) {
+
+ //
+ // The input Acl is ill-formed
+ //
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Given a pointer to the first free spot we can now easily compute
+ // the number of free bytes and used bytes in the Acl.
+ //
+
+ SizeInfo = (PACL_SIZE_INFORMATION)AclInformation;
+ SizeInfo->AceCount = Acl->AceCount;
+
+ if (FirstFree == NULL) {
+
+ //
+ // With a null first free we don't have any free space in the Acl
+ //
+
+ SizeInfo->AclBytesInUse = Acl->AclSize;
+
+ SizeInfo->AclBytesFree = 0;
+
+ } else {
+
+ //
+ // The first free is not null so we have some free room left in
+ // the acl
+ //
+
+ SizeInfo->AclBytesInUse = (PUCHAR)FirstFree - (PUCHAR)Acl;
+
+ SizeInfo->AclBytesFree = Acl->AclSize - SizeInfo->AclBytesInUse;
+
+ }
+
+ break;
+
+ default:
+
+ return STATUS_INVALID_INFO_CLASS;
+
+ }
+
+ //
+ // and return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlSetInformationAcl (
+ IN PACL Acl,
+ IN PVOID AclInformation,
+ IN ULONG AclInformationLength,
+ IN ACL_INFORMATION_CLASS AclInformationClass
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the state of an ACL. For now only the revision
+ level can be set and for now only a revision level of 1 is accepted
+ so this procedure is rather simple
+
+Arguments:
+
+ Acl - Supplies the Acl being altered
+
+ AclInformation - Supplies the buffer containing the information being
+ set
+
+ AclInformationLength - Supplies the length of the Acl information buffer
+
+ AclInformationClass - Supplies the type of information begin set
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
+ status otherwise
+
+--*/
+
+{
+ PACL_REVISION_INFORMATION RevisionInfo;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the ACL revision level
+ //
+
+ if (!ValidAclRevision( Acl )) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Case on the information class being requested
+ //
+
+ switch (AclInformationClass) {
+
+ case AclRevisionInformation:
+
+ //
+ // Make sure the buffer size is correct
+ //
+
+ if (AclInformationLength < sizeof(ACL_REVISION_INFORMATION)) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Get the Acl requested ACL revision level
+ //
+
+ RevisionInfo = (PACL_REVISION_INFORMATION)AclInformation;
+
+ //
+ // Don't let them lower the revision of an ACL.
+ //
+
+ if (RevisionInfo->AclRevision < Acl->AclRevision ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Assign the new revision.
+ //
+
+ Acl->AclRevision = (UCHAR)RevisionInfo->AclRevision;
+
+ break;
+
+ default:
+
+ return STATUS_INVALID_INFO_CLASS;
+
+ }
+
+ //
+ // and return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlAddAce (
+ IN OUT PACL Acl,
+ IN ULONG AceRevision,
+ IN ULONG StartingAceIndex,
+ IN PVOID AceList,
+ IN ULONG AceListLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds a string of ACEs to an ACL.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceRevision - Supplies the Acl/Ace revision of the ACE being added
+
+ StartingAceIndex - Supplies the ACE index which will be the index of
+ the first ace inserted in the acl. 0 for the beginning of the list
+ and MAXULONG for the end of the list.
+
+ AceList - Supplies the list of Aces to be added to the Acl
+
+ AceListLength - Supplies the size, in bytes, of the AceList buffer
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful, and an appropriate error
+ status otherwise
+
+--*/
+
+{
+ PVOID FirstFree;
+
+ PACE_HEADER Ace;
+ ULONG NewAceCount;
+
+ PVOID AcePosition;
+ ULONG i;
+ UCHAR NewRevision;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the ACL revision level
+ //
+
+ if (!ValidAclRevision(Acl)) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Locate the first free ace and check to see that the Acl is
+ // well formed.
+ //
+
+ if (!RtlFirstFreeAce( Acl, &FirstFree )) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // If the AceRevision is greater than the ACL revision, then we want to
+ // increase the ACL revision to be the same as the new ACE revision.
+ // We can do this because our previously defined ACE types ( 0 -> 3 ) have
+ // not changed structure nor been discontinued in the new revision. So
+ // we can bump the revision and the older types will not be misinterpreted.
+ //
+ // Compute what the final revision of the ACL is going to be, and save it
+ // for later so we can update it once we know we're going to succeed.
+ //
+
+ NewRevision = (UCHAR)AceRevision > Acl->AclRevision ? (UCHAR)AceRevision : Acl->AclRevision;
+
+ //
+ // Check that the AceList is well formed, we do this by simply zooming
+ // down the Ace list until we're equal to or have exceeded the ace list
+ // length. If we are equal to the length then we're well formed otherwise
+ // we're ill-formed. We'll also calculate how many Ace's there are
+ // in the AceList
+ //
+ // In addition, now we have to make sure that we haven't been handed an
+ // ACE type that is inappropriate for the AceRevision that was passed
+ // in.
+ //
+
+ for (Ace = AceList, NewAceCount = 0;
+ Ace < (PACE_HEADER)((PUCHAR)AceList + AceListLength);
+ Ace = NextAce( Ace ), NewAceCount++) {
+
+ //
+ // Compound ACEs weren't defined before ACL_REVISION3. That means that
+ // if we got passed that type with a lower revision number, it means
+ // that someone stole one of our reserved types. Return an error.
+ //
+
+ if (((PACE_HEADER)Ace)->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) {
+
+ if (AceRevision < ACL_REVISION3) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ //
+ // Check to see if we've exceeded the ace list length
+ //
+
+ if (Ace > (PACE_HEADER)((PUCHAR)AceList + AceListLength)) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Check to see if there is enough room in the Acl to store the additional
+ // Ace list
+ //
+
+ if (FirstFree == NULL ||
+ (PUCHAR)FirstFree + AceListLength > (PUCHAR)Acl + Acl->AclSize) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // All of the input has checked okay, we now need to locate the position
+ // where to insert the new ace list. We won't check the acl for
+ // validity because we did earlier when got the first free ace position.
+ //
+
+ AcePosition = FirstAce( Acl );
+
+ for (i = 0; i < StartingAceIndex && i < Acl->AceCount; i++) {
+
+ AcePosition = NextAce( AcePosition );
+
+ }
+
+ //
+ // Now Ace points to where we want to insert the ace list, We do the
+ // insertion by adding ace list to the acl and shoving over the remainder
+ // of the list down the acl. We know this will work because we earlier
+ // check to make sure the new acl list will fit in the acl size
+ //
+
+ RtlpAddData( AceList, AceListLength,
+ AcePosition, ((PUCHAR)FirstFree - (PUCHAR)AcePosition));
+
+ //
+ // Update the Acl Header
+ //
+
+ Acl->AceCount = (USHORT)(Acl->AceCount + NewAceCount);
+
+ Acl->AclRevision = NewRevision;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlDeleteAce (
+ IN OUT PACL Acl,
+ IN ULONG AceIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes one ACE from an ACL.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceIndex - Supplies the index of the Ace to delete.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
+ status otherwise
+
+--*/
+
+{
+ PVOID FirstFree;
+
+ PACE_HEADER Ace;
+ ULONG i;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the ACL revision level
+ //
+
+ if (!ValidAclRevision(Acl)) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Make sure the AceIndex is within proper range, it's ulong so we know
+ // it can't be negative
+ //
+
+ if (AceIndex >= Acl->AceCount) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Locate the first free spot, this will tell us how much data
+ // we'll need to colapse. If the results is false then the acl is
+ // ill-formed
+ //
+
+ if (!RtlFirstFreeAce( Acl, &FirstFree )) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Now locate the ace that we're going to delete. This loop
+ // doesn't need to check the acl for being well formed.
+ //
+
+ Ace = FirstAce( Acl );
+
+ for (i = 0; i < AceIndex; i++) {
+
+ Ace = NextAce( Ace );
+
+ }
+
+ //
+ // We've found the ace to delete to simply copy over the rest of
+ // the acl over this ace. The delete data procedure also deletes
+ // rest of the string that it's moving over so we don't have to
+ //
+
+ RtlpDeleteData( Ace, Ace->AceSize, (PUCHAR)FirstFree - (PUCHAR)Ace);
+
+ //
+ // Update the Acl header
+ //
+
+ Acl->AceCount--;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlGetAce (
+ IN PACL Acl,
+ ULONG AceIndex,
+ OUT PVOID *Ace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to an ACE in an ACl referenced by
+ ACE index
+
+Arguments:
+
+ Acl - Supplies the ACL being queried
+
+ AceIndex - Supplies the Ace index to locate
+
+ Ace - Receives the address of the ACE within the ACL
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
+ status otherwise
+
+--*/
+
+{
+ ULONG i;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the ACL revision level
+ //
+
+ if (!ValidAclRevision(Acl)) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // Check the AceIndex against the Ace count of the Acl, it's ulong so
+ // we know it can't be negative
+ //
+
+ if (AceIndex >= Acl->AceCount) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // To find the Ace requested by zooming down the Ace List.
+ //
+
+ *Ace = FirstAce( Acl );
+
+ for (i = 0; i < AceIndex; i++) {
+
+ //
+ // Check to make sure we haven't overrun the Acl buffer
+ // with our ace pointer. If we have then our input is bogus
+ //
+
+ if (*Ace >= (PVOID)((PUCHAR)Acl + Acl->AclSize)) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // And move Ace to the next ace position
+ //
+
+ *Ace = NextAce( *Ace );
+
+ }
+
+ //
+ // Now Ace points to the Ace we're after, but make sure we aren't
+ // beyond the Acl.
+ //
+
+ if (*Ace >= (PVOID)((PUCHAR)Acl + Acl->AclSize)) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ //
+ // The Ace is still within the Acl so return success to our caller
+ //
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlAddCompoundAce (
+ IN PACL Acl,
+ IN ULONG AceRevision,
+ IN UCHAR CompoundAceType,
+ IN ACCESS_MASK AccessMask,
+ IN PSID ServerSid,
+ IN PSID ClientSid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds a KNOWN_COMPOUND_ACE to an ACL. This is
+ expected to be a common form of ACL modification.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceRevision - Supplies the Acl/Ace revision of the ACE being added
+
+ CompoundAceType - Supplies the type of compound ACE being added.
+ Currently the only defined type is COMPOUND_ACE_IMPERSONATION.
+
+ AccessMask - The mask of accesses to be granted to the specified SID pair.
+
+ ServerSid - Pointer to the Server SID to be placed in the ACE.
+
+ ClientSid - Pointer to the Client SID to be placed in the ACE.
+
+Return Value:
+
+ NTSTATUS - STATUS_SUCCESS if successful and an appropriate error
+ status otherwise
+
+--*/
+
+
+
+
+{
+ PVOID FirstFree;
+ USHORT AceSize;
+ PKNOWN_COMPOUND_ACE GrantAce;
+ UCHAR NewRevision;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Validate the structure of the SID
+ //
+
+ if (!RtlValidSid(ServerSid) || !RtlValidSid(ClientSid)) {
+ return STATUS_INVALID_SID;
+ }
+
+ //
+ // Check the ACL & ACE revision levels
+ //
+
+ if ( Acl->AclRevision > ACL_REVISION3 || AceRevision != ACL_REVISION3 ) {
+ return STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Calculate the new revision of the ACL. The new revision is the maximum
+ // of the old revision and and new ACE's revision. This is possible because
+ // the format of previously defined ACEs did not change across revisions.
+ //
+
+ NewRevision = Acl->AclRevision > (UCHAR)AceRevision ? Acl->AclRevision : (UCHAR)AceRevision;
+
+ //
+ // Locate the first free ace and check to see that the Acl is
+ // well formed.
+ //
+
+ if (!RtlFirstFreeAce( Acl, &FirstFree )) {
+
+ return STATUS_INVALID_ACL;
+ }
+
+ //
+ // Check to see if there is enough room in the Acl to store the new
+ // ACE
+ //
+
+ AceSize = (USHORT)(sizeof(KNOWN_COMPOUND_ACE) -
+ sizeof(ULONG) +
+ RtlLengthSid(ClientSid) +
+ RtlLengthSid(ServerSid)
+ );
+
+ if ( FirstFree == NULL ||
+ ((PUCHAR)FirstFree + AceSize > ((PUCHAR)Acl + Acl->AclSize))
+ ) {
+
+ return STATUS_ALLOTTED_SPACE_EXCEEDED;
+ }
+
+ //
+ // Add the ACE to the end of the ACL
+ //
+
+ GrantAce = (PKNOWN_COMPOUND_ACE)FirstFree;
+ GrantAce->Header.AceFlags = 0;
+ GrantAce->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
+ GrantAce->Header.AceSize = AceSize;
+ GrantAce->Mask = AccessMask;
+ GrantAce->CompoundAceType = CompoundAceType;
+ RtlCopySid( RtlLengthSid(ServerSid), (PSID)(&GrantAce->SidStart), ServerSid );
+ RtlCopySid( RtlLengthSid(ClientSid), (PSID)(((ULONG)&GrantAce->SidStart) + RtlLengthSid(ServerSid)), ClientSid );
+
+ //
+ // Increment the number of ACEs by 1.
+ //
+
+ Acl->AceCount += 1;
+
+ //
+ // Adjust the Acl revision, if necessary
+ //
+
+ Acl->AclRevision = NewRevision;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlpAddKnownAce (
+ IN OUT PACL Acl,
+ IN ULONG AceRevision,
+ IN ACCESS_MASK AccessMask,
+ IN PSID Sid,
+ IN UCHAR NewType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds KNOWN_ACE to an ACL. This is
+ expected to be a common form of ACL modification.
+
+ A very bland ACE header is placed in the ACE. It provides no
+ inheritance and no ACE flags. The type is specified by the caller.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceRevision - Supplies the Acl/Ace revision of the ACE being added
+
+ AccessMask - The mask of accesses to be denied to the specified SID.
+
+ Sid - Pointer to the SID being denied access.
+
+ NewType - Type of ACE to be added.
+
+Return Value:
+
+ STATUS_SUCCESS - The ACE was successfully added.
+
+ STATUS_INVALID_ACL - The specified ACL is not properly formed.
+
+ STATUS_REVISION_MISMATCH - The specified revision is not known
+ or is incompatible with that of the ACL.
+
+ STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
+ ACL. A larger ACL buffer is required.
+
+ STATUS_INVALID_SID - The provided SID is not a structurally valid
+ SID.
+
+--*/
+
+{
+ PVOID FirstFree;
+ USHORT AceSize;
+ PKNOWN_ACE GrantAce;
+ UCHAR NewRevision;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Validate the structure of the SID
+ //
+
+ if (!RtlValidSid(Sid)) {
+ return STATUS_INVALID_SID;
+ }
+
+ //
+ // Check the ACL & ACE revision levels
+ //
+
+ if ( Acl->AclRevision > ACL_REVISION3 || AceRevision > ACL_REVISION3 ) {
+
+ return STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Calculate the new revision of the ACL. The new revision is the maximum
+ // of the old revision and and new ACE's revision. This is possible because
+ // the format of previously defined ACEs did not change across revisions.
+ //
+
+ NewRevision = Acl->AclRevision > (UCHAR)AceRevision ? Acl->AclRevision : (UCHAR)AceRevision;
+
+ //
+ // Locate the first free ace and check to see that the Acl is
+ // well formed.
+ //
+
+ if (!RtlFirstFreeAce( Acl, &FirstFree )) {
+
+ return STATUS_INVALID_ACL;
+ }
+
+ //
+ // Check to see if there is enough room in the Acl to store the new
+ // ACE
+ //
+
+ AceSize = (USHORT)(sizeof(ACE_HEADER) +
+ sizeof(ACCESS_MASK) +
+ RtlLengthSid(Sid));
+
+ if ( FirstFree == NULL ||
+ ((PUCHAR)FirstFree + AceSize > ((PUCHAR)Acl + Acl->AclSize))
+ ) {
+
+ return STATUS_ALLOTTED_SPACE_EXCEEDED;
+ }
+
+ //
+ // Add the ACE to the end of the ACL
+ //
+
+ GrantAce = (PKNOWN_ACE)FirstFree;
+ GrantAce->Header.AceFlags = 0;
+ GrantAce->Header.AceType = NewType;
+ GrantAce->Header.AceSize = AceSize;
+ GrantAce->Mask = AccessMask;
+ RtlCopySid( RtlLengthSid(Sid), (PSID)(&GrantAce->SidStart), Sid );
+
+ //
+ // Increment the number of ACEs by 1.
+ //
+
+ Acl->AceCount += 1;
+
+ //
+ // Adjust the Acl revision, if necessary
+ //
+
+ Acl->AclRevision = NewRevision;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlAddAccessAllowedAce (
+ IN OUT PACL Acl,
+ IN ULONG AceRevision,
+ IN ACCESS_MASK AccessMask,
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds an ACCESS_ALLOWED ACE to an ACL. This is
+ expected to be a common form of ACL modification.
+
+ A very bland ACE header is placed in the ACE. It provides no
+ inheritance and no ACE flags.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceRevision - Supplies the Acl/Ace revision of the ACE being added
+
+ AccessMask - The mask of accesses to be granted to the specified SID.
+
+ Sid - Pointer to the SID being granted access.
+
+Return Value:
+
+ STATUS_SUCCESS - The ACE was successfully added.
+
+ STATUS_INVALID_ACL - The specified ACL is not properly formed.
+
+ STATUS_REVISION_MISMATCH - The specified revision is not known
+ or is incompatible with that of the ACL.
+
+ STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
+ ACL. A larger ACL buffer is required.
+
+ STATUS_INVALID_SID - The provided SID is not a structurally valid
+ SID.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return RtlpAddKnownAce (
+ Acl,
+ AceRevision,
+ AccessMask,
+ Sid,
+ ACCESS_ALLOWED_ACE_TYPE
+ );
+}
+
+
+NTSTATUS
+RtlAddAccessDeniedAce (
+ IN OUT PACL Acl,
+ IN ULONG AceRevision,
+ IN ACCESS_MASK AccessMask,
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds an ACCESS_DENIED ACE to an ACL. This is
+ expected to be a common form of ACL modification.
+
+ A very bland ACE header is placed in the ACE. It provides no
+ inheritance and no ACE flags.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceRevision - Supplies the Acl/Ace revision of the ACE being added
+
+ AccessMask - The mask of accesses to be denied to the specified SID.
+
+ Sid - Pointer to the SID being denied access.
+
+Return Value:
+
+ STATUS_SUCCESS - The ACE was successfully added.
+
+ STATUS_INVALID_ACL - The specified ACL is not properly formed.
+
+ STATUS_REVISION_MISMATCH - The specified revision is not known
+ or is incompatible with that of the ACL.
+
+ STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
+ ACL. A larger ACL buffer is required.
+
+ STATUS_INVALID_SID - The provided SID is not a structurally valid
+ SID.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return RtlpAddKnownAce (
+ Acl,
+ AceRevision,
+ AccessMask,
+ Sid,
+ ACCESS_DENIED_ACE_TYPE
+ );
+
+}
+
+
+NTSTATUS
+RtlAddAuditAccessAce (
+ IN OUT PACL Acl,
+ IN ULONG AceRevision,
+ IN ACCESS_MASK AccessMask,
+ IN PSID Sid,
+ IN BOOLEAN AuditSuccess,
+ IN BOOLEAN AuditFailure
+ )
+
+/*++
+
+Routine Description:
+
+ This routine adds a SYSTEM_AUDIT ACE to an ACL. This is
+ expected to be a common form of ACL modification.
+
+ A very bland ACE header is placed in the ACE. It provides no
+ inheritance.
+
+ Parameters are used to indicate whether auditing is to be performed
+ on success, failure, or both.
+
+Arguments:
+
+ Acl - Supplies the Acl being modified
+
+ AceRevision - Supplies the Acl/Ace revision of the ACE being added
+
+ AccessMask - The mask of accesses to be denied to the specified SID.
+
+ Sid - Pointer to the SID to be audited.
+
+ AuditSuccess - If TRUE, indicates successful access attempts are to be
+ audited.
+
+ AuditFailure - If TRUE, indicated failed access attempts are to be
+ audited.
+
+Return Value:
+
+ STATUS_SUCCESS - The ACE was successfully added.
+
+ STATUS_INVALID_ACL - The specified ACL is not properly formed.
+
+ STATUS_REVISION_MISMATCH - The specified revision is not known
+ or is incompatible with that of the ACL.
+
+ STATUS_ALLOTTED_SPACE_EXCEEDED - The new ACE does not fit into the
+ ACL. A larger ACL buffer is required.
+
+ STATUS_INVALID_SID - The provided SID is not a structurally valid
+ SID.
+
+--*/
+
+{
+ PVOID FirstFree;
+ UCHAR AuditFlags;
+ USHORT AceSize;
+
+ PSYSTEM_AUDIT_ACE AuditAce;
+
+ RTL_PAGED_CODE();
+
+ AuditFlags = 0;
+ if (AuditSuccess) {
+ AuditFlags |= SUCCESSFUL_ACCESS_ACE_FLAG;
+ }
+ if (AuditFailure) {
+ AuditFlags |= FAILED_ACCESS_ACE_FLAG;
+ }
+
+ //
+ // Validate the structure of the SID
+ //
+
+ if (!RtlValidSid(Sid)) {
+ return STATUS_INVALID_SID;
+ }
+
+ //
+ // Check the ACL & ACE revision levels
+ //
+
+ if ( Acl->AclRevision > ACL_REVISION3 || AceRevision > ACL_REVISION3 ) {
+
+ return STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Locate the first free ace and check to see that the Acl is
+ // well formed.
+ //
+
+ if (!RtlFirstFreeAce( Acl, &FirstFree )) {
+
+ return STATUS_INVALID_ACL;
+
+ }
+
+ //
+ // Check to see if there is enough room in the Acl to store the new
+ // ACE
+ //
+
+ AceSize = (USHORT)(sizeof(ACE_HEADER) +
+ sizeof(ACCESS_MASK) +
+ RtlLengthSid(Sid));
+
+ if ( FirstFree == NULL ||
+ ((PUCHAR)FirstFree + AceSize > ((PUCHAR)Acl + Acl->AclSize))
+ ) {
+
+ return STATUS_ALLOTTED_SPACE_EXCEEDED;
+
+ }
+
+ //
+ // Add the ACE to the end of the ACL
+ //
+
+ AuditAce = (PSYSTEM_AUDIT_ACE)FirstFree;
+ AuditAce->Header.AceFlags = AuditFlags;
+ AuditAce->Header.AceType = SYSTEM_AUDIT_ACE_TYPE;
+ AuditAce->Header.AceSize = AceSize;
+ AuditAce->Mask = AccessMask;
+ RtlCopySid( RtlLengthSid(Sid), (PSID)(&AuditAce->SidStart), Sid );
+
+ //
+ // Increment the number of ACEs by 1.
+ //
+
+ Acl->AceCount += 1;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+#if 0
+
+NTSTATUS
+RtlMakePosixAcl(
+ IN ULONG AclRevision,
+ IN PSID UserSid,
+ IN PSID GroupSid,
+ IN ACCESS_MASK UserAccess,
+ IN ACCESS_MASK GroupAccess,
+ IN ACCESS_MASK OtherAccess,
+ IN ULONG AclLength,
+ OUT PACL Acl,
+ OUT PULONG ReturnLength
+ )
+/*++
+
+Routine Description:
+
+ NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
+
+ Make an ACL representing Posix protection from AccessMask and
+ security account ID (SID) information.
+
+Arguments:
+
+ AclRevision - Indicates the ACL revision level of the access masks
+ provided. The ACL generated will be revision compatible with this
+ value and will not be a higher revision than this value.
+
+ UserSid - Provides the SID of the user (owner).
+
+ GroupSid - Provides the SID of the primary group.
+
+ UserAccess - Specifies the accesses to be given to the user (owner).
+
+ GroupAccess - Specifies the accesses to be given to the primary group.
+
+ OtherAccess - Specifies the accesses to be given to others (WORLD).
+
+ AclLength - Provides the length (in bytes) of the Acl buffer.
+
+ Acl - Points to a buffer to receive the generated ACL.
+
+ ReturnLength - Returns the actual length needed to store the resultant
+ ACL. If this length is greater than that specified in AclLength,
+ then STATUS_BUFFER_TOO_SMALL is returned and no ACL is generated.
+
+Return Values:
+
+ STATUS_SUCCESS - The service completed successfully.
+
+ STATUS_UNKNOWN_REVISION - The revision level specified is not supported
+ by this service.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the length of the output buffer
+ wasn't large enough to hold the generated ACL. The length needed
+ is returned via the ReturnLength parameter.
+
+--*/
+
+{
+
+ SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
+
+ ULONG UserSidLength;
+ ULONG GroupSidLength;
+ ULONG WorldSidLength;
+ ULONG RequiredAclSize;
+ ULONG AceSize;
+ ULONG CurrentAce;
+ PACCESS_ALLOWED_ACE Ace;
+ NTSTATUS Status;
+
+ RTL_PAGED_CODE();
+
+ if (!RtlValidSid( UserSid ) || !RtlValidSid( GroupSid )) {
+ return( STATUS_INVALID_SID );
+ }
+
+ UserSidLength = RtlLengthSid( UserSid );
+ GroupSidLength = RtlLengthSid( GroupSid );
+ WorldSidLength = RtlLengthRequiredSid( 1 );
+
+ //
+ // Figure out how much room we need for an ACL and three
+ // ACCESS_ALLOWED Ace's
+ //
+
+ RequiredAclSize = sizeof( ACL );
+
+ AceSize = sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG );
+
+ RequiredAclSize += (AceSize * 3) +
+ UserSidLength +
+ GroupSidLength +
+ WorldSidLength ;
+
+ if (RequiredAclSize > AclLength) {
+ *ReturnLength = RequiredAclSize;
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ //
+ // The passed buffer is big enough, build the ACL in it.
+ //
+
+ Status = RtlCreateAcl(
+ Acl,
+ RequiredAclSize,
+ AclRevision
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ CurrentAce = (ULONG)Acl + sizeof( ACL );
+ Ace = (PACCESS_ALLOWED_ACE)CurrentAce;
+
+ //
+ // Build the user (owner) ACE
+ //
+
+ Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ Ace->Header.AceSize = (USHORT)(UserSidLength + AceSize);
+ Ace->Header.AceFlags = 0;
+
+ Ace->Mask = UserAccess;
+
+ RtlMoveMemory(
+ (PVOID)(Ace->SidStart),
+ UserSid,
+ UserSidLength
+ );
+
+ CurrentAce += (ULONG)(Ace->Header.AceSize);
+ Ace = (PACCESS_ALLOWED_ACE)CurrentAce;
+
+ //
+ // Build the group ACE
+ //
+
+ Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ Ace->Header.AceSize = (USHORT)(GroupSidLength + AceSize);
+ Ace->Header.AceFlags = 0;
+
+ Ace->Mask = GroupAccess;
+
+ RtlMoveMemory(
+ (PVOID)(Ace->SidStart),
+ GroupSid,
+ GroupSidLength
+ );
+
+ CurrentAce += (ULONG)(Ace->Header.AceSize);
+ Ace = (PACCESS_ALLOWED_ACE)CurrentAce;
+
+ //
+ // Build the World ACE
+ //
+
+ Ace->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ Ace->Header.AceSize = (USHORT)(GroupSidLength + AceSize);
+ Ace->Header.AceFlags = 0;
+
+ Ace->Mask = OtherAccess;
+
+ RtlInitializeSid(
+ (PSID)(Ace->SidStart),
+ &WorldSidAuthority,
+ 1
+ );
+
+ *(RtlSubAuthoritySid((PSID)(Ace->SidStart), 0 )) = SECURITY_WORLD_RID;
+
+ return( STATUS_SUCCESS );
+
+}
+
+NTSTATUS
+RtlInterpretPosixAcl(
+ IN ULONG AclRevision,
+ IN PSID UserSid,
+ IN PSID GroupSid,
+ IN PACL Acl,
+ OUT PACCESS_MASK UserAccess,
+ OUT PACCESS_MASK GroupAccess,
+ OUT PACCESS_MASK OtherAccess
+ )
+/*++
+
+Routine Description:
+
+ NOTE: THIS ROUTINE IS STILL BEING SPEC'D.
+
+ Interpret an ACL representing Posix protection, returning AccessMasks.
+ Use security account IDs (SIDs) for object owner and primary group
+ identification.
+
+ This algorithm will pick up the first match of a given SID and ignore
+ all further matches of that SID. The first unrecognized SID becomes
+ the "other" SID.
+
+Arguments:
+
+ AclRevision - Indicates the ACL revision level of the access masks to
+ be returned.
+
+ UserSid - Provides the SID of the user (owner).
+
+ GroupSid - Provides the SID of the primary group.
+
+ Acl - Points to a buffer containing the ACL to interpret.
+
+ UserAccess - Receives the accesses allowed for the user (owner).
+
+ GroupAccess - Receives the accesses allowed for the primary group.
+
+ OtherAccess - Receives the accesses allowed for others (WORLD).
+
+Return Values:
+
+ STATUS_SUCCESS - The service completed successfully.
+
+ STATUS_UNKNOWN_REVISION - The revision level specified is not supported
+ by this service.
+
+ STATUS_EXTRENEOUS_INFORMATION - This warning status value indicates the
+ ACL contained protection or other information unrelated to Posix
+ style protection. This is a warning only. The interpretation was
+ otherwise successful and all access masks were returned.
+
+ STATUS_COULD_NOT_INTERPRET - Indicates the ACL does not contain
+ sufficient Posix style (user/group) protection information. The
+ ACL could not be interpreted.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN UserFound = FALSE;
+ BOOLEAN GroupFound = FALSE;
+ BOOLEAN OtherFound = FALSE;
+ ULONG i;
+ PKNOWN_ACE Ace;
+
+ RTL_PAGED_CODE();
+
+ if (AclRevision != ACL_REVISION2) {
+ return( STATUS_UNKNOWN_REVISION );
+ }
+
+ if (Acl->AceCount > 3) {
+ Status = STATUS_EXTRANEOUS_INFORMATION;
+ }
+
+ for (i=0, Ace = FirstAce( Acl );
+ (i < Acl->AceCount) && (!UserFound || !GroupFound || !OtherFound);
+ i++, Ace = NextAce( Ace )) {
+
+ if (Ace->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
+ Status = STATUS_EXTRANEOUS_INFORMATION;
+ continue;
+ }
+
+ if (RtlEqualSid(
+ (PSID)(Ace->SidStart),
+ UserSid
+ ) && !UserFound) {
+
+ *UserAccess = Ace->Mask;
+ UserFound = TRUE;
+ continue;
+ }
+
+ if (RtlEqualSid(
+ (PSID)(Ace->SidStart),
+ GroupSid
+ ) && !GroupFound) {
+
+ *GroupAccess = Ace->Mask;
+ GroupFound = TRUE;
+ continue;
+ }
+
+ //
+ // It isn't the user, and it isn't the group, pick it up
+ // as "other"
+ //
+
+ if (!OtherFound) {
+ *OtherAccess = Ace->Mask;
+ OtherFound = TRUE;
+ continue;
+ }
+
+ }
+
+ //
+ // Make sure we got everything we need, error otherwise
+ //
+
+ if (!UserFound || !GroupFound || !OtherFound) {
+ Status = STATUS_COULD_NOT_INTERPRET;
+ }
+
+ return( Status );
+
+}
+
+#endif // 0
+
+
+//
+// Internal support routine
+//
+
+BOOLEAN
+RtlFirstFreeAce (
+ IN PACL Acl,
+ OUT PVOID *FirstFree
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to the first free byte in an Acl
+ or NULL if the acl is ill-formed. If the Acl is full then the
+ return pointer is to the byte immediately following the acl, and
+ TRUE will be returned.
+
+Arguments:
+
+ Acl - Supplies a pointer to the Acl to examine
+
+ FirstFree - Receives a pointer to the first free position in the Acl
+
+Return Value:
+
+ BOOLEAN - TRUE if the Acl is well formed and FALSE otherwise
+
+--*/
+
+{
+ PACE_HEADER Ace;
+ ULONG i;
+
+ RTL_PAGED_CODE();
+
+ //
+ // To find the first free spot in the Acl we need to search for
+ // the last ace. We do this by zooming down the list until
+ // we've exhausted the ace count or the ace size (which ever comes
+ // first). In the following loop Ace points to the next spot
+ // for an Ace and I is the ace index
+ //
+
+ *FirstFree = NULL;
+
+ for (i=0, Ace = FirstAce( Acl );
+ i < Acl->AceCount;
+ i++, Ace = NextAce( Ace )) {
+
+ //
+ // Check to make sure we haven't overrun the Acl buffer
+ // with our Ace pointer. If we have then our input is bogus.
+ //
+
+ if (Ace >= (PACE_HEADER)((PUCHAR)Acl + Acl->AclSize)) {
+
+ return FALSE;
+
+ }
+
+ if ( (Ace->AceType == ACCESS_ALLOWED_COMPOUND_ACE_TYPE) && (Acl->AclRevision < ACL_REVISION3) ) {
+
+ return FALSE;
+ }
+ }
+
+ //
+ // Now Ace points to the first free spot in the Acl so set the
+ // output variable and check to make sure it is still in the Acl
+ // or just one beyond the end of the acl (i.e., the acl is full).
+ //
+
+ if (Ace <= (PACE_HEADER)((PUCHAR)Acl + Acl->AclSize)) {
+
+ *FirstFree = Ace;
+ }
+
+ //
+ // The Acl is well formed so return the first free spot we've found
+ // (or NULL if there is no free space for another ACE)
+ //
+
+ return TRUE;
+
+}
+
+
+//
+// Internal support routine
+//
+
+VOID
+RtlpAddData (
+ IN PVOID From,
+ IN ULONG FromSize,
+ IN PVOID To,
+ IN ULONG ToSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies data to a string of bytes. It does this by moving
+ over data in the to string so that the from string will fit. It also
+ assumes that the checks that the data will fit in memory have already
+ been done. Pictorally the results are as follows.
+
+ Before:
+
+ From -> ffffffffff
+
+ To -> tttttttttttttttt
+
+ After:
+
+ From -> ffffffffff
+
+ To -> fffffffffftttttttttttttttt
+
+Arguments:
+
+ From - Supplies a pointer to the source buffer
+
+ FromSize - Supplies the size of the from buffer in bytes
+
+ To - Supplies a pointer to the destination buffer
+
+ ToSize - Supplies the size of the to buffer in bytes
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LONG i;
+
+ //
+ // Shift over the To buffer enough to fit in the From buffer
+ //
+
+ for (i = ToSize - 1; i >= 0; i--) {
+
+ ((PUCHAR)To)[i+FromSize] = ((PUCHAR)To)[i];
+ }
+
+ //
+ // Now copy over the From buffer
+ //
+
+ for (i = 0; (ULONG)i < FromSize; i += 1) {
+
+ ((PUCHAR)To)[i] = ((PUCHAR)From)[i];
+
+ }
+
+ //
+ // and return to our caller
+ //
+
+ return;
+
+}
+
+
+//
+// Internal support routine
+//
+
+VOID
+RtlpDeleteData (
+ IN PVOID Data,
+ IN ULONG RemoveSize,
+ IN ULONG TotalSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes a string of bytes from the front of a data buffer
+ and compresses the data. It also zeros out the part of the string
+ that is no longer in use. Pictorially the results are as follows
+
+ Before:
+
+ Data = DDDDDddddd
+ RemoveSize = 5
+ TotalSize = 10
+
+ After:
+
+ Data = ddddd00000
+
+Arguments:
+
+ Data - Supplies a pointer to the data being altered
+
+ RemoveSize - Supplies the number of bytes to delete from the front
+ of the data buffer
+
+ TotalSize - Supplies the total number of bytes in the data buffer
+ before the delete operation
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG i;
+
+ //
+ // Shift over the buffer to remove the amount
+ //
+
+ for (i = RemoveSize; i < TotalSize; i++) {
+
+ ((PUCHAR)Data)[i-RemoveSize] = ((PUCHAR)Data)[i];
+
+ }
+
+ //
+ // Now as a safety precaution we'll zero out the rest of the string
+ //
+
+ for (i = TotalSize - RemoveSize; i < TotalSize; i++) {
+
+ ((PUCHAR)Data)[i] = 0;
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return;
+
+}
diff --git a/private/ntos/rtl/alpha/capture.s b/private/ntos/rtl/alpha/capture.s
new file mode 100644
index 000000000..027ced3f5
--- /dev/null
+++ b/private/ntos/rtl/alpha/capture.s
@@ -0,0 +1,310 @@
+// TITLE("Capture and Restore Context")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// capture.s
+//
+// Abstract:
+//
+// This module implements the code necessary to capture and restore
+// the context of the caller.
+//
+// Author:
+//
+// David N. Cutler (davec) 14-Sep-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 13-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Capture Context")
+//++
+//
+// VOID
+// RtlCaptureContext (
+// OUT PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This function captures the context of the caller in the specified
+// context record.
+//
+// The context record is assumed to be quadword aligned (since all the
+// members of the record are themselves quadwords).
+//
+// N.B. The stored value of registers a0 and ra will be a side effect of
+// having made this call. All other registers will be stored as they
+// were when the call to this function was made.
+//
+// Arguments:
+//
+// ContextRecord (a0) - Supplies the address of a context record.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlCaptureContext)
+
+//
+// Save all the integer registers.
+//
+
+ .set noreorder
+ .set noat
+ stq v0, CxIntV0(a0) // 0: store integer register v0
+ stq t0, CxIntT0(a0) // 1: store integer registers t0 - t7
+ stq t1, CxIntT1(a0) // 2:
+ stq t2, CxIntT2(a0) // 3:
+ stq t3, CxIntT3(a0) // 4:
+ stq t4, CxIntT4(a0) // 5:
+ stq t5, CxIntT5(a0) // 6:
+ stq t6, CxIntT6(a0) // 7:
+ stq t7, CxIntT7(a0) // 8:
+
+ stq s0, CxIntS0(a0) // 9: store integer registers s0 - s5
+ stq s1, CxIntS1(a0) // 10:
+ stq s2, CxIntS2(a0) // 11:
+ stq s3, CxIntS3(a0) // 12:
+ stq s4, CxIntS4(a0) // 13:
+ stq s5, CxIntS5(a0) // 14:
+ stq fp, CxIntFp(a0) // 15: store integer register fp/s6
+
+ stq a0, CxIntA0(a0) // 16: store integer registers a0 - a5
+ stq a1, CxIntA1(a0) // 17:
+ stq a2, CxIntA2(a0) // 18:
+ stq a3, CxIntA3(a0) // 19:
+ stq a4, CxIntA4(a0) // 20:
+ stq a5, CxIntA5(a0) // 21:
+
+ stq t8, CxIntT8(a0) // 22: store integer registers t8 - t11
+ stq t9, CxIntT9(a0) // 23:
+ stq t10, CxIntT10(a0) // 24:
+ stq t11, CxIntT11(a0) // 25:
+
+ stq ra, CxIntRa(a0) // 26: store integer register ra
+ stq t12, CxIntT12(a0) // 27: store integer register t12
+ stq AT, CxIntAt(a0) // 28: store integer register at
+ stq gp, CxIntGp(a0) // 29: store integer register gp
+ stq sp, CxIntSp(a0) // 30: store integer register sp
+ stq zero, CxIntZero(a0) // 31: store integer register zero
+
+//
+// Save all the floating registers, and the floating control register.
+//
+
+ stt f0, CxFltF0(a0) // store floating registers f0 - f31
+ stt f1, CxFltF1(a0) //
+ stt f2, CxFltF2(a0) //
+ stt f3, CxFltF3(a0) //
+ stt f4, CxFltF4(a0) //
+ stt f5, CxFltF5(a0) //
+ stt f6, CxFltF6(a0) //
+ stt f7, CxFltF7(a0) //
+ stt f8, CxFltF8(a0) //
+ stt f9, CxFltF9(a0) //
+ stt f10, CxFltF10(a0) //
+ stt f11, CxFltF11(a0) //
+ stt f12, CxFltF12(a0) //
+ stt f13, CxFltF13(a0) //
+ stt f14, CxFltF14(a0) //
+ stt f15, CxFltF15(a0) //
+ stt f16, CxFltF16(a0) //
+ stt f17, CxFltF17(a0) //
+ stt f18, CxFltF18(a0) //
+ stt f19, CxFltF19(a0) //
+ stt f20, CxFltF20(a0) //
+ stt f21, CxFltF21(a0) //
+ stt f22, CxFltF22(a0) //
+ stt f23, CxFltF23(a0) //
+ stt f24, CxFltF24(a0) //
+ stt f25, CxFltF25(a0) //
+ stt f26, CxFltF26(a0) //
+ stt f27, CxFltF27(a0) //
+ stt f28, CxFltF28(a0) //
+ stt f29, CxFltF29(a0) //
+ stt f30, CxFltF30(a0) //
+ stt f31, CxFltF31(a0) //
+
+//
+// Save control information and set context flags.
+//
+
+ mf_fpcr f0, f0, f0 // get floating point control register
+ stt f0, CxFpcr(a0) // store it
+ ldt f0, CxFltF0(a0) // restore original f0 value
+ stq ra, CxFir(a0) // set continuation address
+ .set at
+ .set reorder
+
+//
+// If called from user mode set a known fixed PSR value for user mode. If
+// called from kernel mode we are able to execute the privileged call pal
+// to get the actual PSR.
+//
+
+ ldil v0, PSR_USER_MODE // set constant user processor status
+ bgt sp, 10f // if sp > 0, call came from user mode
+
+ GET_CURRENT_PROCESSOR_STATUS_REGISTER // get contents of PSR in v0
+
+10: stl v0, CxPsr(a0) // store processor status
+ ldil v0, CONTEXT_FULL // set context control flags
+ stl v0, CxContextFlags(a0) // store it
+ ret zero, (ra) // return
+
+ .end RtlCaptureContext
+
+ SBTTL("Restore Context")
+//++
+//
+// VOID
+// RtlpRestoreContext (
+// IN PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This function restores the context of the caller to the specified
+// context.
+//
+// The context record is assumed to be quadword aligned (since all the
+// members of the record are themselves quadwords).
+//
+// N.B. This is a special routine that is used by RtlUnwind to restore
+// context in the current mode. PSR, t0, and t1 are not restored.
+//
+// Arguments:
+//
+// ContextRecord (a0) - Supplies the address of a context record.
+//
+// Return Value:
+//
+// None.
+//
+// N.B. There is no return from this routine.
+//
+//--
+
+ LEAF_ENTRY(RtlpRestoreContext)
+
+//
+// If the call is from user mode, then use the continue system service to
+// continue execution. Otherwise, restore the context directly since the
+// current mode is kernel and threads can't be arbitrarily interrupted.
+//
+
+ blt ra, 10f // if ra < 0, kernel mode restore
+ ldil a1, FALSE // set test alert argument false
+ bsr ra, ZwContinue // continue execution
+
+//
+// Save the address of the context record and continuation address in
+// registers t0 and t1. These registers are not restored.
+//
+
+10: mov a0, t0 // save context record address
+ ldq t1, CxFir(t0) // get continuation address
+
+//
+// Restore floating control and floating registers f0 - f30.
+//
+
+ .set noreorder
+ .set noat
+ ldt f0, CxFpcr(t0) // get floating point control register
+ mt_fpcr f0, f0, f0 // load register
+
+ ldt f0, CxFltF0(t0) // restore floating registers f0 - f30
+ ldt f1, CxFltF1(t0) //
+ ldt f2, CxFltF2(t0) //
+ ldt f3, CxFltF3(t0) //
+ ldt f4, CxFltF4(t0) //
+ ldt f5, CxFltF5(t0) //
+ ldt f6, CxFltF6(t0) //
+ ldt f7, CxFltF7(t0) //
+ ldt f8, CxFltF8(t0) //
+ ldt f9, CxFltF9(t0) //
+ ldt f10, CxFltF10(t0) //
+ ldt f11, CxFltF11(t0) //
+ ldt f12, CxFltF12(t0) //
+ ldt f13, CxFltF13(t0) //
+ ldt f14, CxFltF14(t0) //
+ ldt f15, CxFltF15(t0) //
+ ldt f16, CxFltF16(t0) //
+ ldt f17, CxFltF17(t0) //
+ ldt f18, CxFltF18(t0) //
+ ldt f19, CxFltF19(t0) //
+ ldt f20, CxFltF20(t0) //
+ ldt f21, CxFltF21(t0) //
+ ldt f22, CxFltF22(t0) //
+ ldt f23, CxFltF23(t0) //
+ ldt f24, CxFltF24(t0) //
+ ldt f25, CxFltF25(t0) //
+ ldt f26, CxFltF26(t0) //
+ ldt f27, CxFltF27(t0) //
+ ldt f28, CxFltF28(t0) //
+ ldt f29, CxFltF29(t0) //
+ ldt f30, CxFltF30(t0) //
+
+//
+// Restore integer registers and continue execution.
+// N.B. Integer registers t0 and t1 cannot be restored by this function.
+//
+
+ ldq v0, CxIntV0(t0) // restore integer register v0
+ ldq t2, CxIntT2(t0) // restore integer registers t2 - t7
+ ldq t3, CxIntT3(t0) //
+ ldq t4, CxIntT4(t0) //
+ ldq t5, CxIntT5(t0) //
+ ldq t6, CxIntT6(t0) //
+ ldq t7, CxIntT7(t0) //
+
+ ldq s0, CxIntS0(t0) // restore integer registers s0 - s5
+ ldq s1, CxIntS1(t0) //
+ ldq s2, CxIntS2(t0) //
+ ldq s3, CxIntS3(t0) //
+ ldq s4, CxIntS4(t0) //
+ ldq s5, CxIntS5(t0) //
+ ldq fp, CxIntFp(t0) // restore integer register fp/s6
+
+ ldq a0, CxIntA0(t0) // restore integer registers a0 - a5
+ ldq a1, CxIntA1(t0) //
+ ldq a2, CxIntA2(t0) //
+ ldq a3, CxIntA3(t0) //
+ ldq a4, CxIntA4(t0) //
+ ldq a5, CxIntA5(t0) //
+
+ ldq t8, CxIntT8(t0) // restore integer registers t8 - t11
+ ldq t9, CxIntT9(t0) //
+ ldq t10, CxIntT10(t0) //
+ ldq t11, CxIntT11(t0) //
+
+ ldq ra, CxIntRa(t0) // restore integer register ra
+ ldq t12, CxIntT12(t0) // restore integer register t12
+ ldq AT, CxIntAt(t0) // restore integer register at
+ ldq gp, CxIntGp(t0) // restore integer register gp
+ ldq sp, CxIntSp(t0) // restore integer register sp
+
+ jmp zero, (t1) // continue execution
+ .set at
+ .set reorder
+
+ .end RtlpRestoreContext
diff --git a/private/ntos/rtl/alpha/chandler.c b/private/ntos/rtl/alpha/chandler.c
new file mode 100644
index 000000000..07b6e3c6b
--- /dev/null
+++ b/private/ntos/rtl/alpha/chandler.c
@@ -0,0 +1,297 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ chandler.c
+
+Abstract:
+
+ This module implements the C specific exception handler that provides
+ structured condition handling for the C language.
+
+Author:
+
+ David N. Cutler (davec) 11-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Thomas Van Baak (tvb) 29-Apr-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+#include "nt.h"
+
+//
+// Define procedure prototypes for exception filter and termination handler
+// execution routines defined in jmpuwind.s.
+//
+
+LONG
+__C_ExecuteExceptionFilter (
+ PEXCEPTION_POINTERS ExceptionPointers,
+ EXCEPTION_FILTER ExceptionFilter,
+ ULONG EstablisherFrame
+ );
+
+VOID
+__C_ExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+EXCEPTION_DISPOSITION
+__C_specific_handler (
+ IN struct _EXCEPTION_RECORD *ExceptionRecord,
+ IN void *EstablisherFrame,
+ IN OUT struct _CONTEXT *ContextRecord,
+ IN OUT struct _DISPATCHER_CONTEXT *DispatcherContext
+ )
+
+/*++
+
+Routine Description:
+
+ This function scans the scope tables associated with the specified
+ procedure and calls exception and termination handlers as necessary.
+
+ This language specific exception handler function is called on a
+ per-frame basis and in two different cases:
+
+ First, the IS_DISPATCHING case, it is called by the exception
+ dispatcher, RtlDispatchException, via the short assembler routine,
+ __C_ExecuteHandlerForException, when trying to locate exception
+ filters within the given frame.
+
+ Second, the IS_UNWINDING case, it is called by the frame unwinder,
+ RtlUnwind, via the short assembler routine, __C_ExecuteHandlerForUnwind,
+ when unwinding the stack and trying to locate termination handlers
+ within the given frame.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ EstablisherFrame - Supplies a pointer to frame of the establisher function.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+ DispatcherContext - Supplies a pointer to the exception dispatcher or
+ unwind dispatcher context.
+
+Return Value:
+
+ If the exception is handled by one of the exception filter routines, then
+ there is no return from this routine and RtlUnwind is called. Otherwise,
+ an exception disposition value of continue execution or continue search is
+ returned.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ EXCEPTION_FILTER ExceptionFilter;
+ EXCEPTION_POINTERS ExceptionPointers;
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG Index;
+ PSCOPE_TABLE ScopeTable;
+ ULONG TargetPc;
+ TERMINATION_HANDLER TerminationHandler;
+ LONG Value;
+
+ //
+ // Get the address of where control left the establisher, the address of
+ // the function table entry that describes the function, and the address of
+ // the scope table.
+ //
+
+ ControlPc = DispatcherContext->ControlPc;
+ FunctionEntry = DispatcherContext->FunctionEntry;
+ ScopeTable = (PSCOPE_TABLE)(FunctionEntry->HandlerData);
+
+ //
+ // The scope table HandlerAddress is either the address of an exception
+ // filter or a termination handler. The C compiler wraps the code in the
+ // exception filter expression or the termination handler clause within
+ // an internal C function. The value of the scope table JumpTarget field
+ // is used to distinguish an exception filter function (JumpTarget non zero)
+ // from a termination handler function (JumpTarget is zero).
+ //
+
+ //
+ // If an unwind is not in progress, then scan the scope table and call
+ // the appropriate exception filter routines. Otherwise, scan the scope
+ // table and call the appropriate termination handlers using the target
+ // PC obtained from the context record.
+ //
+
+ if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // Set up the ExceptionPointers structure that is passed as the argument
+ // to the exception filter. It is used by the compiler to implement the
+ // intrinsic functions exception_code() and exception_info().
+ //
+
+ ExceptionPointers.ExceptionRecord = ExceptionRecord;
+ ExceptionPointers.ContextRecord = ContextRecord;
+
+ //
+ // Scan the scope table and call the appropriate exception filter
+ // routines. The scope table entries are known to be sorted by
+ // increasing EndAddress order. Thus a linear scan will naturally
+ // hit inner scope exception clauses before outer scope clauses.
+ //
+
+ for (Index = 0; Index < ScopeTable->Count; Index += 1) {
+ if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
+ (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress) &&
+ (ScopeTable->ScopeRecord[Index].JumpTarget != 0)) {
+
+ //
+ // Call the exception filter routine.
+ //
+
+ ExceptionFilter =
+ (EXCEPTION_FILTER)ScopeTable->ScopeRecord[Index].HandlerAddress;
+ Value = __C_ExecuteExceptionFilter(&ExceptionPointers,
+ ExceptionFilter,
+ (ULONG)EstablisherFrame);
+
+ //
+ // If the return value is less than zero, then dismiss the
+ // exception. Otherwise, if the value is greater than zero,
+ // then unwind to the target exception handler corresponding
+ // to the exception filter. Otherwise, continue the search for
+ // an exception filter.
+ //
+
+ //
+ // Exception filters will usually return one of the following
+ // defines, although the decision below is made only by sign:
+ //
+ // #define EXCEPTION_EXECUTE_HANDLER 1
+ // #define EXCEPTION_CONTINUE_SEARCH 0
+ // #define EXCEPTION_CONTINUE_EXECUTION -1
+ //
+
+ if (Value < 0) {
+ return ExceptionContinueExecution;
+
+ } else if (Value > 0) {
+
+ //
+ // Set the return value for the unwind to the exception
+ // code so the exception handler clause can retrieve it
+ // from v0. This is how GetExceptionCode() is implemented
+ // in exception handler clauses.
+ //
+
+ RtlUnwind2(EstablisherFrame,
+ (PVOID)ScopeTable->ScopeRecord[Index].JumpTarget,
+ ExceptionRecord,
+ (PVOID)ExceptionRecord->ExceptionCode,
+ ContextRecord);
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // Scan the scope table and call the appropriate termination handler
+ // routines.
+ //
+
+ TargetPc = (ULONG)ContextRecord->Fir;
+ for (Index = 0; Index < ScopeTable->Count; Index += 1) {
+ if ((ControlPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
+ (ControlPc < ScopeTable->ScopeRecord[Index].EndAddress)) {
+
+ //
+ // If the target PC is within the same scope the control PC
+ // is within, then this is an uplevel goto out of an inner try
+ // scope or a long jump back into a try scope. Terminate the
+ // scan for termination handlers - because any other handlers
+ // will be outside the scope of both the goto and its label.
+ //
+ // N.B. The target PC can be just beyond the end of the scope,
+ // in which case it is a leave from the scope.
+ //
+
+ if ((TargetPc >= ScopeTable->ScopeRecord[Index].BeginAddress) &&
+ (TargetPc <= ScopeTable->ScopeRecord[Index].EndAddress)) {
+ break;
+
+ } else {
+
+ //
+ // If the scope table entry describes an exception filter
+ // and the associated exception handler is the target of
+ // the unwind, then terminate the scan for termination
+ // handlers. Otherwise, if the scope table entry describes
+ // a termination handler, then record the address of the
+ // end of the scope as the new control PC address and call
+ // the termination handler.
+ //
+ // Recording a new control PC is necessary to ensure that
+ // termination handlers are called only once even when a
+ // collided unwind occurs.
+ //
+
+ if (ScopeTable->ScopeRecord[Index].JumpTarget != 0) {
+
+ //
+ // try/except - exception filter (JumpTarget != 0).
+ // After the exception filter is called, the exception
+ // handler clause is executed by the call to unwind
+ // above. Having reached this point in the scan of the
+ // scope tables, any other termination handlers will
+ // be outside the scope of the try/except.
+ //
+
+ if (TargetPc == ScopeTable->ScopeRecord[Index].JumpTarget) {
+ break;
+ }
+
+ } else {
+
+ //
+ // try/finally - termination handler (JumpTarget == 0).
+ //
+
+ //
+ // Unless the termination handler results in a long
+ // jump, execution will resume at the instruction after
+ // the exception handler clause.
+ //
+ // ## tvb - I'm still suspicious of the +4 below.
+
+ DispatcherContext->ControlPc =
+ ScopeTable->ScopeRecord[Index].EndAddress + 4;
+ TerminationHandler =
+ (TERMINATION_HANDLER)ScopeTable->ScopeRecord[Index].HandlerAddress;
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ (ULONG)EstablisherFrame);
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Continue search for exception filters or termination handlers.
+ //
+
+ return ExceptionContinueSearch;
+}
diff --git a/private/ntos/rtl/alpha/chkstk.s b/private/ntos/rtl/alpha/chkstk.s
new file mode 100644
index 000000000..749af86a7
--- /dev/null
+++ b/private/ntos/rtl/alpha/chkstk.s
@@ -0,0 +1,131 @@
+// TITLE("Runtime Stack Checking")
+//++
+//
+// Copyright (c) 1991 Microsoft Corporation
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// chkstk.s
+//
+// Abstract:
+//
+// This module implements runtime stack checking.
+//
+// Author:
+//
+// David N. Cutler (davec) 14-Mar-1991
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 7-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Check Stack")
+//++
+//
+// ULONG
+// _RtlCheckStack (
+// IN ULONG Allocation
+// )
+//
+// Routine Description:
+//
+// This function provides runtime stack checking for local allocations
+// that are more than a page and for storage dynamically allocated with
+// the alloca function. Stack checking consists of probing downward in
+// the stack a page at a time. If the current stack commitment is exceeded,
+// then the system will automatically attempts to expand the stack. If the
+// attempt succeeds, then another page is committed. Otherwise, a stack
+// overflow exception is raised. It is the responsibility of the caller to
+// handle this exception.
+//
+// N.B. This routine is called using a non-standard calling sequence since
+// it is typically called from within the prologue. The allocation size
+// argument is in register t12 and it must be preserved. Register t11
+// may contain the callers saved ra and it must be preserved. The choice
+// of these registers is hard-coded into the acc C compiler. Register v0
+// may contain a static link pointer (exception handlers) and so it must
+// be preserved. Since this function is called from within the prolog,
+// the a' registers must be preserved, as well as all the s' registers.
+// Registers t8, t9, and t10 are used by this function and are not
+// preserved.
+//
+// The typical calling sequence from the prologue is:
+//
+// mov ra, t11 // save return address
+// ldil t12, SIZE // set requested stack frame size
+// bsr ra, _RtlCheckStack // check stack page allocation
+// subq sp, t12, sp // allocate stack frame
+// mov t11, ra // restore return address
+//
+// Arguments:
+//
+// Allocation (t12) - Supplies the size of the allocation on the stack.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(_RtlCheckStack)
+
+ subq sp, t12, t8 // compute requested new stack address
+ mov v0, t10 // save v0 since the PALcode uses it
+#if defined(NTOS_KERNEL_RUNTIME)
+
+//
+// Running on kernel stack - get stack limit from thread object
+//
+
+ GET_CURRENT_THREAD // current thread in v0
+ ldl t9, ThStackLimit(v0)
+
+#else
+//
+// Running on user stack - get stack limit from thread environment block.
+//
+
+10: GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) put TEB address in v0
+
+ ldl t9, TeStackLimit(v0) // get low limit of user stack address
+#endif
+
+//
+// The requested bottom of the stack is in t8.
+// The current low limit of the stack is in t9.
+//
+// If the new stack address is greater than the current stack limit, then the
+// pages have already been allocated, and nothing further needs to be done.
+//
+
+ mov t10, v0 // restore v0, no further PAL calls
+ cmpult t8, t9, t10 // t8<t9? new stack base within limit?
+ beq t10, 40f // if eq [false], then t8>=t9, so yes
+
+ ldil t10, ~(PAGE_SIZE - 1) // round down new stack address
+ and t8, t10, t8 // to next page boundary
+
+//
+// Go down one page, touch one quadword in it, and repeat until we reach the
+// new stack limit.
+//
+
+30: lda t9, -PAGE_SIZE(t9) // compute next address to check
+ stq zero, 0(t9) // probe stack address with a write
+ cmpeq t8, t9, t10 // t8=t9? at the low limit yet?
+ beq t10, 30b // if eq [false], more pages to probe
+
+40: ret zero, (ra) // return
+
+ .end _RtlCheckStack
diff --git a/private/ntos/rtl/alpha/context.c b/private/ntos/rtl/alpha/context.c
new file mode 100644
index 000000000..dfcf0aa1e
--- /dev/null
+++ b/private/ntos/rtl/alpha/context.c
@@ -0,0 +1,330 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ context.c
+
+Abstract:
+
+ This module implements user-mode callable context manipulation routines.
+
+Author:
+
+ Mark Lucovsky (markl) 20-Jun-1989
+
+Revision History:
+
+ David N. Cutler (davec) 18-Apr-1990
+
+ Revise for MIPS environment.
+
+ Thomas Van Baak (tvb) 11-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <alphaops.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:
+
+ Process - Supplies an open handle to the target process. This argument
+ is ignored by this function.
+
+ Context - Supplies a pointer to a context record that is to be initialized.
+
+ Parameter - Supplies an initial value for register A0.
+
+ InitialPc - Supplies an initial program counter value.
+
+ InitialSp - Supplies an initial stack pointer value.
+
+Return Value:
+
+ Raises STATUS_BAD_INITIAL_STACK if the value of InitialSp is not properly
+ aligned.
+
+ Raises STATUS_BAD_INITIAL_PC if the value of InitialPc is not properly
+ aligned.
+
+--*/
+
+{
+
+ //
+ // Check for proper initial stack and PC alignment.
+ //
+
+ if (((ULONG)InitialSp & 0xF) != 0) {
+ RtlRaiseStatus(STATUS_BAD_INITIAL_STACK);
+ }
+ if (((ULONG)InitialPc & 0x3) != 0) {
+ RtlRaiseStatus(STATUS_BAD_INITIAL_PC);
+ }
+
+ //
+ // Initialize the integer registers to contain their register number
+ // (they must be initialized and using register numbers instead of 0
+ // can be useful for debugging). Integer registers a0, ra, gp, and sp
+ // are set later.
+ //
+
+ Context->IntV0 = 0;
+ Context->IntT0 = 1;
+ Context->IntT1 = 2;
+ Context->IntT2 = 3;
+ Context->IntT3 = 4;
+ Context->IntT4 = 5;
+ Context->IntT5 = 6;
+ Context->IntT6 = 7;
+ Context->IntT7 = 8;
+ Context->IntS0 = 9;
+ Context->IntS1 = 10;
+ Context->IntS2 = 11;
+ Context->IntS3 = 12;
+ Context->IntS4 = 13;
+ Context->IntS5 = 14;
+ Context->IntFp = 15;
+ Context->IntA1 = 17;
+ Context->IntA2 = 18;
+ Context->IntA3 = 19;
+ Context->IntA4 = 20;
+ Context->IntA5 = 21;
+ Context->IntT8 = 22;
+ Context->IntT9 = 23;
+ Context->IntT10 = 24;
+ Context->IntT11 = 25;
+ Context->IntT12 = 27;
+ Context->IntAt = 28;
+
+ //
+ // Initialize the floating point registers to contain the integer value
+ // of their register number (they must be initialized and using register
+ // numbers instead of 0 can be useful for debugging).
+ //
+
+ Context->FltF0 = 0;
+ Context->FltF1 = 1;
+ Context->FltF2 = 2;
+ Context->FltF3 = 3;
+ Context->FltF4 = 4;
+ Context->FltF5 = 5;
+ Context->FltF6 = 6;
+ Context->FltF7 = 7;
+ Context->FltF8 = 8;
+ Context->FltF9 = 9;
+ Context->FltF10 = 10;
+ Context->FltF11 = 11;
+ Context->FltF12 = 12;
+ Context->FltF13 = 13;
+ Context->FltF14 = 14;
+ Context->FltF15 = 15;
+ Context->FltF16 = 16;
+ Context->FltF17 = 17;
+ Context->FltF18 = 18;
+ Context->FltF19 = 19;
+ Context->FltF20 = 20;
+ Context->FltF21 = 21;
+ Context->FltF22 = 22;
+ Context->FltF23 = 23;
+ Context->FltF24 = 24;
+ Context->FltF25 = 25;
+ Context->FltF26 = 26;
+ Context->FltF27 = 27;
+ Context->FltF28 = 28;
+ Context->FltF29 = 29;
+ Context->FltF30 = 30;
+ Context->FltF31 = 0;
+
+ //
+ // Initialize the control registers.
+ //
+ // Gp: will be set in LdrpInitialize at thread startup.
+ // Ra: some debuggers compare for 1 as a top-of-stack indication.
+ //
+ // N.B. ULONG becomes canonical longword with (ULONGLONG)(LONG) cast.
+ //
+
+ Context->IntGp = 0;
+ Context->IntSp = (ULONGLONG)(LONG)InitialSp;
+ Context->IntRa = 1;
+ Context->Fir = (ULONGLONG)(LONG)InitialPc;
+
+ //
+ // Set default Alpha floating point control register values.
+ //
+
+ Context->Fpcr = (ULONGLONG)0;
+ ((PFPCR)(&Context->Fpcr))->DynamicRoundingMode = ROUND_TO_NEAREST;
+ Context->SoftFpcr = (ULONGLONG)0;
+
+ Context->Psr = 0;
+ Context->ContextFlags = CONTEXT_FULL;
+
+ //
+ // Set the initial context of the thread in a machine specific way.
+ //
+
+ Context->IntA0 = (ULONGLONG)(LONG)Parameter;
+}
+
+NTSTATUS
+RtlRemoteCall(
+ HANDLE Process,
+ HANDLE Thread,
+ PVOID CallSite,
+ ULONG ArgumentCount,
+ PULONG Arguments,
+ BOOLEAN PassContext,
+ BOOLEAN AlreadySuspended
+ )
+
+/*++
+
+Routine Description:
+
+ This function calls a procedure in another thread/process by using
+ NtGetContext and NtSetContext. Parameters are passed to the target
+ procedure via the nonvolatile registers (s0 - s5).
+
+Arguments:
+
+ Process - Supplies an open handle to the target process.
+
+ Thread - Supplies an open handle to the target thread within the target
+ process.
+
+ CallSite - Supplies the address of the procedure to call in the target
+ process.
+
+ ArgumentCount - Supplies the number of 32 bit parameters to pass to the
+ target procedure.
+
+ Arguments - Supplies a pointer to the array of 32 bit parameters to pass.
+
+ PassContext - Supplies a boolean value that determines whether a parameter
+ is to be passed that points to a context record. This parameter is
+ ignored on MIPS and Alpha hosts.
+
+ AlreadySuspended - Supplies a boolean value that determines whether the
+ target thread is already in a suspended or waiting state.
+
+Return Value:
+
+ Status - Status value.
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ CONTEXT Context;
+ ULONG Index;
+ ULONGLONG NewSp;
+
+ if ((ArgumentCount > 6) ||
+ (PassContext && (ArgumentCount > 5))) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // If necessary, suspend the target thread before getting the thread's
+ // current state.
+ //
+
+ if (AlreadySuspended == FALSE) {
+ Status = NtSuspendThread(Thread, NULL);
+ if (NT_SUCCESS(Status) == FALSE) {
+ return(Status);
+ }
+ }
+
+ //
+ // Get the current state of the target thread.
+ //
+
+ Context.ContextFlags = CONTEXT_FULL;
+ Status = NtGetContextThread(Thread, &Context);
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+ }
+
+ if (AlreadySuspended) {
+ Context.IntV0 = STATUS_ALERTED;
+ }
+
+ //
+ // Pass the parameters to the other thread via the non-volatile registers
+ // s0 - s5. The context record is passed on the stack of the target thread.
+ //
+
+ NewSp = Context.IntSp - sizeof(CONTEXT);
+ Status = NtWriteVirtualMemory(Process, (PVOID)NewSp, &Context,
+ sizeof(CONTEXT), NULL);
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+ }
+
+ //
+ // N.B. Each ULONG argument is converted to canonical form with the
+ // (ULONGLONG)(LONG) cast as required by the calling standard.
+ //
+ // N.B. Given the PULONG Arguments prototype, this function cannot pass
+ // quadword arguments (including structures such as LARGE_INTEGER).
+ //
+
+ Context.IntSp = NewSp;
+
+ if (PassContext) {
+ Context.IntS0 = NewSp;
+ for (Index = 0; Index < ArgumentCount; Index += 1) {
+ (&Context.IntS1)[Index] = (ULONGLONG)(LONG)Arguments[Index];
+ }
+
+ } else {
+ for (Index = 0; Index < ArgumentCount; Index += 1) {
+ (&Context.IntS0)[Index] = (ULONGLONG)(LONG)Arguments[Index];
+ }
+ }
+
+ //
+ // Set the address of the target code into FIR and set the thread context
+ // to cause the target procedure to be executed.
+ //
+ // N.B. The PVOID CallSite is stored as a canonical longword in order
+ // for Fir to be a valid 64-bit address.
+ //
+
+ Context.Fir = (ULONGLONG)(LONG)CallSite;
+ Status = NtSetContextThread(Thread, &Context);
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+}
diff --git a/private/ntos/rtl/alpha/debugstb.s b/private/ntos/rtl/alpha/debugstb.s
new file mode 100644
index 000000000..5847eb47f
--- /dev/null
+++ b/private/ntos/rtl/alpha/debugstb.s
@@ -0,0 +1,269 @@
+// TITLE("Debug Support Functions")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+//
+// Module Name:
+//
+// debug.s
+//
+// Abstract:
+//
+// This module implements functions to support debugging NT. Each
+// function executes a specific call pal which is interpreted by the
+// kernel debugger.
+//
+// Author:
+//
+// Steven R. Wood (stevewo) 3-Aug-1989
+// Joe Notarangelo 24-Jun-1992
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+//++
+//
+// VOID
+// DbgBreakPoint()
+//
+// Routine Description:
+//
+// This function executes a breakpoint instruction. Useful for entering
+// the debugger under program control. This breakpoint will always go to
+// the kernel debugger if one is installed, otherwise it will go to the
+// debug subsystem.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(DbgBreakPoint)
+
+ BREAK_DEBUG_STOP // debug stop breakpoint
+ ret zero, (ra) // return
+
+ .end DbgBreakPoint
+
+//++
+//
+// VOID
+// DbgBreakPointWithStatus(
+// IN ULONG Status
+// )
+//
+// Routine Description:
+//
+// This function executes a breakpoint instruction. Useful for entering
+// the debugger under program control. This breakpoint will always go to
+// the kernel debugger if one is installed, otherwise it will go to the
+// debug subsystem. This function is identical to DbgBreakPoint, except
+// that it takes an argument which the debugger can see.
+//
+// Arguments:
+//
+// A status code.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(DbgBreakPointWithStatus)
+
+
+ ALTERNATE_ENTRY(RtlpBreakWithStatusInstruction)
+ BREAK_DEBUG_STOP // debug stop breakpoint
+ ret zero, (ra) // return
+
+ .end DbgBreakPointWithStatus
+
+//++
+//
+// VOID
+// DbgUserBreakPoint()
+//
+// Routine Description:
+//
+// This function executes a breakpoint instruction. Useful for entering
+// the debug subsystem under program control. The kernel debug will ignore
+// this breakpoint since it will not find the instruction address in its
+// breakpoint table.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// None.
+//
+//--
+ LEAF_ENTRY(DbgUserBreakPoint)
+
+ BREAK // issue user breakpoint
+ ret zero, (ra) // return
+
+ .end DbgUserBreakPoint
+
+//++
+//
+// NTSTATUS
+// DebugPrint(
+// IN PSTRING Output
+// )
+//
+// Routine Description:
+//
+// This function executes a debug print breakpoint.
+//
+// Arguments:
+//
+// Output (a0) - Supplies a pointer to the output string descriptor.
+//
+// Return Value:
+//
+// Status code. STATUS_SUCCESS if debug print happened.
+// STATUS_BREAKPOINT if user typed a Control-C during print.
+// STATUS_DEVICE_NOT_CONNECTED if kernel debugger not present.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugPrint)
+
+ ldwu a1, StrLength(a0) // set length of output string
+ ldl a0, StrBuffer(a0) // set address of output string
+ BREAK_DEBUG_PRINT // execute a debug print breakpoint
+ ret zero, (ra) // return
+ .end DebugPrint
+
+#endif
+
+//++
+//
+// ULONG
+// DebugPrompt(
+// IN PSTRING Output,
+// IN PSTRING Input
+// )
+//
+// Routine Description:
+//
+// This function executes a debug prompt breakpoint.
+//
+// Arguments:
+//
+// Output (a0) - Supplies a pointer to the output string descriptor.
+//
+// Input (a1) - Supplies a pointer to the input string descriptor.
+//
+// Return Value:
+//
+// The length of the input string is returned as the function value.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugPrompt)
+
+ ldwu a3,StrMaximumLength(a1) // set maximum length of input string
+ ldl a2,StrBuffer(a1) // set address of input string
+ ldwu a1,StrLength(a0) // set length of output string
+ ldl a0,StrBuffer(a0) // set address of output string
+ BREAK_DEBUG_PROMPT
+ ret zero, (ra)
+
+ .end DebugPrompt
+
+#endif
+
+
+//++
+//
+// VOID
+// DebugLoadImageSymbols(
+// IN PSTRING ImagePathName,
+// IN PKD_SYMBOLS_INFO SymbolInfo
+// )
+//
+// Routine Description:
+//
+// This function calls the kernel debugger to load the symbol
+// table for the specified image.
+//
+// Arguments:
+//
+// ImagePathName - specifies the fully qualified path name of the image
+// file that has been loaded into an NT address space.
+//
+// SymbolInfo - information captured from header of image file.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugLoadImageSymbols)
+
+ BREAK_DEBUG_LOAD_SYMBOLS
+ ret zero, (ra)
+
+ .end DebugLoadImageSymbols
+
+#endif
+
+//++
+//
+// VOID
+// DebugUnLoadImageSymbols(
+// IN PSTRING ImagePathName,
+// IN PKD_SYMBOLS_INFO SymbolInfo
+// )
+//
+// Routine Description:
+//
+// This function calls the kernel debugger to unload the symbol
+// table for the specified image.
+//
+// Arguments:
+//
+// ImagePathName - specifies the fully qualified path name of the image
+// file that has been unloaded from an NT address space.
+//
+// SymbolInfo - information captured from header of image file.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+#if DEVL
+
+ LEAF_ENTRY(DebugUnLoadImageSymbols)
+
+ BREAK_DEBUG_UNLOAD_SYMBOLS
+ ret zero, (ra)
+
+ .end DebugUnLoadImageSymbols
+
+#endif
diff --git a/private/ntos/rtl/alpha/exdsptch.c b/private/ntos/rtl/alpha/exdsptch.c
new file mode 100644
index 000000000..298073e6a
--- /dev/null
+++ b/private/ntos/rtl/alpha/exdsptch.c
@@ -0,0 +1,2335 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ exdsptch.c
+
+Abstract:
+
+ This module implements the dispatching of exceptions and the unwinding of
+ procedure call frames.
+
+Author:
+
+ David N. Cutler (davec) 11-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Thomas Van Baak (tvb) 13-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Define local macros.
+//
+// Raise noncontinuable exception with associated exception record.
+//
+
+#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \
+ EXCEPTION_RECORD ExceptionRecordn; \
+ \
+ ExceptionRecordn.ExceptionCode = Status; \
+ ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \
+ ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \
+ ExceptionRecordn.NumberParameters = 0; \
+ RtlRaiseException(&ExceptionRecordn); \
+ }
+
+//
+// The low 2 bits of ExceptionHandler are flags bits and not part of the
+// exception handler address.
+//
+
+#define IS_HANDLER_DEFINED(FunctionEntry) \
+ (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0)
+
+
+#if DBG
+
+//
+// Maintain a short history of PC's for malformed function table errors.
+//
+
+#define PC_HISTORY_DEPTH 4
+
+//
+// Definition of global flag to debug/validate exception handling.
+// See ntrtlalp.h for the bit definitions in this flag word.
+//
+
+ULONG RtlDebugFlags = 0;
+
+#endif
+
+#define Virtual VirtualFramePointer
+#define Real RealFramePointer
+
+//
+// Define private function prototypes.
+//
+
+VOID
+RtlpRaiseException (
+ IN PEXCEPTION_RECORD ExceptionRecord
+ );
+
+VOID
+RtlpRaiseStatus (
+ IN NTSTATUS Status
+ );
+
+ULONG
+RtlpVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame
+ );
+
+BOOLEAN
+RtlDispatchException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to dispatch an exception to a frame based
+ handler by searching backwards through the stack based call frames.
+ The search begins with the frame specified in the context record and
+ continues backward until either a handler is found that handles the
+ exception, the stack is found to be invalid (i.e., out of limits or
+ unaligned), or the end of the call hierarchy is reached.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called. If the
+ handler does not handle the exception, then the prologue of the routine
+ is executed backwards to "unwind" the effect of the prologue and then
+ the next frame is examined.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+Return Value:
+
+ If the exception is handled by one of the frame based handlers, then
+ a value of TRUE is returned. Otherwise a value of FALSE is returned.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LowLimit;
+ ULONG NestedFrame;
+ ULONG NextPc;
+
+ //
+ // Get current stack limits, copy the context record, get the initial
+ // PC value, capture the exception flags, and set the nested exception
+ // frame pointer.
+ //
+ // The initial PC value is obtained from ExceptionAddress rather than
+ // from ContextRecord.Fir since some Alpha exceptions are asynchronous.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlMoveMemory(&ContextRecord1, ContextRecord, sizeof(CONTEXT));
+ ControlPc = (ULONG)ExceptionRecord->ExceptionAddress;
+#if DBG
+ if ((ULONG)ExceptionRecord->ExceptionAddress != (ULONG)ContextRecord->Fir) {
+ DbgPrint("RtlDispatchException: ExceptionAddress = %lx, Fir = %lx\n",
+ (ULONG)ExceptionRecord->ExceptionAddress, (ULONG)ContextRecord->Fir);
+ }
+#endif
+ ExceptionFlags = ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE;
+ NestedFrame = 0;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
+ DbgPrint("\nRtlDispatchException(ExceptionRecord = %lx, ContextRecord = %lx)\n",
+ ExceptionRecord, ContextRecord);
+ DbgPrint("RtlDispatchException: ControlPc = %lx, ExceptionRecord->ExceptionCode = %lx\n",
+ ControlPc, ExceptionRecord->ExceptionCode);
+ }
+#endif
+
+ //
+ // Start with the frame specified by the context record and search
+ // backwards through the call frame hierarchy attempting to find an
+ // exception handler that will handle the exception.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, ContextRecord1.IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then virtually
+ // unwind to the caller of the current routine to obtain the virtual
+ // frame pointer of the establisher and check if there is an exception
+ // handler for the frame.
+ //
+
+ if (FunctionEntry != NULL) {
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // If the virtual frame pointer is not within the specified stack
+ // limits or the virtual frame pointer is unaligned, then set the
+ // stack invalid flag in the exception record and return exception
+ // not handled. Otherwise, check if the current routine has an
+ // exception handler.
+ //
+
+ if ((EstablisherFrame.Virtual < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - stack invalid (exception).\n");
+ DbgPrint(" EstablisherFrame.Virtual = %08lx, EstablisherFrame.Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n",
+ NextPc, ControlPc);
+ DbgPrint(" Now setting EXCEPTION_STACK_INVALID flag.\n");
+#endif
+ ExceptionFlags |= EXCEPTION_STACK_INVALID;
+ break;
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+
+ ULONG Index;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+ }
+#endif
+
+ //
+ // The frame has an exception handler. The handler must be
+ // executed by calling another routine that is written in
+ // assembler. This is required because up level addressing
+ // of the handler information is required when a nested
+ // exception is encountered.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = ContextRecord;
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ Index = RtlpLogExceptionHandler(
+ ExceptionRecord,
+ ContextRecord,
+ ControlPc,
+ FunctionEntry,
+ sizeof(RUNTIME_FUNCTION));
+ }
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: calling RtlpExecuteHandlerForException, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForException(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ ContextRecord,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlDispatchException: RtlpExecuteHandlerForException returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ RtlpLogLastExceptionDisposition(Index, Disposition);
+ }
+
+ ExceptionFlags |=
+ (ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE);
+
+ //
+ // If the current scan is within a nested context and the frame
+ // just examined is the end of the nested region, then clear
+ // the nested context frame and the nested exception flag in
+ // the exception flags.
+ //
+
+ if (NestedFrame == EstablisherFrame.Virtual) {
+ ExceptionFlags &= (~EXCEPTION_NESTED_CALL);
+ NestedFrame = 0;
+ }
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue execution.
+ //
+ // If the exception is not continuable, then raise the
+ // exception STATUS_NONCONTINUABLE_EXCEPTION. Otherwise
+ // return exception handled.
+ //
+
+ case ExceptionContinueExecution :
+ if ((ExceptionFlags & EXCEPTION_NONCONTINUABLE) != 0) {
+ RAISE_EXCEPTION(STATUS_NONCONTINUABLE_EXCEPTION, ExceptionRecord);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
+ DbgPrint("RtlDispatchException: returning TRUE\n");
+ }
+#endif
+ return TRUE;
+ }
+
+ //
+ // The disposition is to continue the search.
+ //
+ // Get next frame address and continue the search.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is nested exception.
+ //
+ // Set the nested context frame to the establisher frame
+ // address and set the nested exception flag in the
+ // exception flags.
+ //
+
+ case ExceptionNestedException :
+ ExceptionFlags |= EXCEPTION_NESTED_CALL;
+ if (DispatcherContext.EstablisherFrame > NestedFrame) {
+ NestedFrame = DispatcherContext.EstablisherFrame;
+ }
+
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ NextPc = (ULONG)ContextRecord1.IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (NextPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (exception).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+#endif
+ break;
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = NextPc;
+
+ } while ((ULONG)ContextRecord1.IntSp < HighLimit);
+
+ //
+ // Set final exception flags and return exception not handled.
+ //
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION) {
+ DbgPrint("RtlDispatchException: returning FALSE\n");
+ }
+#endif
+ return FALSE;
+}
+
+PRUNTIME_FUNCTION
+RtlLookupFunctionEntry (
+ IN ULONG ControlPc
+ )
+
+/*++
+
+Routine Description:
+
+ This function searches the currently active function tables for an entry
+ that corresponds to the specified PC value.
+
+Arguments:
+
+ ControlPc - Supplies the address of an instruction within the specified
+ function.
+
+Return Value:
+
+ If there is no entry in the function table for the specified PC, then
+ NULL is returned. Otherwise, the address of the function table entry
+ that corresponds to the specified PC is returned.
+
+--*/
+
+{
+
+ PRUNTIME_FUNCTION FunctionEntry;
+ PRUNTIME_FUNCTION FunctionTable;
+ ULONG SizeOfExceptionTable;
+ LONG High;
+ PVOID ImageBase;
+ LONG Low;
+ LONG Middle;
+
+ //
+ // Search for the image that includes the specified PC value.
+ //
+
+ ImageBase = RtlPcToFileHeader((PVOID)ControlPc, &ImageBase);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) {
+ DbgPrint("RtlLookupFunctionEntry(ControlPc = %lx) ImageBase = %lx\n",
+ ControlPc, ImageBase);
+ }
+#endif
+
+ //
+ // If an image is found that includes the specified PC, then locate the
+ // function table for the image.
+ //
+
+ if (ImageBase != NULL) {
+ FunctionTable = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData(
+ ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
+ &SizeOfExceptionTable);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint("RtlLookupFunctionEntry: FunctionTable = %lx, SizeOfExceptionTable = %lx\n",
+ FunctionTable, SizeOfExceptionTable);
+ }
+#endif
+
+ //
+ // If a function table is located, then search the function table
+ // for a function table entry for the specified PC.
+ //
+
+ if (FunctionTable != NULL) {
+
+ //
+ // Initialize search indicies.
+ //
+
+ Low = 0;
+ High = (SizeOfExceptionTable / sizeof(RUNTIME_FUNCTION)) - 1;
+
+ //
+ // Perform binary search on the function table for a function table
+ // entry that subsumes the specified PC.
+ //
+
+ while (High >= Low) {
+
+ //
+ // Compute next probe index and test entry. If the specified PC
+ // is greater than of equal to the beginning address and less
+ // than the ending address of the function table entry, then
+ // return the address of the function table entry. Otherwise,
+ // continue the search.
+ //
+
+ Middle = (Low + High) >> 1;
+ FunctionEntry = &FunctionTable[Middle];
+ if (ControlPc < FunctionEntry->BeginAddress) {
+ High = Middle - 1;
+
+ } else if (ControlPc >= FunctionEntry->EndAddress) {
+ Low = Middle + 1;
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress);
+ DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress);
+ DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress);
+ DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler);
+ DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData);
+ }
+#endif
+ //
+ // The capability exists for more than one function entry
+ // to map to the same function. This permits a function to
+ // have (within reason) discontiguous code segment(s). If
+ // PrologEndAddress is out of range, it is re-interpreted
+ // as a pointer to the primary function table entry for
+ // that function.
+ //
+
+ if ((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
+ (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) {
+ FunctionEntry = (PRUNTIME_FUNCTION)FunctionEntry->PrologEndAddress;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint("RtlLookupFunctionEntry: ** indirect function entry **\n");
+ DbgPrint(" BeginAddress = %lx\n", FunctionEntry->BeginAddress);
+ DbgPrint(" EndAddress = %lx\n", FunctionEntry->EndAddress);
+ DbgPrint(" PrologEndAddress = %lx\n", FunctionEntry->PrologEndAddress);
+ DbgPrint(" ExceptionHandler = %lx\n", FunctionEntry->ExceptionHandler);
+ DbgPrint(" HandlerData = %lx\n", FunctionEntry->HandlerData);
+ }
+#endif
+ }
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY_DETAIL) {
+ DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = %lx\n",
+ FunctionEntry);
+ }
+#endif
+ return FunctionEntry;
+ }
+ }
+ }
+ }
+
+ //
+ // A function table entry for the specified PC was not found.
+ //
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_FUNCTION_ENTRY) {
+ DbgPrint("RtlLookupFunctionEntry: returning FunctionEntry = NULL\n");
+ }
+#endif
+ return NULL;
+}
+
+VOID
+RtlRaiseException (
+ IN PEXCEPTION_RECORD ExceptionRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises a software exception by building a context record
+ and calling the raise exception system service.
+
+ N.B. This routine is a shell routine that simply calls another routine
+ to do the real work. The reason this is done is to avoid a problem
+ in try/finally scopes where the last statement in the scope is a
+ call to raise an exception.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) {
+ DbgPrint("RtlRaiseException(ExceptionRecord = %lx) Status = %lx\n",
+ ExceptionRecord, ExceptionRecord->ExceptionCode);
+ }
+#endif
+ RtlpRaiseException(ExceptionRecord);
+ return;
+}
+
+VOID
+RtlpRaiseException (
+ IN PEXCEPTION_RECORD ExceptionRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises a software exception by building a context record
+ and calling the raise exception system service.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ CONTEXT ContextRecord;
+ FRAME_POINTERS EstablisherFrame;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG NextPc;
+ NTSTATUS Status;
+
+ //
+ // Capture the current context, virtually unwind to the caller of this
+ // routine, set the fault instruction address to that of the caller, and
+ // call the raise exception system service.
+ //
+
+ RtlCaptureContext(&ContextRecord);
+ ControlPc = (ULONG)ContextRecord.IntRa - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4;
+ ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Fir;
+ Status = ZwRaiseException(ExceptionRecord, &ContextRecord, TRUE);
+
+ //
+ // There should never be a return from this system service unless
+ // there is a problem with the argument list itself. Raise another
+ // exception specifying the status value returned.
+ //
+
+ RtlRaiseStatus(Status);
+ return;
+}
+
+VOID
+RtlRaiseStatus (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises an exception with the specified status value. The
+ exception is marked as noncontinuable with no parameters.
+
+ N.B. This routine is a shell routine that simply calls another routine
+ to do the real work. The reason this is done is to avoid a problem
+ in try/finally scopes where the last statement in the scope is a
+ call to raise an exception.
+
+Arguments:
+
+ Status - Supplies the status value to be used as the exception code
+ for the exception that is to be raised.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_RAISE_EXCEPTION) {
+ DbgPrint("RtlRaiseStatus(Status = %lx)\n", Status);
+ }
+#endif
+ RtlpRaiseStatus(Status);
+ return;
+}
+
+VOID
+RtlpRaiseStatus (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises an exception with the specified status value. The
+ exception is marked as noncontinuable with no parameters.
+
+Arguments:
+
+ Status - Supplies the status value to be used as the exception code
+ for the exception that is to be raised.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ ULONG ControlPc;
+ CONTEXT ContextRecord;
+ FRAME_POINTERS EstablisherFrame;
+ EXCEPTION_RECORD ExceptionRecord;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG NextPc;
+
+ //
+ // Construct an exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = Status;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+
+ //
+ // Capture the current context, virtually unwind to the caller of this
+ // routine, set the fault instruction address to that of the caller, and
+ // call the raise exception system service.
+ //
+
+ RtlCaptureContext(&ContextRecord);
+ ControlPc = (ULONG)ContextRecord.IntRa - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ContextRecord.Fir = (ULONGLONG)(LONG)NextPc + 4;
+ ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Fir;
+ Status = ZwRaiseException(&ExceptionRecord, &ContextRecord, TRUE);
+
+ //
+ // There should never be a return from this system service unless
+ // there is a problem with the argument list itself. Raise another
+ // exception specifying the status value returned.
+ //
+
+ RtlRaiseStatus(Status);
+ return;
+}
+
+VOID
+RtlUnwind (
+ IN PVOID TargetFrame OPTIONAL,
+ IN PVOID TargetIp OPTIONAL,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. If the TargetFrame parameter is not specified, then the exit unwind
+ flag is also set in the exception flags of the exception record. A backward
+ scan through the procedure call frames is then performed to find the target
+ of the unwind operation.
+
+ As each frame is encounter, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ N.B. This routine is provided for backward compatibility with release 1.
+
+Arguments:
+
+ TargetFrame - Supplies an optional pointer to the call frame that is the
+ target of the unwind. If this parameter is not specified, then an exit
+ unwind is performed.
+
+ TargetIp - Supplies an optional instruction address that specifies the
+ continuation address of the unwind. This address is ignored if the
+ target frame parameter is not specified.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CONTEXT ContextRecord;
+
+
+ //
+ // Call real unwind routine specifying a context record as an
+ // extra argument.
+ //
+
+ RtlUnwind2(TargetFrame,
+ TargetIp,
+ ExceptionRecord,
+ ReturnValue,
+ &ContextRecord);
+
+ return;
+}
+
+VOID
+RtlUnwind2 (
+ IN PVOID TargetFrame OPTIONAL,
+ IN PVOID TargetIp OPTIONAL,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue,
+ IN PCONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. If the TargetFrame parameter is not specified, then the exit unwind
+ flag is also set in the exception flags of the exception record. A backward
+ scan through the procedure call frames is then performed to find the target
+ of the unwind operation.
+
+ As each frame is encounter, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ N.B. This routine is provided for backward compatibility with release 1.
+
+Arguments:
+
+ TargetFrame - Supplies an optional pointer to the call frame that is the
+ target of the unwind. If this parameter is not specified, then an exit
+ unwind is performed.
+
+ TargetIp - Supplies an optional instruction address that specifies the
+ continuation address of the unwind. This address is ignored if the
+ target frame parameter is not specified.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LowLimit;
+ ULONG NextPc;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("\nRtlUnwind(TargetFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n",
+ TargetFrame, TargetIp, ReturnValue);
+ }
+#endif
+ //
+ // Get current stack limits, capture the current context, virtually
+ // unwind to the caller of this routine, get the initial PC value, and
+ // set the unwind target address.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlCaptureContext(ContextRecord);
+ ControlPc = (ULONG)ContextRecord->IntRa - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = NextPc;
+ ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp;
+
+ //
+ // If an exception record is not specified, then build a local exception
+ // record for use in calling exception handlers during the unwind operation.
+ //
+
+ if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
+ ExceptionRecord = &ExceptionRecord1;
+ ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // If the target frame of the unwind is specified, then a normal unwind
+ // is being performed. Otherwise, an exit unwind is being performed.
+ //
+
+ ExceptionFlags = EXCEPTION_UNWINDING;
+ if (ARGUMENT_PRESENT(TargetFrame) == FALSE) {
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
+ }
+
+ //
+ // Scan backward through the call frame hierarchy and call exception
+ // handlers until the target frame of the unwind is reached.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwind: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, ContextRecord->IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then virtually
+ // unwind to the caller of the routine to obtain the virtual frame
+ // pointer of the establisher, but don't update the context record.
+ //
+
+ if (FunctionEntry != NULL) {
+ NextPc = RtlpVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ (PULONG)&EstablisherFrame);
+
+ //
+ // If the virtual frame pointer is not within the specified stack
+ // limits, the virtual frame pointer is unaligned, or the target
+ // frame is below the virtual frame and an exit unwind is not being
+ // performed, then raise the exception STATUS_BAD_STACK. Otherwise,
+ // check to determine if the current routine has an exception
+ // handler.
+ //
+
+ if ((EstablisherFrame.Virtual < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual)) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
+ DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" TargetFrame = %08lx\n", TargetFrame);
+ if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual)) {
+ DbgPrint(" TargetFrame is below EstablisherFrame!\n");
+ }
+ DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
+ (ULONG)ContextRecord->IntSp);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" NextPc = %08lx, ControlPc = %08lx\n",
+ NextPc, ControlPc);
+ DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
+#endif
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlUnwind: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+}
+#endif
+
+ //
+ // The frame has an exception handler.
+ //
+ // The control PC, establisher frame pointer, the address
+ // of the function table entry, and the address of the
+ // context record are all stored in the dispatcher context.
+ // This information is used by the unwind linkage routine
+ // and can be used by the exception handler itself.
+ //
+ // A linkage routine written in assembler is used to actually
+ // call the actual exception handler. This is required by the
+ // exception handler that is associated with the linkage
+ // routine so it can have access to two sets of dispatcher
+ // context when it is called.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = ContextRecord;
+
+ //
+ // Call the exception handler.
+ //
+
+ do {
+
+ //
+ // If the establisher frame is the target of the unwind
+ // operation, then set the target unwind flag.
+ //
+
+ if ((ULONG)TargetFrame == EstablisherFrame.Virtual) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwind: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ ContextRecord,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwind: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ //
+ // Clear target unwind and collided unwind flags.
+ //
+
+ ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
+ EXCEPTION_TARGET_UNWIND);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search.
+ //
+ // If the target frame has not been reached, then
+ // virtually unwind to the caller of the current
+ // routine, update the context record, and continue
+ // the search for a handler.
+ //
+
+ case ExceptionContinueSearch :
+ if (EstablisherFrame.Virtual != (ULONG)TargetFrame) {
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ }
+
+ break;
+
+ //
+ // The disposition is collided unwind.
+ //
+ // Set the target of the current unwind to the context
+ // record of the previous unwind, and reexecute the
+ // exception handler from the collided frame with the
+ // collided unwind flag set in the exception record.
+ //
+
+ case ExceptionCollidedUnwind :
+ ControlPc = DispatcherContext.ControlPc;
+ FunctionEntry = DispatcherContext.FunctionEntry;
+ ContextRecord = DispatcherContext.ContextRecord;
+ ContextRecord->Fir = (ULONGLONG)(LONG)TargetIp;
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ EstablisherFrame.Virtual = DispatcherContext.EstablisherFrame;
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+
+ } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
+ } else {
+
+ //
+ // Virtually unwind to the caller of the current routine and
+ // update the context record.
+ //
+
+ if (EstablisherFrame.Virtual != (ULONG)TargetFrame) {
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ }
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (NextPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (unwind).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", NextPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+ DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
+#endif
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = NextPc;
+
+ } while ((EstablisherFrame.Virtual < HighLimit) &&
+ (EstablisherFrame.Virtual != (ULONG)TargetFrame));
+
+ //
+ // If the establisher stack pointer is equal to the target frame
+ // pointer, then continue execution. Otherwise, an exit unwind was
+ // performed or the target of the unwind did not exist and the
+ // debugger and subsystem are given a second chance to handle the
+ // unwind.
+ //
+
+ if (EstablisherFrame.Virtual == (ULONG)TargetFrame) {
+ ContextRecord->IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwind: finished unwinding, and calling RtlpRestoreContext(%lx)\n",ContextRecord);
+ }
+#endif
+ RtlpRestoreContext(ContextRecord);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwind: finished unwinding, but calling ZwRaiseException\n");
+ }
+#endif
+ ZwRaiseException(ExceptionRecord, ContextRecord, FALSE);
+ }
+
+}
+
+#if DBG
+//
+// Define an array of symbolic names for the integer registers.
+//
+
+PCHAR RtlpIntegerRegisterNames[32] = {
+ "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", // 0 - 7
+ "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp", // 8 - 15
+ "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", // 16 - 23
+ "t10", "t11", "ra", "t12", "at", "gp", "sp", "zero", // 24 - 31
+};
+
+//
+// This function disassembles the instruction at the given address. It is
+// only used for debugging and recognizes only those few instructions that
+// are relevant during reverse execution of the prologue by virtual unwind.
+//
+
+VOID
+_RtlpDebugDisassemble (
+ IN ULONG ControlPc,
+ IN PCONTEXT ContextRecord
+ )
+{
+ UCHAR Comments[50];
+ PULONGLONG FloatingRegister;
+ ULONG Function;
+ ULONG Hint;
+ ULONG Literal8;
+ ALPHA_INSTRUCTION Instruction;
+ PULONGLONG IntegerRegister;
+ LONG Offset16;
+ UCHAR Operands[20];
+ ULONG Opcode;
+ PCHAR OpName;
+ ULONG Ra;
+ ULONG Rb;
+ ULONG Rc;
+ PCHAR RaName;
+ PCHAR RbName;
+ PCHAR RcName;
+
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND_DETAIL) {
+ Instruction.Long = *((PULONG)ControlPc);
+ Hint = Instruction.Jump.Hint;
+ Literal8 = Instruction.OpLit.Literal;
+ Offset16 = Instruction.Memory.MemDisp;
+ Opcode = Instruction.Memory.Opcode;
+ Ra = Instruction.OpReg.Ra;
+ RaName = RtlpIntegerRegisterNames[Ra];
+ Rb = Instruction.OpReg.Rb;
+ RbName = RtlpIntegerRegisterNames[Rb];
+ Rc = Instruction.OpReg.Rc;
+ RcName = RtlpIntegerRegisterNames[Rc];
+
+ IntegerRegister = &ContextRecord->IntV0;
+ FloatingRegister = &ContextRecord->FltF0;
+
+ OpName = NULL;
+ switch (Opcode) {
+ case JMP_OP :
+ if (Instruction.Jump.Function == RET_FUNC) {
+ OpName = "ret";
+ sprintf(Operands, "%s, (%s), %04lx", RaName, RbName, Hint);
+ sprintf(Comments, "%s = %Lx", RbName, IntegerRegister[Rb]);
+ }
+ break;
+
+ case LDAH_OP :
+ case LDA_OP :
+ case STQ_OP :
+ if (Opcode == LDA_OP) {
+ OpName = "lda";
+
+ } else if (Opcode == LDAH_OP) {
+ OpName = "ldah";
+
+ } else if (Opcode == STQ_OP) {
+ OpName = "stq";
+ }
+ sprintf(Operands, "%s, $%d(%s)", RaName, Offset16, RbName);
+ sprintf(Comments, "%s = %Lx", RaName, IntegerRegister[Ra]);
+ break;
+
+ case ARITH_OP :
+ case BIT_OP :
+ Function = Instruction.OpReg.Function;
+ if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC)) {
+ OpName = "addq";
+
+ } else if ((Opcode == ARITH_OP) && (Function == SUBQ_FUNC)) {
+ OpName = "subq";
+
+ } else if ((Opcode == BIT_OP) && (Function == BIS_FUNC)) {
+ OpName = "bis";
+
+ } else {
+ break;
+ }
+ if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
+ sprintf(Operands, "%s, %s, %s", RaName, RbName, RcName);
+
+ } else {
+ sprintf(Operands, "%s, $%d, %s", RaName, Literal8, RcName);
+ }
+ sprintf(Comments, "%s = %Lx", RcName, IntegerRegister[Rc]);
+ break;
+
+ case FPOP_OP :
+ if (Instruction.FpOp.Function == CPYS_FUNC) {
+ OpName = "cpys";
+ sprintf(Operands, "f%d, f%d, f%d", Ra, Rb, Rc);
+ sprintf(Comments, "f%d = %Lx", Rc, FloatingRegister[Rc]);
+ }
+ break;
+
+ case STT_OP :
+ OpName = "stt";
+ sprintf(Operands, "f%d, $%d(%s)", Ra, Offset16, RbName);
+ sprintf(Comments, "f%d = %Lx", Ra, FloatingRegister[Ra]);
+ break;
+ }
+ if (OpName == NULL) {
+ OpName = "???";
+ sprintf(Operands, "...");
+ sprintf(Comments, "Unknown to virtual unwind.");
+ }
+ DbgPrint(" %08lx: %08lx %-5s %-16s // %s\n",
+ ControlPc, Instruction.Long, OpName, Operands, Comments);
+ }
+ return;
+}
+
+#define _RtlpFoundTrapFrame(NextPc) \
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
+ DbgPrint(" *** Looks like a trap frame (fake prologue), Fir = %lx\n", \
+ NextPc); \
+ }
+
+#define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame) \
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) { \
+ DbgPrint("RtlVirtualUnwind: EstablisherFrame Virtual = %08lx, Real = %08lx\n", \
+ (EstablisherFrame)->Virtual, (EstablisherFrame)->Real); \
+ DbgPrint("RtlVirtualUnwind: returning NextPc = %lx, sp = %lx\n\n", \
+ NextPc, ContextRecord->IntSp); \
+ }
+
+#else
+
+#define _RtlpDebugDisassemble(ControlPc, ContextRecord)
+#define _RtlpFoundTrapFrame(NextPc)
+#define _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame)
+
+#endif
+
+ULONG
+RtlVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PFRAME_POINTERS EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specified function by executing its
+ prologue code backwards. Given the current context and the instructions
+ that preserve registers in the prologue, it is possible to recreate the
+ nonvolatile context at the point the function was called.
+
+ If the function is a leaf function, then the address where control left
+ the previous frame is obtained from the context record. If the function
+ is a nested function, but not an exception or interrupt frame, then the
+ prologue code is executed backwards and the address where control left
+ the previous frame is obtained from the updated context record.
+
+ Otherwise, an exception or interrupt entry to the system is being unwound
+ and a specially coded prologue restores the return address twice. Once
+ from the fault instruction address and once from the saved return address
+ register. The first restore is returned as the function value and the
+ second restore is placed in the updated context record.
+
+ During the unwind, the virtual and real frame pointers for the function
+ are calculated and returned in the given frame pointers structure.
+
+ If a context pointers record is specified, then the address where each
+ register is restored from is recorded in the appropriate element of the
+ context pointers record.
+
+Arguments:
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function.
+
+ ContextRecord - Supplies the address of a context record.
+
+ InFunction - Supplies a pointer to a variable that receives whether the
+ control PC is within the current function.
+
+ EstablisherFrame - Supplies a pointer to a frame pointers structure
+ that will receive the values for the virtual frame pointer and the
+ real frame pointer. The value of the real frame pointer is reliable
+ only when InFunction is TRUE.
+
+ ContextPointers - Supplies an optional pointer to a context pointers
+ record.
+
+Return Value:
+
+ The address where control left the previous frame is returned as the
+ function value.
+
+Implementation Notes:
+
+ N.B. "where control left" is not the "return address" of the call in the
+ previous frame. For normal frames, NextPc points to the last instruction
+ that completed in the previous frame (the JSR/BSR). The difference between
+ NextPc and NextPc + 4 (return address) is important for correct behavior
+ in boundary cases of exception addresses and scope tables.
+
+ For exception and interrupt frames, NextPc is obtained from the trap frame
+ contination address (Fir). For faults and synchronous traps, NextPc is both
+ the last instruction to execute in the previous frame and the next
+ instruction to execute if the function were to return. For asynchronous
+ traps, NextPc is the continuation address. It is the responsibility of the
+ compiler to insert TRAPB instructions to insure asynchronous traps do not
+ occur outside the scope from the instruction(s) that caused them.
+
+ N.B. in this and other files where RtlVirtualUnwind is used, the variable
+ named NextPc is perhaps more accurately, LastPc - the last PC value in
+ the previous frame, or CallPc - the address of the call instruction, or
+ ControlPc - the address where control left the previous frame. Instead
+ think of NextPc as the next PC to use in another call to virtual unwind.
+
+ The Alpha version of virtual unwind is similar in design, but slightly
+ more complex than the Mips version. This is because Alpha compilers
+ are given more flexibility to optimize generated code and instruction
+ sequences, including within procedure prologues. In addition, because of
+ the current inability of the GEM compiler to materialize virtual frame
+ pointers, this function must manage both virtual and real frame pointers.
+
+--*/
+
+{
+
+ ULONG Address;
+ ULONG DecrementOffset;
+ ULONG DecrementRegister;
+ ALPHA_INSTRUCTION FollowingInstruction;
+ PULONGLONG FloatingRegister;
+ ULONG FrameSize;
+ ULONG Function;
+ ALPHA_INSTRUCTION Instruction;
+ PULONGLONG IntegerRegister;
+ ULONG Literal8;
+ ULONG NextPc;
+ LONG Offset16;
+ ULONG Opcode;
+ ULONG Ra;
+ ULONG Rb;
+ ULONG Rc;
+ BOOLEAN RestoredRa;
+ BOOLEAN RestoredSp;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_VIRTUAL_UNWIND) {
+ DbgPrint("\nRtlVirtualUnwind(ControlPc = %lx, FunctionEntry = %lx,) sp = %lx\n",
+ ControlPc, FunctionEntry, ContextRecord->IntSp);
+ }
+#endif
+#if DBG
+ if ((FunctionEntry == NULL) || (ControlPc & 0x3) ||
+ (FunctionEntry->BeginAddress >= FunctionEntry->EndAddress) ||
+#if 0
+ (ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= FunctionEntry->EndAddress) ||
+#endif
+ (FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
+ (FunctionEntry->PrologEndAddress >= FunctionEntry->EndAddress)) {
+ DbgPrint("\n****** Warning - invalid PC or function table entry (virtual unwind).\n");
+ return ControlPc;
+ }
+#endif
+ //
+ // Set the base address of the integer and floating register arrays within
+ // the context record. Each set of 32 registers is known to be contiguous.
+ //
+
+ IntegerRegister = &ContextRecord->IntV0;
+ FloatingRegister = &ContextRecord->FltF0;
+
+ //
+ // Handle the epilogue case where the next instruction is a return.
+ //
+ // Exception handlers cannot be called if the ControlPc is within the
+ // epilogue because exception handlers expect to operate with a current
+ // stack frame. The value of SP is not current within the epilogue.
+ //
+
+ Instruction.Long = *((PULONG)ControlPc);
+ if (IS_RETURN_0001_INSTRUCTION(Instruction.Long)) {
+ Rb = Instruction.Jump.Rb;
+ NextPc = (ULONG)IntegerRegister[Rb] - 4;
+
+ //
+ // The instruction at the point where control left the specified
+ // function is a return, so any saved registers have already been
+ // restored, and the stack pointer has already been adjusted. The
+ // stack does not need to be unwound in this case and the saved
+ // return address register is returned as the function value.
+ //
+ // In fact, reverse execution of the prologue is not possible in
+ // this case: the stack pointer has already been incremented and
+ // so, for this frame, neither a valid stack pointer nor frame
+ // pointer exists from which to begin reverse execution of the
+ // prologue. In addition, the integrity of any data on the stack
+ // below the stack pointer is never guaranteed (due to interrupts
+ // and exceptions).
+ //
+ // The epilogue instruction sequence is:
+ //
+ // ==> ret zero, (Ra), 1 // return
+ // or
+ //
+ // mov ra, Rx // save return address
+ // ...
+ // ==> ret zero, (Rx), 1 // return
+ //
+
+ EstablisherFrame->Real = 0;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ *InFunction = FALSE;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+ }
+
+ //
+ // Handle the epilogue case where the next two instructions are a stack
+ // frame deallocation and a return.
+ //
+
+ FollowingInstruction.Long = *((PULONG)(ControlPc + 4));
+ if (IS_RETURN_0001_INSTRUCTION(FollowingInstruction.Long)) {
+ Rb = FollowingInstruction.Jump.Rb;
+ NextPc = (ULONG)IntegerRegister[Rb] - 4;
+
+ //
+ // The second instruction following the point where control
+ // left the specified function is a return. If the instruction
+ // before the return is a stack increment instruction, then all
+ // saved registers have already been restored except for SP.
+ // The value of the stack pointer register cannot be recovered
+ // through reverse execution of the prologue because in order
+ // to begin reverse execution either the stack pointer or the
+ // frame pointer (if any) must still be valid.
+ //
+ // Instead, the effect that the stack increment instruction
+ // would have had on the context is manually applied to the
+ // current context. This is forward execution of the epilogue
+ // rather than reverse execution of the prologue.
+ //
+ // In an epilogue, as in a prologue, the stack pointer is always
+ // adjusted with a single instruction: either an immediate-value
+ // (lda) or a register-value (addq) add instruction.
+ //
+
+ Function = Instruction.OpReg.Function;
+ Offset16 = Instruction.Memory.MemDisp;
+ Opcode = Instruction.OpReg.Opcode;
+ Ra = Instruction.OpReg.Ra;
+ Rb = Instruction.OpReg.Rb;
+ Rc = Instruction.OpReg.Rc;
+
+ if ((Opcode == LDA_OP) && (Ra == SP_REG)) {
+
+ //
+ // Load Address instruction.
+ //
+ // Since the destination (Ra) register is SP, an immediate-
+ // value stack deallocation operation is being performed. The
+ // displacement value should be added to SP. The displacement
+ // value is assumed to be positive. The amount of stack
+ // deallocation possible using this instruction ranges from
+ // 16 to 32752 (32768 - 16) bytes. The base register (Rb) is
+ // usually SP, but may be another register.
+ //
+ // The epilogue instruction sequence is:
+ //
+ // ==> lda sp, +N(sp) // deallocate stack frame
+ // ret zero, (ra) // return
+ // or
+ //
+ // ==> lda sp, +N(Rx) // restore SP and deallocate frame
+ // ret zero, (ra) // return
+ //
+
+ ContextRecord->IntSp = Offset16 + IntegerRegister[Rb];
+ EstablisherFrame->Real = 0;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ *InFunction = FALSE;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ _RtlpDebugDisassemble(ControlPc + 4, ContextRecord);
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+
+ } else if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC) &&
+ (Rc == SP_REG) &&
+ (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
+
+ //
+ // Add Quadword instruction.
+ //
+ // Since both source operands are registers, and the
+ // destination register is SP, a register-value stack
+ // deallocation is being performed. The value of the two
+ // source registers should be added and this is the new
+ // value of SP. One of the source registers is usually SP,
+ // but may be another register.
+ //
+ // The epilogue instruction sequence is:
+ //
+ // ldiq Rx, N // set [large] frame size
+ // ...
+ // ==> addq sp, Rx, sp // deallocate stack frame
+ // ret zero, (ra) // return
+ // or
+ //
+ // ==> addq Rx, Ry, sp // restore SP and deallocate frame
+ // ret zero, (ra) // return
+ //
+
+ ContextRecord->IntSp = IntegerRegister[Ra] + IntegerRegister[Rb];
+ EstablisherFrame->Real = 0;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ *InFunction = FALSE;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ _RtlpDebugDisassemble(ControlPc + 4, ContextRecord);
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+ }
+ }
+
+ //
+ // By default set the frame pointers to the current value of SP.
+ //
+ // When a procedure is called, the value of SP before the stack
+ // allocation instruction is the virtual frame pointer. When reverse
+ // executing instructions in the prologue, the value of SP before the
+ // stack allocation instruction is encountered is the real frame
+ // pointer. This is the current value of SP unless the procedure uses
+ // a frame pointer (e.g., FP_REG).
+ //
+
+ EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+
+ //
+ // If the address where control left the specified function is beyond
+ // the end of the prologue, then the control PC is considered to be
+ // within the function and the control address is set to the end of
+ // the prologue. Otherwise, the control PC is not considered to be
+ // within the function (i.e., the prologue).
+ //
+ // N.B. PrologEndAddress is equal to BeginAddress for a leaf function.
+ //
+ // The low-order two bits of PrologEndAddress are reserved for the IEEE
+ // exception mode and so must be masked out.
+ //
+
+ if ((ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= FunctionEntry->PrologEndAddress)) {
+ *InFunction = TRUE;
+ ControlPc = (FunctionEntry->PrologEndAddress & (~0x3));
+
+ } else {
+ *InFunction = FALSE;
+ }
+
+ //
+ // Scan backward through the prologue to reload callee saved registers
+ // that were stored or copied and to increment the stack pointer if it
+ // was decremented.
+ //
+
+ DecrementRegister = ZERO_REG;
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+ RestoredRa = FALSE;
+ RestoredSp = FALSE;
+ while (ControlPc > FunctionEntry->BeginAddress) {
+
+ //
+ // Get instruction value, decode fields, case on opcode value, and
+ // reverse register store and stack decrement operations.
+ // N.B. The location of Opcode, Ra, Rb, and Rc is the same across
+ // all opcode formats. The same is not true for Function.
+ //
+
+ ControlPc -= 4;
+ Instruction.Long = *((PULONG)ControlPc);
+ Function = Instruction.OpReg.Function;
+ Literal8 = Instruction.OpLit.Literal;
+ Offset16 = Instruction.Memory.MemDisp;
+ Opcode = Instruction.OpReg.Opcode;
+ Ra = Instruction.OpReg.Ra;
+ Rb = Instruction.OpReg.Rb;
+ Rc = Instruction.OpReg.Rc;
+
+ //
+ // Compare against each instruction type that will affect the context
+ // and that is allowed in a prologue. Any other instructions found
+ // in the prologue will be ignored since they are assumed to have no
+ // effect on the context.
+ //
+
+ switch (Opcode) {
+
+ case STQ_OP :
+
+ //
+ // Store Quad instruction.
+ //
+ // If the base register is SP, then reload the source register
+ // value from the value stored on the stack.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> stq Rx, N(sp) // save integer register Rx
+ //
+
+ if ((Rb == SP_REG) && (Ra != ZERO_REG)) {
+
+ //
+ // Reload the register by retrieving the value previously
+ // stored on the stack.
+ //
+
+ Address = Offset16 + ContextRecord->IntSp;
+ IntegerRegister[Ra] = *((PULONGLONG)Address);
+
+ //
+ // If the destination register is RA and this is the first
+ // time that RA is being restored, then set the address of
+ // where control left the previous frame. Otherwise, if this
+ // is the second time RA is being restored, then the first
+ // one was an interrupt or exception address and the return
+ // PC should not have been biased by 4.
+ //
+
+ if (Ra == RA_REG) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ _RtlpFoundTrapFrame(NextPc);
+ }
+
+ //
+ // Otherwise, if the destination register is SP and this is
+ // the first time that SP is being restored, then set the
+ // establisher frame pointers.
+ //
+
+ } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination register contents
+ // are stored.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->IntegerContext[Ra] = (PULONGLONG)Address;
+ }
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ break;
+
+ case LDAH_OP :
+ Offset16 <<= 16;
+
+ case LDA_OP :
+
+ //
+ // Load Address High, Load Address instruction.
+ //
+ // There are several cases where the lda and/or ldah instructions
+ // are used: one to decrement the stack pointer directly, and the
+ // others to load immediate values into another register and that
+ // register is then used to decrement the stack pointer.
+ //
+ // In the examples below, as a single instructions or as a pair,
+ // a lda may be substituted for a ldah and visa-versa.
+ //
+
+ if (Ra == SP_REG) {
+ if (Rb == SP_REG) {
+
+ //
+ // If both the destination (Ra) and base (Rb) registers
+ // are SP, then a standard stack allocation was performed
+ // and the negated displacement value is the stack frame
+ // size. The amount of stack allocation possible using
+ // the lda instruction ranges from 16 to 32768 bytes and
+ // the amount of stack allocation possible using the ldah
+ // instruction ranges from 65536 to 2GB in multiples of
+ // 65536 bytes. It is rare for the ldah instruction to be
+ // used in this manner.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> lda sp, -N(sp) // allocate stack frame
+ //
+
+ FrameSize = -Offset16;
+ goto StackAllocation;
+
+ } else {
+
+ //
+ // The destination register is SP and the base register
+ // is not SP, so this instruction must be the second
+ // half of an instruction pair to allocate a large size
+ // (>32768 bytes) stack frame. Save the displacement value
+ // as the partial decrement value and postpone adjusting
+ // the value of SP until the first instruction of the pair
+ // is encountered.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ldah Rx, -N(sp) // prepare new SP (upper)
+ // ==> lda sp, sN(Rx) // allocate stack frame
+ //
+
+ DecrementRegister = Rb;
+ DecrementOffset = Offset16;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+
+ } else if (Ra == DecrementRegister) {
+ if (Rb == DecrementRegister) {
+
+ //
+ // Both the destination and base registers are the
+ // decrement register, so this instruction exists as the
+ // second half of a two instruction pair to load a
+ // 31-bit immediate value into the decrement register.
+ // Save the displacement value as the partial decrement
+ // value.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ldah Rx, +N(zero) // set frame size (upper)
+ // ==> lda Rx, sN(Rx) // set frame size (+lower)
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ DecrementOffset += Offset16;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+
+ } else if (Rb == ZERO_REG) {
+
+ //
+ // The destination register is the decrement register and
+ // the base register is zero, so this instruction exists
+ // to load an immediate value into the decrement register.
+ // The stack frame size is the new displacement value added
+ // to the previous displacement value, if any.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> lda Rx, +N(zero) // set frame size
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ // or
+ //
+ // ==> ldah Rx, +N(zero) // set frame size (upper)
+ // lda Rx, sN(Rx) // set frame size (+lower)
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ FrameSize = (Offset16 + DecrementOffset);
+ goto StackAllocation;
+
+ } else if (Rb == SP_REG) {
+
+ //
+ // The destination (Ra) register is SP and the base (Rb)
+ // register is the decrement register, so a two
+ // instruction, large size (>32768 bytes) stack frame
+ // allocation was performed. Add the new displacement
+ // value to the previous displacement value. The negated
+ // displacement value is the stack frame size.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> ldah Rx, -N(sp) // prepare new SP (upper)
+ // lda sp, sN(Rx) // allocate stack frame
+ //
+
+ FrameSize = -(Offset16 + (LONG)DecrementOffset);
+ goto StackAllocation;
+ }
+ }
+ break;
+
+ case ARITH_OP :
+
+ if ((Function == ADDQ_FUNC) &&
+ (Instruction.OpReg.RbvType != RBV_REGISTER_FORMAT)) {
+
+ //
+ // Add Quadword (immediate) instruction.
+ //
+ // If the first source register is zero, and the second
+ // operand is a literal, and the destination register is
+ // the decrement register, then the instruction exists
+ // to load an unsigned immediate value less than 256 into
+ // the decrement register. The immediate value is the stack
+ // frame size.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> addq zero, N, Rx // set frame size
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
+ FrameSize = Literal8;
+ goto StackAllocation;
+ }
+
+ } else if ((Function == SUBQ_FUNC) &&
+ (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
+
+ //
+ // Subtract Quadword (register) instruction.
+ //
+ // If both source operands are registers and the first
+ // source (minuend) register and the destination
+ // (difference) register are both SP, then a register value
+ // stack allocation was performed and the second source
+ // (subtrahend) register value will be added to SP when its
+ // value is known. Until that time save the register number of
+ // this decrement register.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ldiq Rx, N // set frame size
+ // ...
+ // ==> subq sp, Rx, sp // allocate stack frame
+ //
+
+ if ((Ra == SP_REG) && (Rc == SP_REG)) {
+ DecrementRegister = Rb;
+ DecrementOffset = 0;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ }
+ break;
+
+ case BIT_OP :
+
+ //
+ // If the second operand is a register the bit set instruction
+ // may be a register move instruction, otherwise if the second
+ // operand is a literal, the bit set instruction may be a load
+ // immediate value instruction.
+ //
+
+ if ((Function == BIS_FUNC) && (Rc != ZERO_REG)) {
+ if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
+
+ //
+ // Bit Set (register move) instruction.
+ //
+ // If both source registers are the same register, or
+ // one of the source registers is zero, then this is a
+ // register move operation. Restore the value of the
+ // source register by copying the current destination
+ // register value back to the source register.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> bis Rx, Rx, Ry // copy register Rx
+ // or
+ //
+ // ==> bis Rx, zero, Ry // copy register Rx
+ // or
+ //
+ // ==> bis zero, Rx, Ry // copy register Rx
+ //
+
+ if (Ra == ZERO_REG) {
+
+ //
+ // Map the third case above to the first case.
+ //
+
+ Ra = Rb;
+
+ } else if (Rb == ZERO_REG) {
+
+ //
+ // Map the second case above to the first case.
+ //
+
+ Rb = Ra;
+ }
+
+ if ((Ra == Rb) && (Ra != ZERO_REG)) {
+ IntegerRegister[Ra] = IntegerRegister[Rc];
+
+ //
+ // If the destination register is RA and this is the
+ // first time that RA is being restored, then set the
+ // address of where control left the previous frame.
+ // Otherwise, if this is the second time RA is being
+ // restored, then the first one was an interrupt or
+ // exception address and the return PC should not
+ // have been biased by 4.
+ //
+
+ if (Ra == RA_REG) {
+ if (RestoredRa == FALSE) {
+ NextPc = (ULONG)ContextRecord->IntRa - 4;
+ RestoredRa = TRUE;
+
+ } else {
+ NextPc += 4;
+ _RtlpFoundTrapFrame(NextPc);
+ }
+
+ //
+ // If the source register is SP and this is the first
+ // time SP is set, then this is a frame pointer set
+ // instruction. Reset the frame pointers to this new
+ // value of SP.
+ //
+
+ } else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ EstablisherFrame->Real = (ULONG)ContextRecord->IntSp;
+ RestoredSp = TRUE;
+ }
+
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+
+ } else {
+
+ //
+ // Bit Set (load immediate) instruction.
+ //
+ // If the first source register is zero, and the second
+ // operand is a literal, and the destination register is
+ // the decrement register, then this instruction exists
+ // to load an unsigned immediate value less than 256 into
+ // the decrement register. The decrement register value is
+ // the stack frame size.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> bis zero, N, Rx // set frame size
+ // ...
+ // subq sp, Rx, sp // allocate stack frame
+ //
+
+ if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
+ FrameSize = Literal8;
+StackAllocation:
+ //
+ // Add the frame size to SP to reverse the stack frame
+ // allocation, leave the real frame pointer as is, set
+ // the virtual frame pointer with the updated SP value,
+ // and clear the decrement register.
+ //
+
+ ContextRecord->IntSp += FrameSize;
+ EstablisherFrame->Virtual = (ULONG)ContextRecord->IntSp;
+ DecrementRegister = ZERO_REG;
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ }
+ }
+ break;
+
+ case STT_OP :
+
+ //
+ // Store T-Floating (quadword integer) instruction.
+ //
+ // If the base register is SP, then reload the source register
+ // value from the value stored on the stack.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> stt Fx, N(sp) // save floating register Fx
+ //
+
+ if ((Rb == SP_REG) && (Ra != FZERO_REG)) {
+
+ //
+ // Reload the register by retrieving the value previously
+ // stored on the stack.
+ //
+
+ Address = Offset16 + ContextRecord->IntSp;
+ FloatingRegister[Ra] = *((PULONGLONG)Address);
+
+ //
+ // If a context pointer record is specified, then record
+ // the address where the destination register contents are
+ // stored.
+ //
+
+ if (ARGUMENT_PRESENT(ContextPointers)) {
+ ContextPointers->FloatingContext[Ra] = (PULONGLONG)Address;
+ }
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ break;
+
+ case FPOP_OP :
+
+ //
+ // N.B. The floating operate function field is not the same as
+ // the integer operate nor the jump function fields.
+ //
+
+ if (Instruction.FpOp.Function == CPYS_FUNC) {
+
+ //
+ // Copy Sign (floating-point move) instruction.
+ //
+ // If both source registers are the same register, then this is
+ // a floating-point register move operation. Restore the value
+ // of the source register by copying the current destination
+ // register value to the source register.
+ //
+ // The prologue instruction sequence is:
+ //
+ // ==> cpys Fx, Fx, Fy // copy floating register Fx
+ //
+
+ if ((Ra == Rb) && (Ra != FZERO_REG)) {
+ FloatingRegister[Ra] = FloatingRegister[Rc];
+ _RtlpDebugDisassemble(ControlPc, ContextRecord);
+ }
+ }
+
+ default :
+ break;
+ }
+ }
+
+ _RtlpVirtualUnwindExit(NextPc, ContextRecord, EstablisherFrame);
+ return NextPc;
+}
+
+ULONG
+RtlpVirtualUnwind (
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specfified function by executing its
+ prologue code backwards.
+
+ If the function is a leaf function, then the address where control left
+ the previous frame is obtained from the context record. If the function
+ is a nested function, but not an exception or interrupt frame, then the
+ prologue code is executed backwards and the address where control left
+ the previous frame is obtained from the updated context record.
+
+ Otherwise, an exception or interrupt entry to the system is being unwound
+ and a specially coded prologue restores the return address twice. Once
+ from the fault instruction address and once from the saved return address
+ register. The first restore is returned as the function value and the
+ second restore is place in the updated context record.
+
+ If a context pointers record is specified, then the address where each
+ nonvolatile registers is restored from is recorded in the appropriate
+ element of the context pointers record.
+
+ N.B. This function copies the specified context record and only computes
+ the establisher frame and whether control is actually in a function.
+
+Arguments:
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function.
+
+ ContextRecord - Supplies the address of a context record.
+
+ InFunction - Supplies a pointer to a variable that receives whether the
+ control PC is within the current function.
+
+ EstablisherFrame - Supplies a pointer to a variable that receives the
+ the establisher frame pointer value.
+
+ ContextPointers - Supplies an optional pointer to a context pointers
+ record.
+
+Return Value:
+
+ The address where control left the previous frame is returned as the
+ function value.
+
+--*/
+
+{
+
+ CONTEXT LocalContext;
+
+ //
+ // Copy the context record so updates will not be reflected in the
+ // original copy and then virtually unwind to the caller of the
+ // specified control point.
+ //
+
+ RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT));
+ return RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &LocalContext,
+ InFunction,
+ (PFRAME_POINTERS)EstablisherFrame,
+ NULL);
+}
diff --git a/private/ntos/rtl/alpha/getcalr.c b/private/ntos/rtl/alpha/getcalr.c
new file mode 100644
index 000000000..01bbe89cb
--- /dev/null
+++ b/private/ntos/rtl/alpha/getcalr.c
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ getcalr.c
+
+Abstract:
+
+ This module implements the routine RtlGetCallerAddress. It will
+ return the address of the caller, and the callers caller to the
+ specified procedure.
+
+Author:
+
+ Larry Osterman (larryo) 18-Mar-1991 (with help from DaveC)
+
+Revision History:
+
+ 18-Mar-1991 larryo
+
+ Created
+
+ Thomas Van Baak (tvb) 5-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+#include "ntrtlp.h"
+
+//
+// Undefine get callers address since it is defined as a macro.
+//
+
+#undef RtlGetCallersAddress
+
+VOID
+RtlGetCallersAddress (
+ OUT PVOID *CallersPc,
+ OUT PVOID *CallersCallersPc
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the address of the call to the routine that called
+ this routine, and the address of the call to the routine that called
+ the routine that called this routine. For example, if A called B called
+ C which called this routine, the return addresses in B and A would be
+ returned.
+
+Arguments:
+
+ CallersPc - Supplies a pointer to a variable that receives the address
+ of the caller of the caller of this routine (B).
+
+ CallersCallersPc - Supplies a pointer to a variable that receives the
+ address of the caller of the caller of the caller of this routine
+ (A).
+
+Return Value:
+
+ None.
+
+Note:
+
+ If either of the calling stack frames exceeds the limits of the stack,
+ they are set to NULL.
+
+--*/
+
+{
+#ifdef REALLY_GET_CALLERS_CALLER
+ CONTEXT ContextRecord;
+ FRAME_POINTERS EstablisherFrame;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ ULONG HighLimit, LowLimit;
+ ULONG NextPc;
+
+ //
+ // Assume the function table entries for the various routines cannot be
+ // found or there are not four procedure activation records on the stack.
+ //
+
+ *CallersPc = NULL;
+ *CallersCallersPc = NULL;
+
+ //
+ // Capture the current context.
+ //
+
+ RtlCaptureContext(&ContextRecord);
+ NextPc = (ULONG)ContextRecord.IntRa;
+
+ //
+ // Get the high and low limits of the current thread's stack.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+
+ //
+ // Attempt to unwind to the caller of this routine (C). The FunctionEntry
+ // returned here will be that of this routine since NextPc was the return
+ // address of our call to RtlCaptureContext. For the purposes of this
+ // function, the +4 and -4 adjustments to NextPc are unnecessary.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if (FunctionEntry != NULL) {
+
+ //
+ // A function entry was found for this routine. Virtually unwind
+ // to the caller of this routine (C).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // Attempt to unwind to the caller of the caller of this routine (B).
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.IntSp < HighLimit)) {
+
+ //
+ // A function table entry was found for the caller of the caller
+ // of this routine (B). Virtually unwind to the caller of the
+ // caller of this routine (B).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ *CallersPc = (PVOID)NextPc;
+
+ //
+ // Attempt to unwind to the caller of the caller of the caller
+ // of the caller of this routine (A).
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if ((FunctionEntry != NULL) && ((ULONG)ContextRecord.IntSp < HighLimit)) {
+
+ //
+ // A function table entry was found for the caller of the
+ // caller of the caller of this routine (A). Virtually unwind
+ // to the caller of the caller of the caller of this routine
+ // (A).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ *CallersCallersPc = (PVOID)NextPc;
+ }
+ }
+ }
+#else
+ *CallersPc = NULL;
+ *CallersCallersPc = NULL;
+#endif
+ return;
+}
+
+USHORT
+RtlCaptureStackBackTrace(
+ IN ULONG FramesToSkip,
+ IN ULONG FramesToCapture,
+ OUT PVOID *BackTrace,
+ OUT PULONG BackTraceHash
+ )
+{
+ return 0;
+}
diff --git a/private/ntos/rtl/alpha/ghandler.c b/private/ntos/rtl/alpha/ghandler.c
new file mode 100644
index 000000000..c15c615b5
--- /dev/null
+++ b/private/ntos/rtl/alpha/ghandler.c
@@ -0,0 +1,436 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ ghandler.c
+
+Abstract:
+
+ This module implements the C specific exception handler that provides
+ structured exception handling for code generated by the GEM compiler.
+
+Author:
+
+ John Parks (parks) 12-Jan-1993
+ Thomas Van Baak (tvb) 28-Jan-1993
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+
+//
+// Define procedure prototypes for exception filter and termination handler
+// execution routines defined in jmpunwnd.s.
+//
+
+LONG
+__C_ExecuteExceptionFilter (
+ PEXCEPTION_POINTERS ExceptionPointers,
+ EXCEPTION_FILTER ExceptionFilter,
+ ULONG EstablisherFrame
+ );
+
+ULONG
+__C_ExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+//
+// Define local procedure prototypes.
+//
+
+EXCEPTION_DISPOSITION
+_OtsCSpecificHandler (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext
+ );
+
+ULONG
+_OtsLocalFinallyUnwind (
+ IN PSEH_CONTEXT SehContext,
+ IN PSEH_BLOCK TargetSeb,
+ IN PVOID RealFramePointer
+ );
+
+//
+// Define local macros.
+//
+
+#define IS_EXCEPT(Seb) ((Seb)->JumpTarget != 0)
+#define IS_FINALLY(Seb) ((Seb)->JumpTarget == 0)
+
+//
+// Initialize an exception record for the unwind with the SEB of the target
+// included as one information parameter. This is done so that the target
+// frame of the unwind may execute all the finally handlers necessary given
+// the SEB pointer at the unwind target.
+//
+
+#define MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord, Seb) { \
+ ExceptionRecord->ExceptionCode = STATUS_UNWIND; \
+ ExceptionRecord->ExceptionFlags = EXCEPTION_UNWINDING; \
+ ExceptionRecord->ExceptionRecord = NULL; \
+ ExceptionRecord->ExceptionAddress = 0; \
+ ExceptionRecord->NumberParameters = 1; \
+ ExceptionRecord->ExceptionInformation[0] = (ULONG)(Seb); \
+ }
+
+EXCEPTION_DISPOSITION
+_OtsCSpecificHandler (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext
+ )
+
+/*++
+
+Routine Description:
+
+ This function walks up the list of SEB's associated with the specified
+ procedure and calls except filters and finally handlers as necessary.
+
+ It is called in two different contexts:
+ (i) by the exception dispatcher after an exception is raised
+ (ii) by the unwinder during an unwind operation
+
+ In the first case, is searches the SEB list for except filters to evaluate.
+ In the second case, it searches for finally handlers to execute.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ EstablisherFrame - Supplies a (virtual frame) pointer to the frame of the
+ establisher function.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+ DispatcherContext - Supplies a pointer to the exception dispatcher or
+ unwind dispatcher context.
+
+Return Value:
+
+ If the exception is handled by one of the exception filter routines, then
+ there is no return from this routine and RtlUnwind is called. Otherwise,
+ an exception disposition value of continue execution or continue search is
+ returned.
+
+Notes:
+ In context (i) there are 3 possibilities:
+
+ (a) If an exception filter returns a value greater that 0 (meaning
+ that the associated handler should be invoked) there is no
+ return from this function. RtlUnwind is called to unwind the
+ stack to the exception handler corresponding to that filter.
+
+ (b) If an exception filter returns a value less than 0 (meaning
+ that the exception should be dismissed), this routine returns
+ value ExceptionContinueExecution.
+
+ (c) If every filter returns value 0 (meaning that the search for a
+ handler should continue elsewhere), this function returns
+ ExceptionContinueSearch.
+
+ In context (ii) there are 2 possibilities:
+
+ (d) If no branches are detected out of finally handlers, this
+ function returns ExceptionContinueSearch.
+
+ (e) If a branch is detected out of a finally handler, there is no
+ return from this routine. RtlUnwind is called to unwind to the
+ branch target (and cancel the current unwind).
+
+ There may be long jumps out of both except filters and finally handlers
+ in which case this routine will be peeled off the stack without returning.
+
+--*/
+
+{
+
+ ULONG ContinuationAddress;
+ EXCEPTION_FILTER ExceptionFilter;
+ PVOID ExceptionHandler;
+ EXCEPTION_POINTERS ExceptionPointers;
+ LONG FilterValue;
+ ULONG RealFramePointer;
+ PSEH_BLOCK Seb;
+ PSEH_CONTEXT SehContext;
+ PSEH_BLOCK TargetSeb;
+ TERMINATION_HANDLER TerminationHandler;
+
+ //
+ // Get the address of the SEH context which is at some negative offset
+ // from the virtual frame pointer. For GEM, the handler data field of
+ // the function entry contains that offset. The current SEB pointer and
+ // the RFP (static link) are obtained from the SEH context.
+ //
+
+ SehContext = (PSEH_CONTEXT)((ULONG)EstablisherFrame +
+ (LONG)DispatcherContext->FunctionEntry->HandlerData);
+ RealFramePointer = SehContext->RealFramePointer;
+
+ //
+ // If this is a dispatching context, walk up the list of SEBs evaluating
+ // except filters.
+ //
+
+ if (IS_DISPATCHING(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // Set up the ExceptionPointers structure that is used by except
+ // filters to obtain data for the GetExceptionInformation intrinsic
+ // function. Copy the current SEB pointer into a local variable
+ // because the real SEB pointer is only modified in unwind contexts.
+ //
+
+ ExceptionPointers.ExceptionRecord = ExceptionRecord;
+ ExceptionPointers.ContextRecord = ContextRecord;
+
+ for (Seb = SehContext->CurrentSeb; Seb != NULL; Seb = Seb->ParentSeb) {
+ if (IS_EXCEPT(Seb)) {
+
+ //
+ // This is an except filter. Get the addresses of the filter
+ // and exception handler from the SEB, then call the except
+ // filter.
+ //
+
+ ExceptionFilter = (EXCEPTION_FILTER)Seb->HandlerAddress;
+ ExceptionHandler = (PVOID)Seb->JumpTarget;
+
+ FilterValue = __C_ExecuteExceptionFilter(&ExceptionPointers,
+ ExceptionFilter,
+ RealFramePointer);
+
+ //
+ // If the except filter < 0, dismiss the exception. If > 0,
+ // store the exception code on the stack for the except
+ // handler, modify the given ExceptionRecord so that finally
+ // handlers will be called properly during the unwind, then
+ // unwind down to the except handler. If = 0, resume the
+ // search for except filters.
+ //
+
+ if (FilterValue < 0) {
+ return ExceptionContinueExecution;
+
+ } else if (FilterValue > 0) {
+ SehContext->ExceptionCode = ExceptionRecord->ExceptionCode;
+ MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
+ Seb->ParentSeb);
+ RtlUnwind2(EstablisherFrame,
+ ExceptionHandler,
+ ExceptionRecord,
+ 0,
+ ContextRecord);
+ }
+ }
+ }
+
+ } else if (!IS_TARGET_UNWIND(ExceptionRecord->ExceptionFlags)) {
+
+ //
+ // This is an unwind but is not the target frame. Since the function
+ // is being terminated, finally handlers for all try bodies that are
+ // presently in scope must be executed. Walk up the SEB list all the
+ // way to the top executing finally handlers. This corresponds to
+ // exiting all try bodies that are presently in scope.
+ //
+
+ while (SehContext->CurrentSeb != NULL) {
+
+ //
+ // Get the address of the SEB and then update the SEH context.
+ //
+
+ Seb = SehContext->CurrentSeb;
+ SehContext->CurrentSeb = Seb->ParentSeb;
+
+ if (IS_FINALLY(Seb)) {
+
+ //
+ // This is a finally handler. Get the address of the handler
+ // from the SEB and call the finally handler.
+ //
+
+ TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress;
+ ContinuationAddress =
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ RealFramePointer);
+
+ //
+ // If the finally handler returns a non-zero result, there
+ // was a branch out of the handler (to that address) and this
+ // routine should unwind to that target.
+ //
+
+ if (ContinuationAddress != 0) {
+ MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
+ SehContext->CurrentSeb);
+ RtlUnwind(EstablisherFrame,
+ (PVOID)ContinuationAddress,
+ ExceptionRecord,
+ 0);
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // This is the target frame of an unwind. Since the target may be
+ // in a different try scope than the one defined by the current SEB
+ // pointer, finally handlers between the two scopes must execute.
+ // Walk up the SEB list from the current SEB to the target SEB and
+ // execute all finally handlers encountered.
+ //
+
+ TargetSeb = (PSEH_BLOCK)ExceptionRecord->ExceptionInformation[0];
+ ContinuationAddress = _OtsLocalFinallyUnwind(SehContext,
+ TargetSeb,
+ (PVOID)RealFramePointer);
+ if (ContinuationAddress != 0) {
+
+ //
+ // A non-zero result indicates there was a branch out of a
+ // finally handler that was being executed during the unwind.
+ // This routine should unwind to that address.
+ //
+
+ MODIFY_UNWIND_EXCEPTION_RECORD(ExceptionRecord,
+ SehContext->CurrentSeb);
+ RtlUnwind(EstablisherFrame,
+ (PVOID)ContinuationAddress,
+ ExceptionRecord,
+ 0);
+ }
+ }
+
+ //
+ // Continue search for exception or termination handlers.
+ //
+
+ return ExceptionContinueSearch;
+}
+
+ULONG
+_OtsLocalFinallyUnwind (
+ IN PSEH_CONTEXT SehContext,
+ IN PSEH_BLOCK TargetSeb,
+ IN PVOID RealFramePointer
+ )
+
+/*++
+
+Routine Description:
+
+ This function walks up the SEB tree of the current procedure from the
+ current SEB to the target SEB and executes all the finally handlers it
+ encounters.
+
+ Calls to this function are inserted into user code by the compiler when
+ there are branches out of guarded regions that may require finally
+ handlers to execute.
+
+ This function is also called from _OtsCSpecificHandler when the target
+ frame is reached during an unwind operation. There may be finally handlers
+ that should execute before resuming execution at the unwind target.
+
+Arguments:
+
+ SehContext - Supplies the address of the SEH context structure which is
+ located in the stack frame.
+
+ TargetSeb - Supplies the address of the SEB corresponding to the branch
+ target address.
+
+ RealFramePointer - Supplies the (real frame) pointer of the establisher
+ frame, which is the current stack frame. This is used to set up the
+ static link if a finally handler is invoked.
+
+Return Value:
+
+ If a branch out of a finally handler is detected, the function value is
+ the address of the branch target. Otherwise, the function value is zero.
+
+--*/
+
+{
+
+ ULONG ContinuationAddress;
+ BOOLEAN Nested;
+ PSEH_BLOCK Seb;
+ TERMINATION_HANDLER TerminationHandler;
+
+ //
+ // If the SEB pointers are the same, no finally handlers need to execute.
+ // The branch is to a target location in the same guarded scope.
+ //
+
+ if (SehContext->CurrentSeb == TargetSeb) {
+ return 0;
+ }
+
+ //
+ // If the current SEB scope is not nested within the target SEB scope, no
+ // finally handlers need to execute. Reset the current SEB pointer to the
+ // target SEB pointer and return.
+ //
+
+ Nested = FALSE;
+ Seb = SehContext->CurrentSeb;
+
+ while (Seb != NULL) {
+ Seb = Seb->ParentSeb;
+ if (Seb == TargetSeb) {
+ Nested = TRUE;
+ break;
+ }
+ }
+ if (Nested == FALSE) {
+ SehContext->CurrentSeb = TargetSeb;
+ return 0;
+ }
+
+ //
+ // Walk up the list of SEB blocks executing finally handlers. If a branch
+ // out of a finally is encountered along the way, return the target
+ // address, otherwise return 0.
+ //
+
+ while (SehContext->CurrentSeb != TargetSeb) {
+
+ //
+ // Get the address of the SEB and then update the SEH context.
+ //
+
+ Seb = SehContext->CurrentSeb;
+ SehContext->CurrentSeb = Seb->ParentSeb;
+
+ if (IS_FINALLY(Seb)) {
+ TerminationHandler = (TERMINATION_HANDLER)Seb->HandlerAddress;
+ ContinuationAddress =
+ __C_ExecuteTerminationHandler(TRUE,
+ TerminationHandler,
+ (ULONG)RealFramePointer);
+ if (ContinuationAddress != 0) {
+ return ContinuationAddress;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/private/ntos/rtl/alpha/largeint.s b/private/ntos/rtl/alpha/largeint.s
new file mode 100644
index 000000000..e6fb89dbb
--- /dev/null
+++ b/private/ntos/rtl/alpha/largeint.s
@@ -0,0 +1,999 @@
+// TITLE("Large Integer Arithmetic")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// largeint.s
+//
+// Abstract:
+//
+// This module implements routines for performing extended integer
+// arithmetic.
+//
+// Author:
+//
+// David N. Cutler (davec) 18-Apr-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 9-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Alpha AXP Implementation Notes:
+//
+// The LargeInteger functions defined below implement a set of portable
+// 64-bit integer arithmetic operations for x86, Mips, and Alpha systems
+// using the LARGE_INTEGER data type. Code using LARGE_INTEGER variables
+// and calling these functions will be portable across all NT platforms.
+// This is the recommended approach to 64-bit arithmetic on NT.
+//
+// However, if performance is more important than portability, then for
+// Alpha systems, the native 64-bit integer data types may be used instead
+// of the LARGE_INTEGER type and the LargeInteger functions. The Alpha C
+// compilers support a __int64 data type (renamed LONGLONG in the system
+// header files). All C integer arithmetic operators may be used with
+// these quadword types. This eliminates the need for, and the overhead
+// of, any of the portable LargeInteger functions.
+//
+// In general, a LARGE_INTEGER cannot simply be converted to a LONGLONG
+// because of explicit references in application code to the 32-bit LowPart
+// and HighPart members of the LARGE_INTEGER structure.
+//
+// The performance difference between using the portable LARGE_INTEGER
+// types with LargeInteger functions and the Alpha LONGLONG types and
+// operations is often not significant enough to warrant modifying otherwise
+// portable code. In addition, future compiler optimization and inlining may
+// actually result in identical performance between portable LARGE_INTEGER
+// code and Alpha specific LONGLONG code. Therefore it is recommended to
+// keep NT source code portable.
+//
+// The Alpha source for the large integer functions below differs from the
+// Mips version since for Alpha a 64-bit argument or return value is passed
+// through a 64-bit integer register. In addition, most operations are
+// implemented with a single instruction. The division routines below,
+// however, like Mips, use a simple shift/subtract algorithm which is
+// considerably slower than the algorithm used by Alpha runtime division
+// routines.
+//
+
+ SBTTL("Convert Long to Large Integer")
+//++
+//
+// LARGE_INTEGER
+// RtlConvertLongToLargeInteger (
+// IN LONG SignedInteger
+// )
+//
+// Routine Description:
+//
+// This function converts a signed integer to a signed large integer
+// and returns the result.
+//
+// Arguments:
+//
+// SignedInteger (a0) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertLongToLargeInteger)
+
+ addl a0, 0, v0 // ensure canonical (signed long) form
+ ret zero, (ra) // return
+
+ .end RtlConvertLongToLargeInteger
+
+ SBTTL("Convert Ulong to Large Integer")
+//++
+//
+// LARGE_INTEGER
+// RtlConvertUlongToLargeInteger (
+// IN ULONG UnsignedInteger
+// )
+//
+// Routine Description:
+//
+// This function converts an unsigned integer to a signed large
+// integer and returns the result.
+//
+// Arguments:
+//
+// UnsignedInteger (a0) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertUlongToLargeInteger)
+
+ zap a0, 0xf0, v0 // convert canonical ULONG to quadword
+ ret zero, (ra) // return
+
+ .end RtlConvertUlongToLargeInteger
+
+ SBTTL("Enlarged Signed Integer Multiply")
+//++
+//
+// LARGE_INTEGER
+// RtlEnlargedIntegerMultiply (
+// IN LONG Multiplicand,
+// IN LONG Multiplier
+// )
+//
+// Routine Description:
+//
+// This function multiplies a signed integer by a signed integer and
+// returns a signed large integer result.
+//
+// N.B. An overflow is not possible.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedIntegerMultiply)
+
+ addl a0, 0, a0 // ensure canonical (signed long) form
+ addl a1, 0, a1 // ensure canonical (signed long) form
+ mulq a0, a1, v0 // multiply signed both quadwords
+ ret zero, (ra) // return
+
+ .end RtlEnlargedIntegerMultiply
+
+ SBTTL("Enlarged Unsigned Divide")
+//++
+//
+// ULONG
+// RtlEnlargedUnsignedDivide (
+// IN ULARGE_INTEGER Dividend,
+// IN ULONG Divisor,
+// IN OUT PULONG Remainder OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned long
+// and returns the resultant quotient and optionally the remainder.
+//
+// N.B. An overflow or divide by zero exception is possible.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the unsigned 64-bit dividend value.
+//
+// Divisor (a1) - Supplies the unsigned 32-bit divisor value.
+//
+// Remainder (a2) - Supplies an optional pointer to a variable that
+// receives the unsigned 32-bit remainder.
+//
+// Return Value:
+//
+// The unsigned long integer quotient is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedDivide)
+
+//
+// Check for division by zero.
+//
+
+ zap a1, 0xf0, a1 // convert ULONG divisor to quadword
+ beq a1, 30f // trap if divisor is zero
+
+//
+// Check for overflow. If the divisor is less than the upper half of the
+// dividend the quotient would be wider than 32 bits.
+//
+
+ srl a0, 32, t0 // get upper longword of dividend
+ cmpule a1, t0, t1 // is divisor <= upper dividend?
+ bne t1, 40f // if ne[true], then overflow trap
+
+//
+// Perform the shift/subtract loop 8 times and 4 bits per loop.
+//
+// t0 - Temp used for 0/1 results of compares.
+// t1 - High 64-bits of 128-bit (t1, a0) dividend.
+// t2 - Loop counter.
+//
+
+ ldiq t2, 32/4 // set iteration count
+
+ srl a0, 32, t1 // get top 32 bits of carry-out
+ sll a0, 32, a0 // preshift first 32 bits left
+
+10: cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ subq t2, 1, t2 // any more iterations?
+ bne t2, 10b //
+
+//
+// Finished with remainder value in t1 and quotient value in a0.
+//
+
+ addl a0, 0, v0 // set longword quotient return value
+ beq a2, 20f // skip optional remainder store
+ stl t1, 0(a2) // store longword remainder
+
+20: ret zero, (ra) // return
+
+//
+// Generate an exception for divide by zero and return a zero quotient if the
+// caller continues execution.
+//
+
+30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO
+
+ GENERATE_TRAP
+
+ ldil v0, 0 // return zero quotient
+ ret zero, (ra) // return
+
+//
+// Generate an exception for overflow.
+//
+
+40: ldiq a0, 0x8000000000000000 //
+ subqv zero, a0, v0 // negate in order to overflow
+ trapb // wait for trap to occur
+ ret zero, (ra) // return
+
+ .end RtlEnlargedUnsignedDivide
+
+ SBTTL("Enlarged Unsigned Integer Multiply")
+//++
+//
+// LARGE_INTEGER
+// RtlEnlargedUnsignedMultiply (
+// IN ULONG Multiplicand,
+// IN ULONG Multiplier
+// )
+//
+// Routine Description:
+//
+// This function multiplies an unsigned integer by an unsigned integer
+// and returns a signed large integer result.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedMultiply)
+
+ zap a0, 0xf0, a0 // convert canonical ULONG to quadword
+ zap a1, 0xf0, a1 // convert canonical ULONG to quadword
+ mulq a0, a1, v0 // multiply signed both quadwords
+ ret zero, (ra) // return
+
+ .end RtlEnlargedUnsignedMultiply
+
+ SBTTL("Extended Integer Multiply")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedIntegerMultiply (
+// IN LARGE_INTEGER Multiplicand,
+// IN LONG Multiplier
+// )
+//
+// Routine Description:
+//
+// This function multiplies a signed large integer by a signed integer and
+// returns the signed large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedIntegerMultiply)
+
+ addl a1, 0, a1 // ensure canonical (signed long) form
+ mulq a0, a1, v0 // multiply signed both quadwords
+ ret zero, (ra) // return
+
+ .end RtlExtendedIntegerMultiply
+
+ SBTTL("Extended Large Integer Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedLargeIntegerDivide (
+// IN LARGE_INTEGER Dividend,
+// IN ULONG Divisor,
+// IN OUT PULONG Remainder OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned long
+// and returns the quadword quotient and optionally the long remainder.
+//
+// N.B. A divide by zero exception is possible.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the dividend value.
+//
+// Divisor (a1) - Supplies the divisor value.
+//
+// Remainder (a2) - Supplies an optional pointer to a variable that
+// receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedLargeIntegerDivide)
+
+//
+// Check for division by zero.
+//
+
+ zap a1, 0xf0, a1 // convert canonical ULONG to quadword
+ beq a1, 30f // trap if divisor is zero
+
+//
+// Perform the shift/subtract loop 16 times and 4 bits per loop.
+//
+// t0 - Temp used for 0/1 results of compares.
+// t1 - High 64-bits of 128-bit (t1, a0) dividend.
+// t2 - Loop counter.
+//
+
+ ldiq t2, 64/4 // set iteration count
+
+ ldiq t1, 0 // zero-extend dividend to 128 bits
+
+10: cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ subq t2, 1, t2 // any more iterations?
+ bne t2, 10b //
+
+//
+// Finished with remainder value in t1 and quotient value in a0.
+//
+
+ mov a0, v0 // set quadword quotient return value
+ beq a2, 20f // skip optional remainder store
+ stl t1, 0(a2) // store longword remainder
+
+20: ret zero, (ra) // return
+
+//
+// Generate an exception for divide by zero.
+//
+
+30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO
+
+ GENERATE_TRAP
+
+ br zero, 30b // in case they continue
+
+ .end RtlExtendedLargeIntegerDivide
+
+ SBTTL("Extended Magic Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedMagicDivide (
+// IN LARGE_INTEGER Dividend,
+// IN LARGE_INTEGER MagicDivisor,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function divides a signed large integer by an unsigned large integer
+// and returns the signed large integer result. The division is performed
+// using reciprocal multiplication of a signed large integer value by an
+// unsigned large integer fraction which represents the most significant
+// 64-bits of the reciprocal divisor rounded up in its least significant bit
+// and normalized with respect to bit 63. A shift count is also provided
+// which is used to truncate the fractional bits from the result value.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the dividend value.
+//
+// MagicDivisor (a1) - Supplies the magic divisor value which
+// is a 64-bit multiplicative reciprocal.
+//
+// Shiftcount (a2) - Supplies the right shift adjustment value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedMagicDivide)
+
+//
+// Make the dividend positive for the reciprocal multiplication to work.
+//
+
+ negq a0, t0 // negate dividend
+ cmovgt a0, a0, t0 // get absolute value in t0
+
+//
+// Multiply both quadword arguments together and take only the upper 64 bits of
+// the resulting 128 bit product. This can be done using the umulh instruction.
+//
+// Division of a dividend by a constant divisor through reciprocal
+// multiplication works because a/b is equivalent to (a*x)/(b*x) for any
+// value of x. Now if b is a constant, some "magic" integer x can be chosen,
+// based on the value of b, so that (b*x) is very close to a large power of
+// two (e.g., 2^64). Then a/b = (a*x)/(b*x) = (a*x)/(2^64) = (a*x)>>64. This
+// effectively turns the problem of division by a constant into multiplication
+// by a constant which is a much faster operation.
+//
+
+ umulh t0, a1, t1 // multiply high both quadword arguments
+ sra t1, a2, t1 // shift result right by requested amount
+
+//
+// Make the result negative if the dividend was negative.
+//
+
+ negq t1, t0 // negate result
+ cmovgt a0, t1, t0 // restore sign of dividend
+
+ mov t0, v0 // set quadword result return value
+ ret zero, (ra) // return
+
+ .end RtlExtendedMagicDivide
+
+ SBTTL("Large Integer Add")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerAdd (
+// IN LARGE_INTEGER Addend1,
+// IN LARGE_INTEGER Addend2
+// )
+//
+// Routine Description:
+//
+// This function adds a signed large integer to a signed large integer and
+// returns the signed large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Addend1 (a0) - Supplies the first addend value.
+//
+// Addend2 (a1) - Supplies the second addend value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerAdd)
+
+ addq a0, a1, v0 // add both quadword arguments
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerAdd
+
+ SBTTL("Large Integer Arithmetic Shift Right")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerArithmeticShift (
+// IN LARGE_INTEGER LargeInteger,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function shifts a signed large integer right by an unsigned integer
+// modulo 64 and returns the shifted signed large integer result.
+//
+// Arguments:
+//
+// LargeInteger (a0) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a1) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerArithmeticShift)
+
+ sra a0, a1, v0 // shift the quadword right/arithmetic
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerArithmeticShift
+
+ SBTTL("Large Integer Divide")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerDivide (
+// IN LARGE_INTEGER Dividend,
+// IN LARGE_INTEGER Divisor,
+// IN OUT PLARGE_INTEGER Remainder OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned large
+// integer and returns the quadword quotient and optionally the quadword
+// remainder.
+//
+// N.B. A divide by zero exception is possible.
+//
+// Arguments:
+//
+// Dividend (a0) - Supplies the dividend value.
+//
+// Divisor (a1) - Supplies the divisor value.
+//
+// Remainder (a2) - Supplies an optional pointer to a variable that
+// receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerDivide)
+
+//
+// Check for division by zero.
+//
+
+ beq a1, 30f // trap if divisor is zero
+
+//
+// Perform the shift/subtract loop 16 times and 4 bits per loop.
+//
+// t0 - Temp used for 0/1 results of compares.
+// t1 - High 64-bits of 128-bit (t1, a0) dividend.
+// t2 - Loop counter.
+//
+
+ ldiq t2, 64/4 // set iteration count
+
+ ldiq t1, 0 // zero-extend dividend to 128 bits
+
+10: cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ cmplt a0, 0, t0 // predict low-dividend shift carry-out
+ addq a0, a0, a0 // shift low-dividend left
+ addq t1, t1, t1 // shift high-dividend left
+ bis t1, t0, t1 // merge in carry-out of low-dividend
+
+ cmpule a1, t1, t0 // if dividend >= divisor,
+ addq a0, t0, a0 // then set quotient bit
+ subq t1, a1, t0 // subtract divisor from dividend,
+ cmovlbs a0, t0, t1 // if dividend >= divisor
+
+ subq t2, 1, t2 // any more iterations?
+ bne t2, 10b //
+
+//
+// Finished with remainder value in t1 and quotient value in a0.
+//
+
+ mov a0, v0 // set quadword quotient return value
+ beq a2, 20f // skip optional remainder store
+ stq t1, 0(a2) // store quadword remainder
+
+20: ret zero, (ra) // return
+
+//
+// Generate an exception for divide by zero.
+//
+
+30: ldil a0, GENTRAP_INTEGER_DIVIDE_BY_ZERO
+
+ GENERATE_TRAP
+
+ br zero, 30b // in case they continue
+
+ .end RtlLargeIntegerDivide
+
+ SBTTL("Large Integer Negate")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerNegate (
+// IN LARGE_INTEGER Subtrahend
+// )
+//
+// Routine Description:
+//
+// This function negates a signed large integer and returns the signed
+// large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Subtrahend (a0) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerNegate)
+
+ subq zero, a0, v0 // negate the quadword argument
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerNegate
+
+ SBTTL("Large Integer Shift Left")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerShiftLeft (
+// IN LARGE_INTEGER LargeInteger,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function shifts a signed large integer left by an unsigned integer
+// modulo 64 and returns the shifted signed large integer result.
+//
+// Arguments:
+//
+// LargeInteger (a0) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a1) - Supplies the left shift count.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftLeft)
+
+ sll a0, a1, v0 // shift the quadword argument left
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerShiftLeft
+
+ SBTTL("Large Integer Logical Shift Right")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerShiftRight (
+// IN LARGE_INTEGER LargeInteger,
+// IN CCHAR ShiftCount
+// )
+//
+// Routine Description:
+//
+// This function shifts an unsigned large integer right by an unsigned
+// integer modulo 64 and returns the shifted unsigned large integer result.
+//
+// Arguments:
+//
+// LargeInteger (a0) - Supplies the large integer to be shifted.
+//
+// ShiftCount (a1) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftRight)
+
+ srl a0, a1, v0 // shift the quadword right/logical
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerShiftRight
+
+ SBTTL("Large Integer Subtract")
+//++
+//
+// LARGE_INTEGER
+// RtlLargeIntegerSubtract (
+// IN LARGE_INTEGER Minuend,
+// IN LARGE_INTEGER Subtrahend
+// )
+//
+// Routine Description:
+//
+// This function subtracts a signed large integer from a signed large
+// integer and returns the signed large integer result.
+//
+// N.B. An overflow is possible, but no exception is generated.
+//
+// Arguments:
+//
+// Minuend (a0) - Supplies the minuend value.
+//
+// Subtrahend (a1) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is returned as the function value in v0.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerSubtract)
+
+ subq a0, a1, v0 // subtract the quadword arguments
+ ret zero, (ra) // return
+
+ .end RtlLargeIntegerSubtract
+
+ SBTTL("128-bit Signed Integer Multiplication")
+//++
+//
+// VOID
+// Rtlp128BitSignedMultiply (
+// IN LONGLONG Multiplicand,
+// IN LONGLONG Multiplier,
+// IN OUT PULONGLONG ProductLower,
+// IN OUT PLONGLONG ProductUpper
+// )
+//
+// Routine Description:
+//
+// This function multiplies a signed quadword (or signed large integer)
+// by a signed quadword (or signed large integer) and returns the full
+// 128-bit signed product indirectly through pointers to two quadwords.
+//
+// N.B. Signed multiplication is implemented with an unsigned multiply
+// followed by up to two subtractions. The subtractions are necessary for
+// the following reason. Within an N-bit register, a negative N-bit two's
+// compliment signed integer (-x), is equal to (2^N - x). So in this case
+// of 64x64 bit multiplication, the following holds:
+//
+// (-x) * (-y) =
+// (2^64 - x) * (2^64 - y) =
+// 2^128 - (2^64)*x - (2^64)*y + (x*y)
+//
+// The lower 64-bits of the 128-bit product is determined solely by the
+// (x*y) term. For a 128-bit result, the 2^128 term is irrelevant. And if
+// either the x and/or the y operand is negative, then either y and/or x
+// must be subtracted from the upper 64 bits of the 128-bit product.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// ProductLower (a2) - Supplies a pointer to an unsigned quadword variable
+// that receives the lower 64-bits of the product.
+//
+// ProductLower (a3) - Supplies a pointer to a signed quadword variable
+// that receives the upper 64-bits of the product.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(Rtlp128BitSignedMultiply)
+
+ mulq a0, a1, t0 // get lower 64 bits of product
+ stq t0, 0(a2) // store lower half of 128-bit product
+
+ umulh a0, a1, t1 // get upper 64 bits of product
+ subq t1, a0, t2 // subtract first operand from product
+ cmovge a1, t1, t2 // if second operand is negative
+ subq t2, a1, t3 // subtract second operand from product
+ cmovge a0, t2, t3 // if first operand is negative
+ stq t3, 0(a3) // store upper half of 128-bit product
+
+ ret zero, (ra) // return
+
+ .end Rtlp128BitSignedMultiply
+
+ SBTTL("128-bit Unsigned Integer Multiplication")
+//++
+//
+// VOID
+// Rtlp128BitUnsignedMultiply (
+// IN ULONGLONG Multiplicand,
+// IN ULONGLONG Multiplier,
+// IN OUT PULONGLONG ProductLower,
+// IN OUT PULONGLONG ProductUpper
+// )
+//
+// Routine Description:
+//
+// This function multiplies an unsigned quadword (or large integer) by an
+// unsigned quadword (or large integer) and returns the full 128-bit unsigned
+// product indirectly through pointers to two unsigned quadwords.
+//
+// Arguments:
+//
+// Multiplicand (a0) - Supplies the multiplicand value.
+//
+// Multiplier (a1) - Supplies the multiplier value.
+//
+// ProductLower (a2) - Supplies a pointer to an unsigned quadword variable
+// that receives the lower 64-bits of the product.
+//
+// ProductLower (a3) - Supplies a pointer to an unsigned quadword variable
+// that receives the upper 64-bits of the product.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(Rtlp128BitUnsignedMultiply)
+
+ mulq a0, a1, t0 // get lower 64 bits of product
+ stq t0, 0(a2) // store lower half of 128-bit product
+
+ umulh a0, a1, t1 // get upper 64 bits of product
+ stq t1, 0(a3) // store upper half of 128-bit product
+
+ ret zero, (ra) // return
+
+ .end Rtlp128BitUnsignedMultiply
diff --git a/private/ntos/rtl/alpha/localrtl.c b/private/ntos/rtl/alpha/localrtl.c
new file mode 100644
index 000000000..e8e91b9ab
--- /dev/null
+++ b/private/ntos/rtl/alpha/localrtl.c
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ localrtl.c
+
+Abstract:
+
+ This module contains alternate implementations of each of the Rtl Memory
+ functions and other common functions required by the Rtl Memory test
+ programs.
+
+Author:
+
+ Thomas Van Baak (tvb) 11-Jan-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include "localrtl.h"
+
+//
+// Simple pattern generator.
+//
+
+#define PATTERN "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define PATTERN_SIZE (sizeof(PATTERN) - 1)
+UCHAR Pattern[] = PATTERN;
+
+VOID
+FillPattern(PUCHAR To, ULONG Length)
+{
+ ULONG Index;
+ ULONG Rotor = 0;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ To[Index] = Pattern[Rotor];
+ Rotor += 1;
+ if (Rotor == PATTERN_SIZE) {
+ Rotor = 0;
+ }
+ }
+}
+
+//
+// The following functions are simple, non-optimized, (and thus maybe even
+// bug-proof) implementations of each of the Rtl Memory functions.
+//
+
+ULONG
+LocalCompareMemory (
+ PVOID Source1,
+ PVOID Source2,
+ ULONG Length
+ )
+{
+ ULONG Index;
+ PUCHAR Left = Source1;
+ ULONG Match;
+ PUCHAR Right = Source2;
+
+ Match = 0;
+ for (Index = 0; Index < Length; Index += 1) {
+ if (Left[Index] != Right[Index]) {
+ break;
+ }
+ Match += 1;
+ }
+ return Match;
+}
+
+ULONG
+LocalCompareMemoryUlong (
+ PVOID Source,
+ ULONG Length,
+ ULONG Pattern
+ )
+{
+ PULONG From = Source;
+ ULONG Index;
+ ULONG Match;
+
+ Match = 0;
+ for (Index = 0; Index < Length / sizeof(ULONG); Index += 1) {
+ if (From[Index] != Pattern) {
+ break;
+ }
+ Match += sizeof(ULONG);
+ }
+ return Match;
+}
+
+VOID
+LocalMoveMemory (
+ PVOID Destination,
+ PVOID Source,
+ ULONG Length
+ )
+{
+ PUCHAR From = Source;
+ ULONG Index;
+ PUCHAR To = Destination;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ if (To <= From) {
+ To[Index] = From[Index];
+
+ } else {
+ To[Length - 1 - Index] = From[Length - 1 - Index];
+ }
+ }
+}
+
+VOID
+LocalFillMemory (
+ PVOID Destination,
+ ULONG Length,
+ UCHAR Fill
+ )
+{
+ ULONG Index;
+ PUCHAR To = Destination;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ To[Index] = Fill;
+ }
+}
+
+VOID
+LocalFillMemoryUlong (
+ PVOID Destination,
+ ULONG Length,
+ ULONG Pattern
+ )
+{
+ ULONG Index;
+ PULONG To = Destination;
+
+ for (Index = 0; Index < Length / sizeof(ULONG); Index += 1) {
+ To[Index] = Pattern;
+ }
+}
+
+VOID
+LocalZeroMemory (
+ PVOID Destination,
+ ULONG Length
+ )
+{
+ ULONG Index;
+ PUCHAR To = Destination;
+
+ for (Index = 0; Index < Length; Index += 1) {
+ To[Index] = 0;
+ }
+}
diff --git a/private/ntos/rtl/alpha/localrtl.h b/private/ntos/rtl/alpha/localrtl.h
new file mode 100644
index 000000000..5445f2b38
--- /dev/null
+++ b/private/ntos/rtl/alpha/localrtl.h
@@ -0,0 +1,82 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ localrtl.h
+
+Abstract:
+
+ This module contains the local Rtl header file.
+
+Author:
+
+ Thomas Van Baak (tvb) 11-Jan-1993
+
+Revision History:
+
+--*/
+
+//
+// Define function prototypes of local Rtl Memory functions.
+//
+
+ULONG
+LocalCompareMemory (
+ PVOID Source1,
+ PVOID Source2,
+ ULONG Length
+ );
+
+ULONG
+LocalCompareMemoryUlong (
+ PVOID Source,
+ ULONG Length,
+ ULONG Pattern
+ );
+
+VOID
+LocalMoveMemory (
+ PVOID Destination,
+ CONST VOID *Source,
+ ULONG Length
+ );
+
+VOID
+LocalFillMemory (
+ PVOID Destination,
+ ULONG Length,
+ UCHAR Fill
+ );
+
+VOID
+LocalFillMemoryUlong (
+ PVOID Destination,
+ ULONG Length,
+ ULONG Pattern
+ );
+
+VOID
+LocalZeroMemory (
+ PVOID Destination,
+ ULONG Length
+ );
+
+//
+// Define function prototypes of other common functions.
+//
+
+VOID
+FillPattern(
+ PUCHAR To,
+ ULONG Length
+ );
+
+//
+// Define maximum values for the string tests.
+//
+
+#define MAX_MARGIN 8
+#define MAX_OFFSET 32
+#define MAX_LENGTH 72
diff --git a/private/ntos/rtl/alpha/longjmp.s b/private/ntos/rtl/alpha/longjmp.s
new file mode 100644
index 000000000..1999d1573
--- /dev/null
+++ b/private/ntos/rtl/alpha/longjmp.s
@@ -0,0 +1,200 @@
+// TITLE("Long Jump")
+//++
+//
+// Copyright (c) 1993 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// longjmp.s
+//
+// Abstract:
+//
+// This module implements the Alpha specific routine to perform a long
+// jump operation. Three jump buffer types are supported: unsafe, safe
+// acc-style (virtual frame pointer, PC mapped SEH scope), and safe
+// GEM-style (real frame pointer, SEB-based SEH context).
+//
+// N.B. This function has been replaced by setjmp/setjmpex/longjmp in the
+// C runtime library. It remains here for backwards compatibility of
+// Beta 2 applications expecting setjmp and longjmp to be present in
+// ntdll, or for new acc or GEM compiled applications that link with
+// ntdll before libc.
+//
+// Author:
+//
+// David N. Cutler (davec) 2-Apr-1993
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 22-Apr-1993
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Define jump buffer types.
+//
+// _JMPBUF_TYPE_ZERO was used by the Beta 2 ntdll setjmp and can be handled
+// the same as _JMPBUF_TYPE_ACC.
+//
+// _JMPBUF_TYPE_FAST is for jump buffers containing the set of non-volatile
+// integer and floating registers. This form of setjmp/longjmp is not
+// compatible with SEH. It is an order of magnitude faster however.
+//
+// _JMPBUF_TYPE_ACC is for setjmp/longjmp compatible with SEH. The Alpha
+// acc compiler uses a virtual frame pointer, and SEH scope is inferred
+// from the PC through passive PC-mapping scope tables.
+//
+// _JMPBUF_TYPE_GEM is for setjmp/longjmp compatible with SEH. The Alpha
+// GEM C compiler uses a real frame pointer, and SEH scope is maintained
+// actively with an SEB pointer.
+//
+
+#define _JMPBUF_TYPE_ZERO 0
+#define _JMPBUF_TYPE_FAST 1
+#define _JMPBUF_TYPE_ACC 2
+#define _JMPBUF_TYPE_GEM 3
+
+ SBTTL("Long Jump")
+//++
+//
+// int
+// longjmp (
+// IN jmp_buf JumpBuffer,
+// IN int ReturnValue
+// )
+//
+// Routine Description:
+//
+// This function performs a long jump to the context specified by the
+// jump buffer.
+//
+// Arguments:
+//
+// JumpBuffer (a0) - Supplies the address of a jump buffer that contains
+// jump information.
+//
+// N.B. This is an array of double's to force quadword alignment.
+//
+// ReturnValue (a1) - Supplies the value that is to be returned to the
+// caller of set jump.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(longjmp)
+
+ ldil t0, 1 // force nonzero value, if
+ cmoveq a1, t0, a1 // given return value is zero
+
+ ldl t1, JbType(a0) // get setjmp context type flag
+ subq t1, 1, t2 // if eq 1, fast, unsafe longjmp
+ bne t2, 10f // otherwise, provide safe longjmp
+
+//
+// Type 0x1: Provide unsafe handling of longjmp.
+//
+
+ mov a1, v0 // set return value
+ .set noreorder
+ .set noat
+ ldt f2, JbFltF2(a0) // restore floating registers f2 - f9
+ ldt f3, JbFltF3(a0) //
+ ldt f4, JbFltF4(a0) //
+ ldt f5, JbFltF5(a0) //
+ ldt f6, JbFltF6(a0) //
+ ldt f7, JbFltF7(a0) //
+ ldt f8, JbFltF8(a0) //
+ ldt f9, JbFltF9(a0) //
+
+ ldq s0, JbIntS0(a0) // restore integer registers s0 - s6/fp
+ ldq s1, JbIntS1(a0) //
+ ldq s2, JbIntS2(a0) //
+ ldq s3, JbIntS3(a0) //
+ ldq s4, JbIntS4(a0) //
+ ldq s5, JbIntS5(a0) //
+ ldq fp, JbIntS6(a0) //
+ .set at
+ .set reorder
+
+ ldq a1, JbFir(a0) // get setjmp return address
+ ldq sp, JbIntSp(a0) // restore stack pointer
+ jmp zero, (a1) // jump back to setjmp site
+
+//
+// Type 0x0: Provide safe handling of longjmp (idw 404 style).
+// Type 0x2: Provide safe handling of longjmp (acc style).
+//
+
+10: bic t1, 0x2, t2 // if 0 or 2, safe acc longjmp
+ bne t2, longjmpRfp // if not, safe GEM longjmp
+
+ mov a1, a3 // set return value
+ mov zero, a2 // set exception record address
+ ldl a1, JbPc(a0) // set target instruction address
+ ldl a0, JbFp(a0) // set target virtual frame pointer
+ br zero, RtlUnwind // finish in common code
+
+ .end longjmp
+
+ SBTTL("Long Jump - GEM")
+
+ .struct 0
+LjRa: .space 8 // saved return address
+ .space 8 // padding for 16-byte stack alignment
+LjEr: .space ExceptionRecordLength // local exception record
+LongjmpFrameLength:
+
+//
+// Type 0x3: Provide safe handling of longjmp (GEM style).
+//
+
+ NESTED_ENTRY(longjmpRfp, LongjmpFrameLength, ra)
+
+ lda sp, -LongjmpFrameLength(sp) // allocate stack frame
+ stq ra, LjRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// The SEB is passed to the GEM exception handler through an exception
+// record. Set up the following local exception record:
+//
+// ExceptionRecord.ExceptionCode = STATUS_UNWIND;
+// ExceptionRecord.ExceptionFlags = EXCEPTION_UNWINDING;
+// ExceptionRecord.ExceptionRecord = NULL;
+// ExceptionRecord.ExceptionAddress = 0;
+// ExceptionRecord.NumberParameters = 1;
+// ExceptionRecord.ExceptionInformation[0] = Seb;
+//
+
+10: mov a1, a3 // set return value
+ lda a2, LjEr(sp) // set exception record address
+
+ ldil t0, STATUS_UNWIND // get status code
+ stl t0, ErExceptionCode(a2) // store in exception record
+ ldil t1, EXCEPTION_UNWINDING // get exception flags
+ stl t1, ErExceptionFlags(a2) // store in exception record
+ stl zero, ErExceptionRecord(a2) // clear indirect exception record
+ stl zero, ErExceptionAddress(a2) // clear exception address
+ ldil t2, 1 // get number of parameters
+ stl t2, ErNumberParameters(a2) // store in exception record
+ ldl t3, JbSeb(a0) // get SEB pointer
+ stl t3, ErExceptionInformation(a2) // store in exception record
+ ldl a1, JbPc(a0) // set target instruction address
+ ldl a0, JbFp(a0) // set target real frame pointer
+
+ bsr ra, RtlUnwindRfp // finish in common code
+
+ .end longjmpRfp
diff --git a/private/ntos/rtl/alpha/lzntaxp.s b/private/ntos/rtl/alpha/lzntaxp.s
new file mode 100644
index 000000000..2eea2462d
--- /dev/null
+++ b/private/ntos/rtl/alpha/lzntaxp.s
@@ -0,0 +1,485 @@
+// TITLE("Decompression Engine")
+//++
+//
+// Copyright (c) 1994 Microsoft Corporation
+//
+// Module Name:
+//
+// lzntaxp.s
+//
+// Abstract:
+//
+// This module implements the lznt1 decompression engine needed
+// to support file system decompression.
+//
+// Author:
+//
+// John Vert (jvert) 19-Jul-1994
+//
+// Environment:
+//
+// Any.
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+
+ SBTTL("Decompress a buffer")
+//++
+// NTSTATUS
+// LZNT1DecompressChunk (
+// OUT PUCHAR UncompressedBuffer,
+// IN PUCHAR EndOfUncompressedBufferPlus1,
+// IN PUCHAR CompressedBuffer,
+// IN PUCHAR EndOfCompressedBufferPlus1,
+// OUT PULONG FinalUncompressedChunkSize
+// )
+//
+// Routine Description:
+//
+// This function decodes a stream of compression tokens and places the
+// resultant output into the destination buffer. The format of the input
+// is described ..\lznt1.c. As the input is decoded, checks are made to
+// ensure that no data is read past the end of the compressed input buffer
+// and that no data is stored past the end of the output buffer. Violations
+// indicate corrupt input and are indicated by a status return.
+//
+//
+// Arguments:
+//
+// UncompressedBuffer (a0) - pointer to destination of uncompression.
+//
+// EndOfUncompressedBufferPlus1 (a1) - pointer just beyond the
+// output buffer. This is used for consistency checking of the stored
+// compressed data.
+//
+// CompressedBuffer (a2) - pointer to compressed source. This begins
+// with a header word followed by a tag byte describing which of the
+// following tokens are literals and which are copy groups.
+//
+// EndOfCompressedBufferPlus1 (a3) - pointer just beyond end of input
+// buffer. This is used to terminate the decompression.
+//
+// FinalUncompressedChunkSize (a4) - pointer to a returned decompressed
+// size. This has meaningful data ONLY when LZNT1DecompressChunk returns
+// STATUS_SUCCESS
+//
+// Return Value:
+//
+// STATUS_SUCCESS is returned only if the decompression consumes thee entire
+// input buffer and does not exceed the output buffer.
+// STATUS_BAD_COMPRESSION_BUFFER is returned when the output buffer would be
+// overflowed.
+//--
+//
+//
+// Register usage:
+// a0 - current destination pointer
+// a1 - end of output buffer
+// a2 - current source pointer
+// a3 - end of compressed buffer
+// a4 - pointer to decompressed size
+// a5 - current decompressed size
+// v0 - boundary for next format transition
+// t0 - count of consecutive copy tokens
+// t1 - current flag byte
+// t2 - bits of t1 processed
+// t3 - temp
+// t4 - temp
+// t5 - bytes following flag byte
+// t6 - temp
+// t7 - temp
+// t8 - temp
+// t9 - temp
+// t10 - temp
+// t11 - current length mask
+// t12 - current displacement shift
+//
+ LEAF_ENTRY(LZNT1DecompressChunk)
+
+ bis zero, zero, a5 // initialize decompressed size
+ ldil t12, 12 // get initial displacement shift
+ lda t11, -1(zero)
+ sll t11, 12, t11 // get initial length mask
+
+ addq a0, 16, v0 // get displacement boundary
+ subq a3, 1, a3 // adjust input buffer end
+10:
+ addq a0, 8, t3 // check for at least 8 bytes available output
+ addq a2, 17, t4 // check for at least 17 bytes available input
+ cmpule t3, a1, t2 // check for output buffer exceeded
+ cmpule t4, a3, t3 // check for input buffer exceeded
+ ldq_u t0, 0(a2) // load flag byte and any subsequent bytes
+ extbl t0, a2, t1 // extract flag byte
+ addq a2, 1, a2
+ beq t3, CopyTailFlag // input buffer exceeded
+ ldq_u t6, 7(a2) // load subsequent bytes
+ and a2, 7, t10 // check for qword alignment
+ extql t0, a2, t3 // extract low part of next 8 bytes
+ extqh t6, a2, t4 // extract high part
+ bis t3, t4, t5 // merge
+ cmoveq t10, t6, t5 // qword aligned, undo merge
+ beq t2, CopyTailFlag // output buffer exceeded
+ bne t1, 20f // !=0 deal with copy tokens
+
+//
+// This is the special case where the next 8 bytes are literal tokens.
+//
+ addq a5, 8, a5 // increment bytes copied
+ addq a0, 8, a0 // increment destination pointer
+ lda a2, 8(a2) // compute pointer to next tag byte
+ and a0, 7, t4 // check for qword-aligned destination
+ bne t4, 15f
+
+//
+// Destination is quadword aligned, do direct store
+//
+ stq t5, -8(a0)
+ br zero, 10b // do next tag byte
+15:
+//
+// Destination is not quadword aligned, merge eight bytes into buffer.
+//
+ ldq_u t4, -8(a0) // get low destination
+ mskql t4, a0, t0 // clear position in destination
+ insql t5, a0, t2 // get low part in position
+ bis t0, t2, t4 // merge in new bytes
+ stq_u t4, -8(a0) // store low part
+ ldq_u t4, -1(a0) // get high destination
+ mskqh t4, a0, t0 // clear position in destination
+ insqh t5, a0, t2 // get high part in position
+ bis t0, t2, t4 // merge in new bytes
+ stq_u t4, -1(a0) // store high part
+ br zero, 10b // do next tag byte
+
+20:
+//
+// Tag indicates both literal bytes and copy tokens. The approach
+// we use here is to loop through the bits counting the consecutive
+// literal bytes until we find a copy token.
+//
+ bis zero, zero, t0 // set bit count to zero
+ ldil t2, 8 // set count of bits to process
+25:
+ blbs t1, CopyToken // go copy the token.
+
+//
+// Count the consecutive clear bits.
+//
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+ blbs t1, 30f
+ srl t1, 1, t1
+ addq t0, 1, t0
+30:
+ bis zero, 1, t9 // compute byte mask
+ sll t9, t0, t3
+ subq t3, 1, t9
+ zapnot t5, t9, t4 // get masked bytes to store
+ and a0, 7, t3 // get byte position of dest
+ addq t3, t0, t10 // compute ending offset
+ sll t9, t3, t9 // shift byte mask into position
+ ldq_u t7, 0(a0) // get low part of dest.
+ insql t4, t3, t6 // insert source bytes into position
+ zap t7, t9, t8 // clear dest
+ bis t8, t6, t7 // merge bytes to store
+ stq_u t7, 0(a0) // store merged result
+
+//
+// Check to see whether the bytes to store extend into the next
+// quadword of the destination.
+//
+ cmpult t10, 8, t9
+ bne t9, 40f // ending offset < 8, next quadword unaffected
+ insqh t4, t3, t6 // shift source bytes into position
+ stq_u t6, 8(a0) // store merged results
+
+40:
+ addq a0, t0, a0 // adjust destination pointer
+ addq a2, t0, a2 // adjust source pointer
+ addq a5, t0, a5 // adjust bytes copied
+ subq t2, t0, t2 // adjust flag bits left
+
+CopyToken:
+//
+// Get the token word
+//
+ ldq_u t10, 0(a2)
+ ldq_u t6, 1(a2)
+ extwl t10, a2, t7
+ extwh t6, a2, t8
+ bis t7, t8, t9
+
+//
+// Check the displacement and length.
+//
+50:
+ cmpult v0, a0, t10
+ bne t10, UpdateFormat // if nez, max displacement < output
+ srl t9, t12, t7 // compute offset
+ andnot t9, t11, t8 // compute length
+ addq t8, 3, t8
+ addq t7, 1, t7
+
+//
+// Check displacement against number of bytes copied
+//
+ cmpule t7, a5, t10
+ beq t10, ErrorExit // if eqz, bytes copied <= displacement
+//
+// Account for end of output buffer and compute ending
+// address of copy.
+//
+ addq a0, t8, t9
+ cmpule t9, a1, t10
+ cmoveq t10, a1, t9 // if ending address > buffer end, set
+ // buffer end to ending address
+ subq a0, t7, t5 // compute copy source
+//
+// Do the copy.
+// t5 - source
+// a0 - dest
+// t9 - end of destination
+//
+// If the source is more than eight bytes away from the destination,
+// we can copy a quadword at a time. Otherwise, we must copy a byte
+// at a time to ensure that fills work correctly.
+//
+
+ subq t9, a0, t7 // compute number of bytes to copy
+ addq a5, t7, a5 // adjust bytes copied here, the only
+ // time this will not be correct is
+ // in an error condition.
+ subq a0, t5, t10 // test if source is >= 8 bytes away
+ cmpult t10, 8, t10 // from destination
+ bne t10, FillBytes // if so, do byte fill
+
+//
+// Write the low part of the first quadword out. This will cause the
+// destination to become qword aligned.
+//
+ ldq_u t10, 0(t5) // get low part of source qword
+ ldq_u t8, 7(t5) // get high part of source qword
+ extql t10, t5, t7
+ extqh t8, t5, t3
+ bis t3, t7, t10 // get aligned qword
+ ldq_u t7, 0(a0) // get low part of source destination
+ insql t10, a0, t3
+ mskql t7, a0, t4 // clear bytes in destination
+ bis t4, t3, t7 // merge qword into destination
+ stq_u t7, 0(a0) // store low part of quadword
+
+ addq a0, 8, t10 // compute qword-aligned destination
+ bic t10, 7, t10
+ subq t10, a0, t8
+ addq t5, t8, t5 // increment source
+ bis t10, zero, a0 // increment destination
+
+//
+// Recompute number of quadwords to copy now that the destination has
+// been qword aligned
+//
+ subq t9, a0, t7
+ cmovlt t7, t9, a0 // back up destination if we went too far
+ ble t7, 64f // no bytes remaining
+ srl t7, 3, t4
+ and t5, 7, t3 // get alignment of source
+ ldq_u t10, 0(t5)
+ bne t3, UnalignedQwordCopy
+ beq t4, 60f // no qwords remaining
+
+AlignedQwordLoop:
+ stq t10, 0(a0) // store qword
+ addq t5, 8, t5 // increment source
+ addq a0, 8, a0 // increment dest
+ subq t4, 1, t4 // decrement remaining qword
+ ldq t10, 0(t5) // get next qword
+ bne t4, AlignedQwordLoop
+ cmpult a0, t9, t4
+ beq t4, 64f // no bytes reamining
+//
+// Tail bytes are in t10, go ahead and store them.
+// We know we will not store beyond the containing qword of
+// the end of the buffer
+//
+60:
+ stq t10, 0(a0)
+ bis t9, zero, a0 // increment dest
+ br zero, 64f
+
+UnalignedQwordCopy:
+ beq t4, 65f // no qword remaining
+
+UnalignedQwordLoop:
+ ldq_u t8, 8(t5)
+ extql t10, t5, t10
+ extqh t8, t5, t7
+ bis t7, t10, t10
+ stq t10, 0(a0)
+ bis t8, zero, t10
+ addq t5, 8, t5 // increment source
+ addq a0, 8, a0 // increment dest
+ subq t4, 1, t4 // decrement remaining qwords
+ bne t4, UnalignedQwordLoop
+ cmpult a0, t9, t4
+ beq t4, 64f // no bytes remaining
+
+//
+// Low word of the tail bytes are in t10
+// Get the high part, then go ahead and store them.
+// We know we will not store beyond the containing qword
+// of the end of the buffer.
+//
+65:
+ ldq_u t8, 8(t5) // get high part of tail bytes
+ extql t10, t5, t7 // extract low part
+ extqh t8, t5, t4 // extract high part
+ bis t7, t4, t10 // merge
+ stq t10, 0(a0) // store result
+ bis t9, zero, a0 // increment dest.
+ br zero, 64f
+
+FillBytes:
+ ldq_u t10, 0(t5)
+ ldq_u t8, 0(a0)
+ extbl t10, t5, t7
+ insbl t7, a0, t10
+ mskbl t8, a0, t4
+ bis t10, t4, t7
+ stq_u t7, 0(a0)
+ addq a0, 1, a0
+ addq t5, 1, t5
+ cmpult a0, t9, t4
+ bne t4, FillBytes
+64:
+//
+// Token successfully copied.
+//
+ addq a2, 2, a2
+ subq t2, 1, t2 // decrement remaining bits
+ srl t1, 1, t1 // shift flag byte
+ beq t2, 10b // no more bits remaining
+
+ addq a0, t2, t3 // check for enough output bytes remaining
+ cmpule t3, a1, t4
+ beq t4, CopyTail
+
+ addq a2, 14, t3 // check for enough input bytes remaining
+ cmpule t3, a3, t4
+ beq t4, CopyTail
+
+ addq a2, t2, t3 // point to last byte.
+//
+// Get remaining bytes
+//
+ bis zero, zero, t0 // set # clear bits back to zero
+ ldq_u t5, 0(t3)
+ and a2, 7, t7
+ beq t7, 65f // source quadword aligned, no shift/merge required
+ extql t6, a2, t4
+ extqh t5, a2, t8
+ bis t4, t8, t5
+65:
+ bne t1, 25b // if any literal tokens remain, repeat
+
+ bis zero, 1, t9 // compute byte mask
+ sll t9, t2, t3
+ subq t3, 1, t9
+ zapnot t5, t9, t4 // get masked bytes to store
+ and a0, 7, t3 // get byte position of dest
+ addq t3, t2, t10 // compute ending offset
+ sll t9, t3, t9 // shift byte mask into position
+ ldq_u t7, 0(a0) // get low part of dest.
+ insql t4, t3, t6 // insert source bytes into position
+ zap t7, t9, t8 // clear dest
+ bis t8, t6, t7 // merge bytes to store
+ stq_u t7, 0(a0) // store merged result
+
+//
+// Check to see whether the bytes to store extend into the next
+// quadword of the destination.
+//
+ cmpult t10, 8, t9
+ bne t9, 70f // ending offset < 8, next quadword unaffected
+ insqh t4, t3, t6 // insert source bytes into position
+ stq_u t6, 8(a0) // store merged results
+
+70:
+ addq a0, t2, a0 // adjust destination pointer
+ addq a2, t2, a2 // adjust source pointer
+ addq a5, t2, a5 // adjust bytes copied
+ br zero, 10b
+
+UpdateFormat:
+ subq a0, a5, t10 // compute original pointer
+ subq v0, t10, t7 // compute current max displacement
+ sll t7, 1, t7 //
+ addq t7, t10, v0 // compute new max displacemnt
+ srl t11, 1, t11 // compute new length mask
+ subq t12, 1, t12 // compute new displacement shift
+ br zero, 50b // start again.
+
+//
+// a0 - the destination
+// a1 - the last byte of the destination
+// t1 - flag byte
+//
+CopyTailFlag:
+ ldil t2, 8 // set count of bits to process
+CopyTail:
+ cmpult a0, a1, t10
+ beq t10, SuccessExit // finished
+ cmpule a2, a3, t10
+ beq t10, SuccessExit // finished
+ blbc t1, CT15 // skip copy token
+ cmpeq a2, a3, t10
+ beq t10, CopyToken // more than one byte left
+ br zero, ErrorExit // only one byte left, error
+CT15:
+ ldq_u t10, 0(a2)
+ extbl t10, a2, t5
+ ldq_u t7, 0(a0)
+ insbl t5, a0, t6
+ mskbl t7, a0, t8
+ bis t6, t8, t7
+ stq_u t7, 0(a0)
+ addq a0, 1, a0
+ addq a2, 1, a2
+ srl t1, 1, t1
+ subq t2, 1, t2
+ addq a5, 1, a5
+ bne t2, CopyTail
+ cmpule a2, a3, t10
+ beq t10, SuccessExit // finished
+ ldq_u t0, 0(a2) // load flag byte and any subsequent bytes
+ extbl t0, a2, t1 // extract flag byte
+ addq a2, 1, a2
+ br zero, CopyTailFlag
+
+SuccessExit:
+ bis zero, zero, v0
+ stl a5, 0(a4)
+ ret zero, (ra)
+
+ErrorExit:
+ ldil v0, STATUS_BAD_COMPRESSION_BUFFER
+ ret zero, (ra)
+ .end LZNT1DecompressChunk
diff --git a/private/ntos/rtl/alpha/mvmem.s b/private/ntos/rtl/alpha/mvmem.s
new file mode 100644
index 000000000..c5ccc9a81
--- /dev/null
+++ b/private/ntos/rtl/alpha/mvmem.s
@@ -0,0 +1,1920 @@
+// TITLE("Compare, Move, Zero, and Fill Memory Support")
+//++
+//
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// mvmem.s
+//
+// Abstract:
+//
+// This module implements functions to compare, move, zero, and fill
+// blocks of memory. If the memory is aligned, then these functions
+// are very efficient.
+//
+// N.B. These routines MUST preserve all floating state since they are
+// frequently called from interrupt service routines that normally
+// do not save or restore floating state.
+//
+// Author:
+//
+// Joe Notarangelo 21-May-1992
+//
+// Environment:
+//
+// User or Kernel mode.
+//
+// Revision History:
+//
+// Monty VanderBilt 14-Feb-1996 Avoid memory loads and branch takens between
+// load lock and store conditional instructions
+// to conform with all alpha architecture rules.
+// Monty VanderBilt 27-Feb-1996 Added RtlZeroBytes and RtlFillBytes to support
+// byte granularity access when necessary.
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Compare Memory")
+//++
+//
+// ULONG
+// RtlCompareMemory (
+// IN PVOID Source1,
+// IN PVOID Source2,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function compares two blocks of memory and returns the number
+// of bytes that compared equal.
+//
+// Arguments:
+//
+// Source1 (a0) - Supplies a pointer to the first block of memory to
+// compare.
+//
+// Source2 (a1) - Supplies a pointer to the second block of memory to
+// compare.
+//
+// Length (a2) - Supplies the length, in bytes, of the memory to be
+// compared.
+//
+// Return Value:
+//
+// The number of bytes that compared equal is returned as the function
+// value. If all bytes compared equal, then the length of the orginal
+// block of memory is returned.
+//
+//--
+
+
+ LEAF_ENTRY(RtlCompareMemory)
+
+ bis a2, zero, v0 // save length of comparison
+ beq a2, 90f // (JAE) quit if nothing to compare
+ xor a0, a1, t0 // check for compatible alignment
+ and t0, 0x7, t0 // low bits only
+ bne t0, CompareUnaligned // if ne, incompatible alignment
+
+//
+// Compare memory aligned
+//
+
+CompareAligned: //
+
+//
+// compare memory until sources are aligned
+//
+ and a0, 0x7, t0 // get low bits
+ bne t0, 10f // if ne, sources not aligned yet
+ br zero, 30f // already aligned, predicted
+
+
+10:
+ ldq_u t1, 0(a0) // get unaligned quad at source 1
+ ldq_u t2, 0(a1) // get unaligned quad at source 2
+
+20:
+ extbl t1, t0, t4 // byte at t0 in source 1 quad
+ extbl t2, t0, t5 // byte at t0 in source 2 quad
+ xor t4, t5, t3 // t1 = t2 ?
+ bne t3, 110f // not equal, miscompare
+ subq a2, 1, a2 // decrement bytes to compare
+ beq a2, 90f // if eq, compare success
+ addq t0, 1, t0 // increment pointer within quad
+ cmpeq t0, 8, t3 // t0 = 8?, if so first quadword done
+ beq t3, 20b // continue while t0 < 8
+
+
+ addq a0, 8, a0 // increment to next quadword
+ addq a1, 8, a1 // increment source 2 to next also
+ bic a0, 7, a0 // align source 1 quadword
+ bic a1, 7, a1 // align source 2 quadword
+
+
+//
+// aligned block compare, compare blocks of 64 bytes
+//
+
+30:
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 50f // if eq, no 64 byte blocks
+
+//
+// N.B. loads from each of the sources were separated in case these
+// blocks are fighting for the cache
+//
+ .set noat
+40:
+ ldq t1, 0(a0) // t1 = source 1, quad 0
+ ldq t2, 8(a0) // t2 = source 1, quad 1
+ ldq t3, 16(a0) // t3 = source 1, quad 2
+ addq a1, 64, a1 // increment source 2 pointer
+ ldq t4, 24(a0) // t4 = source 1, quad 3
+
+ ldq t5, -64(a1) // t5 = source 2, quad 0
+ ldq a4, -56(a1) // a4 = source 2, quad 1
+ ldq a5, -48(a1) // a5 = source 2, quad 2
+ xor t1, t5, $at // quad 0 match?
+ bne $at, 200f // if ne[false], miscompare
+ ldq t5, -40(a1) // t5 = source 2, quad 3
+ ldq t1, 32(a0) // t1 = source 1, quad 4
+ xor t2, a4, $at // quad 1 match?
+ bne $at, 122f // if ne[false], miscompare
+ ldq t2, 40(a0) // t2 = source 1, quad 5
+ xor t3, a5, $at // quad 2 match?
+ bne $at, 124f // if ne[false], miscompare
+ ldq t3, 48(a0) // t3 = source 1, quad 6
+ xor t4, t5, $at // quad 3 match?
+ bne $at, 126f // if ne[false], miscompare
+ ldq t4, 56(a0) // t4 = source 1, quad 7
+
+ ldq t5, -32(a1) // t5 = source 2, quad 4
+ addq a0, 64, a0 // increment source 1 pointer
+ ldq a4, -24(a1) // a4 = source 2, quad 5
+ subq t0, 1, t0 // decrement blocks to compare
+ ldq a5, -16(a1) // a5 = source 2, quad 6
+ xor t1, t5, $at // quad 4 match?
+ bne $at, 130f // if ne[false], miscompare
+ ldq t5, -8(a1) // t5 = source 2, quad 7
+ xor t2, a4, $at // quad 5 match?
+ bne $at, 132f // if ne[false], miscompare
+ xor t3, a5, $at // quad 6 match?
+ bne $at, 134f // if ne[false], miscompare
+ xor t4, t5, $at // quad 7 match?
+ bne $at, 136f // if ne[false], miscompare
+ subq a2, 64, a2 // decrement bytes to compare
+ bne t0, 40b // if ne, more blocks to compare
+ .set at
+
+
+//
+// Compare quadwords
+//
+
+50:
+ srl a2, 3, t0 // t0 = number of quadwords to compare
+ beq t0, 70f // if eq, no quadwords to compare
+
+ .set noat
+60:
+ ldq t1, 0(a0) // t1 = quad from source 1
+ lda a0, 8(a0) // increment source 1 pointer
+ ldq t2, 0(a1) // t2 = quad from source 2
+ lda a1, 8(a1) // increment source 2 pointer
+ xor t1, t2, $at // are quadwords equal?
+ bne $at, 200f // if ne, miscompare
+ subq t0, 1, t0 // decrement quads to compare
+ subq a2, 8, a2 // decrement bytes to compare
+ bne t0, 60b // if ne, more quads to compare
+
+ .set at
+
+//
+// Compare bytes in last quadword
+//
+
+// a2 = number of bytes to compare, less than 8, greater than zero
+// a0, a1, quad-aligned to last quadword
+
+ beq a2, 80f // if eq, all bytes compared
+
+ .set noat
+70:
+ ldq t1, 0(a0) // t1 = quad at source 1
+ ldq t2, 0(a1) // t2 = quad at source 2
+ bis zero, 0xff, t0 // zap mask
+ sll t0, a2, t0 //
+ zap t1, t0, t1 // zero bytes not compared
+ zap t2, t0, t2 // same for source 2
+ xor t1, t2, $at // compare quadwords
+ bne $at, 200f // if ne, miscompare
+
+ .set at
+//
+// Successful compare
+// v0 already contains full length
+//
+
+80:
+ ret zero, (ra) // return
+
+
+//
+// Sources have incompatible alignment
+//
+CompareUnaligned:
+
+
+//
+// Compare until source 1 (a0) is aligned
+//
+
+ and a0, 0x7, t0 // get byte position of pointer
+ beq t0, 30f // if eq, already aligned
+
+ ldq_u t1, 0(a0) // get unaligned quad at a0
+
+10:
+ ldq_u t2, 0(a1) // get unaligned quad at a1
+ extbl t1, t0, t4 // get byte to compare from source 1
+ extbl t2, a1, t2 // get byte to compare from source 2
+ xor t4, t2, t3 // do bytes match?
+ bne t3, 110f // if ne, miscompare
+ subq a2, 1, a2 // decrement bytes to compare
+ beq a2, 90f // (JAE) quit if nothing left to compare
+ addq t0, 1, t0 // increment byte within source 1
+ addq a1, 1, a1 // increment source 2 pointer
+ cmpeq t0, 8, t3 // finished with source 1 quad?
+ beq t3, 10b // if eq[false], more to compare
+
+ addq a0, 7, a0 // point to next source 1 quad
+ bic a0, 7, a0 // align to quadword
+
+
+//
+// Compare 64-byte blocks
+//
+
+30:
+ srl a2, 6, t0 // t0 = number of blocks to compare
+ beq t0, 50f // if eq, no blocks to move
+
+ ldq_u t1, 0(a1) // get source 2 unaligned quad 1
+
+ .set noat
+40:
+ ldq_u t2, 7(a1) // get source 2 unaligned quad 2
+ addq a0, 64, a0 // increment source 1 pointer
+ ldq_u t3, 15(a1) // get source 2 unaligned quad 3
+ extql t1, a1, t1 // bytes from unaligned quad 1
+ extqh t2, a1, $at // bytes from unaligned quad 2
+ ldq_u t4, 23(a1) // get source 2 unaligned quad 4
+ bis t1, $at, t1 // t1 = quadword 1 (source 2)
+ ldq_u t5, 31(a1) // get source 2 unaligned quad 5
+ extql t2, a1, t2 // bytes from unaligned quad 2
+ extqh t3, a1, $at // bytes from unaligned quad 3
+ ldq a3, -64(a0) // a3 = quadword 1 (source 1)
+ bis t2, $at, t2 // t2 = quadword 2 (source 2)
+ ldq a4, -56(a0) // a4 = quadword 2 (source 1)
+ extql t3, a1, t3 // bytes from unaligned quad 3
+ extqh t4, a1, $at // bytes from unaligned quad 4
+ ldq a5, -48(a0) // a5 = quadword 3 (source 1)
+ bis t3, $at, t3 // t3 = quadword 3 (source 2)
+ extql t4, a1, t4 // bytes from unaligned quad 4
+ extqh t5, a1, $at // bytes from unaligned quad 5
+ subq t0, 1, t0 // decrement blocks to compare
+ bis t4, $at, t4 // t4 = quadword 4 (source 2)
+
+ xor t1, a3, $at // match on quadword 1?
+ ldq a3, -40(a0) // a3 = quadword 4 (source 1)
+ bne $at, 200f // if ne, miscompare quad 1
+ xor t2, a4, $at // match on quadword 2?
+ ldq_u t2, 39(a1) // get source 2 unaligned quad 6
+ bne $at, 122f // if ne, miscompare quad 2
+ xor t3, a5, $at // match on quadword 3?
+ ldq_u t3, 47(a1) // get source 2 unaligned quad 7
+ bne $at, 124f // if ne, miscompare quad 3
+ xor t4, a3, $at // match on quadword 4?
+ ldq_u t4, 55(a1) // get source 2 unaligned quad 8
+ bne $at, 126f // if ne, miscompare quad 4
+ ldq_u t1, 63(a1) // get source 2 unaligned quad 9
+
+ ldq a3, -32(a0) // a3 = quadword 5 (source 1)
+ extql t5, a1, t5 // bytes from unaligned quad 5
+ extqh t2, a1, $at // bytes from unaligned quad 6
+ ldq a4, -24(a0) // a4 = quadword 6 (source 1)
+ ldq a5, -16(a0) // a5 = quadword 7 (source 1)
+ bis t5, $at, t5 // t5 = quadword 5 (source 2)
+
+ xor t5, a3, $at // match on quadword 5?
+ ldq a3, -8(a0) // a3 = quadword 8 (source 1)
+ bne $at, 130f // if ne, miscompare quad 5
+ extql t2, a1, t2 // bytes from unaligned quad 6
+ extqh t3, a1, $at // bytes from unaligned quad 7
+ extql t3, a1, t3 // bytes from unaligned quad 7
+ bis t2, $at, t2 // t2 = quadword 6 (source 2)
+ xor t2, a4, $at // match on quadword 6?
+ bne $at, 132f // if ne, miscompare quad 6
+ extqh t4, a1, $at // bytes from unaligned quad 8
+ extql t4, a1, t4 // bytes from unaligned quad 8
+ bis t3, $at, t3 // t3 = quadword 7 (source 2)
+ xor t3, a5, $at // match on quadword 7?
+ bne $at, 134f // if ne, miscompare quad 7
+ extqh t1, a1, $at // bytes from unaligned quad 9
+ addq a1, 64, a1 // increment source 2 pointer
+ bis t4, $at, t4 // t4 = quadword 8 (source 2)
+ xor t4, a3, $at // match on quadword 8?
+ bne $at, 136f // if ne, miscompare quad 8
+ subq a2, 64, a2 // decrement number of bytes to compare
+ bne t0, 40b // if ne, more blocks to compare
+
+ .set at
+
+//
+// Compare quadwords
+//
+
+
+50:
+ srl a2, 3, t0 // t0 = number of quads to compare
+ beq t0, 70f // if eq, no quads to compare
+ ldq_u t1, 0(a1) // get unaligned quad 1 (source 2)
+
+ .set noat
+60:
+ ldq_u t2, 7(a1) // get unaligned quad 2 (source 2)
+ ldq t3, 0(a0) // t3 = quadword 1 (source 1)
+ extql t1, a1, t1 // get bytes from unaligned quad 1
+ extqh t2, a1, $at // get bytes from unaligned quad 2
+ addq a1, 8, a1 // increment source 2 pointer
+ bis t1, $at, t1 // t1 = quadword 1 (source 2)
+ xor t1, t3, $at // match on quadword?
+ bne $at, 200f // if ne, miscompare
+ subq t0, 1, t0 // decrement quadwords to compare
+ addq a0, 8, a0 // increment source 1 pointer
+ subq a2, 8, a2 // decrement bytes to compare
+ bis t2, zero, t1 // save low quadword for next loop
+ bne t0, 60b // if ne, more quads to compare
+
+ .set at
+
+//
+// Compare bytes for final quadword
+//
+
+70:
+ beq a2, 90f // if eq, comparison complete
+
+ ldq t1, 0(a0) // get quadword from source 1
+ bis zero, zero, t0 // t0 = byte position to compare
+
+ .set noat
+80:
+ ldq_u t2, 0(a1) // get unaligned quad from source 2
+ extbl t1, t0, t3 // t3 = byte from source 1
+ extbl t2, a1, t2 // t2 = byte from source 2
+ xor t3, t2, $at // match on byte?
+ bne $at, 100f // if ne, miscompare on byte
+ addq t0, 1, t0 // increment byte position
+ addq a1, 1, a1 // increment source 2 pointer
+ subq a2, 1, a2 // decrement bytes to compare
+ bne a2, 80b // if ne, more bytes to compare
+
+ .set at
+//
+// Successful full comparison
+//
+
+90:
+ ret zero, (ra) // return, v0 already set
+
+
+//
+// Miscompare on last quadword
+//
+
+100:
+ subq v0, a2, v0 // subtract bytes not compared
+ ret zero, (ra) // return
+
+//
+// Miscompare on first quadword, unaligned case
+//
+// v0 = total bytes to compare
+// a2 = bytes remaining to compare
+//
+
+110:
+ subq v0, a2, v0 // bytes compared successfully
+ ret zero, (ra) // return
+
+//
+// Miscompare on 64-byte block compare
+//
+
+122:
+ subq a2, 8, a2 // miscompare on quad 2
+ br zero, 200f // finish in common code
+
+124:
+ subq a2, 16, a2 // miscompare on quad 3
+ br zero, 200f // finish in common code
+
+126:
+ subq a2, 24, a2 // miscompare on quad 4
+ br zero, 200f // finish in common code
+
+130:
+ subq a2, 32, a2 // miscompare on quad 5
+ br zero, 200f // finish in common code
+
+132:
+ subq a2, 40, a2 // miscompare on quad 6
+ br zero, 200f // finish in common code
+
+134:
+ subq a2, 48, a2 // miscompare on quad 7
+ br zero, 200f // finish in common code
+
+136:
+ subq a2, 56, a2 // miscompare on quad 8
+ br zero, 200f // finish in common code
+
+//
+// Miscompare, determine number of bytes that successfully compared
+// $at = xor of relevant quads from sources, must be non-zero
+// a2 = number of bytes left to compare
+//
+ .set noat
+200:
+ cmpbge zero, $at, $at // $at = mask of non-zero bytes
+
+ //
+ // look for the first bit cleared in $at, this is the
+ // number of the first byte which differed
+ //
+ bis zero, zero, t0 // bit position to look for clear
+
+210:
+ blbc $at, 220f // if low clear, found difference
+ srl $at, 1, $at // check next bit
+ addq t0, 1, t0 // count bit position checked
+ br zero, 210b
+
+220:
+ subq v0, a2, v0 // subtract bytes yet to compare
+ addq v0, t0, v0 // add bytes that matched on last quad
+
+ ret zero, (ra)
+
+ .set at
+
+ .end RtlCompareMemory
+
+
+
+ SBTTL("Move Memory")
+//++
+//
+// VOID
+// RtlMoveMemory (
+// IN PVOID Destination,
+// IN PVOID Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function moves memory either forward or backward, aligned or
+// unaligned, in 64-byte blocks, followed by 8-byte blocks, followed
+// by any remaining bytes.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the destination address of
+// the move operation.
+//
+// Source (a1) - Supplies a pointer to the source address of the move
+// operation.
+//
+// Length (a2) - Supplies the length, in bytes, of the memory to be moved.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlMoveMemory)
+
+ beq a2, 80f // if eq, no bytes to move
+//
+// If the source address is less than the destination address and source
+// address plus the length of the move is greater than the destination
+// address, then the source and destination overlap such that the move
+// must be performed backwards.
+//
+
+ cmpult a0, a1, t0 // is destination less than source
+ bne t0, MoveForward // if eq [true] no overlap possible
+ addq a1, a2, t0 // compute source ending address
+ cmpult t0, a0, t1 // is source end less than dest.
+ beq t1, MoveBackward // if eq [false], overlap
+
+//
+// Move memory forward aligned and unaligned.
+//
+
+MoveForward: //
+ xor a0, a1, t0 // compare alignment bits
+ and t0, 0x7, t0 // isloate alignment comparison
+ bne t0, MoveForwardUnaligned // if ne, incompatible alignment
+
+//
+// Move memory forward aligned.
+//
+
+MoveForwardAligned: //
+
+//
+// Move bytes until source and destination are quadword aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, not quad aligned
+ br zero, 20f // predicted taken
+
+5:
+ ldq_u t2, 0(a0) // get unaligned quad from dest.
+ ldq_u t1, 0(a1) // get unaligned quadword from source
+10:
+ beq a2, 15f // if eq, all bytes moved
+ extbl t1, t0, t3 // t3 = byte from source
+ insbl t3, t0, t3 // t3 = byte from source, in position
+ mskbl t2, t0, t2 // clear position in dest. quad
+ bis t2, t3, t2 // merge in byte from source
+ subq a2, 1, a2 // decrement bytes to move
+ addq t0, 1, t0 // increment byte within quad
+ cmpeq t0, 8, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+15:
+ stq_u t2, 0(a0) // store merged destination bytes
+
+ addq a0, 7, a0 // move to next quadword
+ bic a0, 7, a0 // aligned quadword
+
+ addq a1, 7, a1 // move to next quadword
+ bic a1, 7, a1 // aligned quadword
+
+//
+// Check for 64-byte block moves
+//
+
+20:
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes
+
+30:
+ ldq t1, 0(a1) // load 64 bytes from source
+ addq a0, 64, a0 // increment destination pointer
+ ldq v0, 56(a1) //
+ ldq a3, 32(a1) //
+ stq t1, -64(a0) // write to destination
+ ldq t2, 8(a1) // into volatile registers
+ ldq t3, 16(a1) //
+ ldq t4, 24(a1) //
+ subq t0, 1, t0 // decrement number of blocks
+ stq t2, -56(a0) //
+ ldq a4, 40(a1) //
+ stq t3, -48(a0) //
+ ldq a5, 48(a1) //
+ stq t4, -40(a0) //
+ addq a1, 64, a1 // increment source pointer
+ stq a3, -32(a0) //
+ stq a4, -24(a0) //
+ stq a5, -16(a0) //
+ stq v0, -8(a0) //
+ bne t0, 30b // if ne, more blocks to copy
+
+//
+// Copy quadwords
+//
+
+40:
+ srl a2, 3, t0 // t0 = number of quadwords to move
+ beq t0, 60f // if eq no quadwords to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+50:
+ ldq t1, 0(a1) // load quadword from source
+ addq a1, 8, a1 // increment source pointer
+ stq t1, 0(a0) // store quadword to destination
+ addq a0, 8, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of quadwords
+ bne t0, 50b // if ne, more quadwords to move
+
+//
+// Move final residual bytes
+//
+
+60:
+ beq a2, 80f // if eq, no more bytes to move
+ ldq t1, 0(a1) // get last source quadword
+ ldq t2, 0(a0) // get last dest. quadword
+ bis zero, zero, t0 // t0 = next byte number to move
+
+70:
+ extbl t1, t0, t3 // extract byte from source
+ insbl t3, t0, t3 // t3 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position for dest.
+ bis t2, t3, t2 // merge in source byte
+ addq t0, 1, t0 // increment byte position
+ subq a2, 1, a2 // decrement bytes to move
+ bne a2, 70b // if ne => more bytes to move
+
+ stq t2, 0(a0) // store merged data
+
+//
+// Finish aligned MoveForward
+//
+
+80:
+ ret zero, (ra) // return
+
+
+
+//
+// Move memory forward unaligned.
+//
+
+MoveForwardUnaligned: //
+
+
+//
+// Move bytes until the destination is aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ beq t0, 100f // if eq, destination quad aligned
+
+ ldq_u t2, 0(a0) // get unaligned quad from dest
+
+90:
+ beq a2, 95f // if eq no more bytes to move
+ ldq_u t1, 0(a1) // get unaligned quad from source
+ extbl t1, a1, t1 // extract source byte
+ insbl t1, t0, t1 // t1 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position in dest.
+ bis t2, t1, t2 // merge in source byte
+ addq t0, 1, t0 // increment byte position
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ cmpeq t0, 8, t3 // t0 = 8? => quad finished
+ beq t3, 90b // if eq [false], more bytes to move
+95:
+ stq_u t2, 0(a0) // store merged quadword
+ addq a0, 7, a0 // increment to next quad
+ bic a0, 7, a0 // align next quadword
+
+//
+// Check for 64-byte blocks to move
+//
+
+100:
+ srl a2, 6, t0 // t0 = number of blocks to move
+ beq t0, 120f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes to move
+
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+
+110:
+ // get source data and merge it
+ // as we go
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quad #1
+ ldq_u t3, 15(a1) // t3 = third unaligned quad
+ extql t2, a1, t2 // extract applicable bytes from t2
+ extqh t3, a1, v0 // extract applicable bytes from t3
+ stq t1, 0(a0) // store quad #1
+ bis t2, v0, t2 // t2 = quad #2
+ ldq_u t4, 23(a1) // t4 = fourth unaligned quad
+ extql t3, a1, t3 // extract applicable bytes from t3
+ extqh t4, a1, v0 // extract applicable bytes from t4
+ stq t2, 8(a0) // store quad #2
+ bis t3, v0, t3 // t3 = quad #3
+ ldq_u t5, 31(a1) // t5 = fifth unaligned quad
+ extql t4, a1, t4 // extract applicable bytes from t4
+ extqh t5, a1, v0 // extract applicable bytes from t5
+ stq t3, 16(a0) // store quad #3
+ bis t4, v0, t4 // t4 = quad #4
+ ldq_u a3, 39(a1) // a3 = sixth unaligned quad
+ extql t5, a1, t5 // extract applicable bytes from t5
+ extqh a3, a1, v0 // extract applicable bytes from a3
+ stq t4, 24(a0) // store quad #4
+ bis t5, v0, t5 // t5 = quad #5
+ ldq_u a4, 47(a1) // a4 = seventh unaligned quad
+ extql a3, a1, a3 // extract applicable bytes from a3
+ extqh a4, a1, v0 // extract applicable bytes from a4
+ stq t5, 32(a0) // store quad #5
+ bis a3, v0, a3 // a3 = quad #6
+ ldq_u a5, 55(a1) // a5 = eighth unaligned quad
+ extql a4, a1, a4 // extract applicable bytes from a4
+ extqh a5, a1, v0 // extract applicable bytes from a5
+ stq a3, 40(a0) // store quad #6
+ bis a4, v0, a4 // a4 = quad #7
+ ldq_u t1, 63(a1) // t1 = ninth unaligned = 1st of next
+ extql a5, a1, a5 // extract applicable bytes from a5
+ extqh t1, a1, v0 // extract applicable bytes from t1
+ stq a4, 48(a0) // store quad #7
+ bis a5, v0, a5 // a5 = quad #8
+ addq a1, 64, a1 // increment source pointer
+ stq a5, 56(a0) // store quad #8
+ addq a0, 64, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of blocks
+ bne t0, 110b // if ne, more blocks to move
+
+//
+// Move unaligned source quads to aligned destination quads
+//
+
+120:
+ srl a2, 3, t0 // t0 = number of quads to move
+ beq t0, 140f // if eq no quads to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+130:
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ addq a0, 8, a0 // increment destination pointer
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quadword of data
+ stq t1, -8(a0) // store data to destination
+ addq a1, 8, a1 // increment source pointer
+ subq t0, 1, t0 // decrement quads to move
+ bis t2, zero, t1 // t1 = first of next unaligned pair
+ bne t0, 130b // if ne, more quads to move
+
+//
+// Move remaining bytes to final quadword
+//
+
+
+140:
+ beq a2, 160f // if eq no more bytes to move
+ ldq t2, 0(a0) // t2 = destination quadword
+ bis zero, zero, t3 // t3 = position for next insertion
+
+150:
+ ldq_u t1, 0(a1) // get unaligned source quad
+ extbl t1, a1, t1 // t1 = source byte
+ insbl t1, t3, t1 // t1 = source byte, in position
+ mskbl t2, t3, t2 // clear byte in destination
+ bis t2, t1, t2 // merge in source byte
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ addq t3, 1, t3 // increment destination position
+ bne a2, 150b // more bytes to move
+
+ stq t2, 0(a0) // store merged data
+
+//
+// Finish unaligned MoveForward
+//
+
+160:
+ ret zero, (ra) // return
+
+
+//
+// Move memory backward.
+//
+
+MoveBackward: //
+
+ addq a0, a2, a0 // compute ending destination address
+ addq a1, a2, a1 // compute ending source address
+ subq a0, 1, a0 // point to last destination byte
+ subq a1, 1, a1 // point to last source byte
+ xor a0, a1, t0 // compare alignment bits
+ and t0, 0x7, t0 // isolate alignment comparison
+ bne t0, MoveBackwardUnaligned // if ne, incompatible alignment
+
+//
+// Move memory backward aligned.
+//
+
+MoveBackwardAligned: //
+
+//
+// Move bytes until source and destination are quadword aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ cmpeq t0, 7, t1 // last byte position 7?
+ beq t1, 5f // if eq [false], not quad aligned
+ subq a0, 7, a0 // point to beginning of last quad
+ subq a1, 7, a1 // point to beginning of last quad
+ br zero, 30f // predicted taken
+
+5:
+ ldq_u t1, 0(a0) // get unaligned quad from dest.
+ ldq_u t2, 0(a1) // get unaligned quad from source
+
+10:
+ beq a2, 20f // if eq, all bytes moved
+ extbl t2, t0, t3 // t3 = byte from source
+ insbl t3, t0, t3 // t3 = byte from source, in position
+ mskbl t1, t0, t1 // clear position in destination
+ bis t1, t3, t1 // merge in byte from source
+ subq a2, 1, a2 // decrement bytes to move
+ subq t0, 1, t0 // decrement byte within quadword
+ cmplt t0, zero, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+
+20:
+ stq_u t1, 0(a0) // store merged destination bytes
+
+ subq a0, 8, a0 // move to previous quadword
+ bic a0, 7, a0 // aligned quadword
+
+ subq a1, 8, a1 // move to previous quadword
+ bic a1, 7, a1 // aligned quadword
+
+//
+// Check for 64-byte block moves
+//
+
+30:
+
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 50f // if eq, no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes
+
+40:
+ ldq t1, 0(a1) // load 64 bytes from source into
+ subq a0, 64, a0 // decrement destination pointer
+ ldq v0, -56(a1) //
+ ldq a3, -32(a1) //
+ stq t1, 64(a0) // write to destination
+ ldq t2, -8(a1) // into volatile registers
+ ldq a5, -48(a1) //
+ ldq a4, -40(a1) //
+ stq t2, 56(a0) //
+ ldq t3, -16(a1) //
+ ldq t4, -24(a1) //
+ subq a1, 64, a1 // decrement source pointer
+ stq t3, 48(a0) //
+ stq t4, 40(a0) //
+ stq a3, 32(a0) //
+ subq t0, 1, t0 // decrement number of blocks
+ stq a4, 24(a0) //
+ stq a5, 16(a0) //
+ stq v0, 8(a0) //
+ bne t0, 40b // if ne, more blocks to copy
+
+//
+// Copy quadwords
+//
+
+50:
+ srl a2, 3, t0 // t0 = number of quadwords to move
+ beq t0, 70f // if eq no quadwords to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+60:
+ ldq t1, 0(a1) // load quadword from source
+ subq a1, 8, a1 // decrement source pointer
+ stq t1, 0(a0) // store quadword to destination
+ subq a0, 8, a0 // decrement destination pointer
+ subq t0, 1, t0 // decrement quadwords to move
+ bne t0, 60b // if ne, more quadwords to move
+
+//
+// Move final residual bytes
+//
+
+70:
+ beq a2, 90f // if eq, no more bytes to move
+ ldq t1, 0(a1) // get last source quadword
+ ldq t2, 0(a0) // get last destination quadword
+ bis zero, 7, t0 // t0 = next byte number to move
+
+80:
+ extbl t1, t0, t3 // extract byte from source
+ insbl t3, t0, t3 // t3 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position for dest.
+ bis t2, t3, t2 // merge in source byte
+ subq t0, 1, t0 // decrement byte position
+ subq a2, 1, a2 // decrement bytes to move
+ bne a2, 80b // if ne, more bytes to move
+
+ stq t2, 0(a0) // write destination data
+//
+// Finish aligned MoveBackward
+//
+
+90:
+
+ ret zero, (ra) // return
+
+
+//
+// Move memory backward unaligned.
+//
+
+MoveBackwardUnaligned: //
+
+
+//
+// Move bytes until the destination is aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ cmpeq t0, 7, t1 // last byte of a quadword
+ beq t1, 95f // if eq[false], not aligned
+ subq a0, 7, a0 // align pointer to beginning of quad
+ br zero, 120f //
+
+95:
+ ldq_u t2, 0(a0) // get unaligned quad from dest.
+
+100:
+ beq a2, 110f // if eq, no more bytes to move
+ ldq_u t1, 0(a1) // get unaligned quad from source
+ extbl t1, a1, t1 // extract source byte
+ insbl t1, t0, t1 // t1 = source byte in position
+ mskbl t2, t0, t2 // clear byte position in dest.
+ bis t2, t1, t2 // merge source byte
+ subq t0, 1, t0 // decrement byte position
+ subq a1, 1, a1 // decrement source pointer
+ subq a2, 1, a2 // decrement number of bytes to move
+ cmplt t0, zero, t3 // t0 < 0? => quad finished
+ beq t3, 100b // if eq [false], more bytes to move
+
+110:
+ stq_u t2, 0(a0) // store merged quadword
+
+ subq a0, 8, a0 // decrement dest. to previous quad
+ bic a0, 7, a0 // align previous quadword
+
+//
+// Check for 64-byte blocks to move
+//
+
+120:
+
+ srl a2, 6, t0 // t0 = number of blocks to move
+ subq a1, 7, a1 // point to beginning of last quad
+ beq t0, 140f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes to move
+
+ ldq_u t1, 7(a1) // t1 = first unaligned quad
+
+130:
+ // get source data and merge it
+ // as we go
+ ldq_u t2, 0(a1) // t2 = second unaligned quad
+ extqh t1, a1, t1 // extract applicable bytes from t1
+ extql t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quad #1
+ ldq_u t3, -8(a1) // t3 = third unaligned quad
+ extqh t2, a1, t2 // extract applicable bytes from t2
+ extql t3, a1, v0 // extract applicable bytes from t3
+ stq t1, 0(a0) // store quad #1
+ bis t2, v0, t2 // t2 = quad #2
+ ldq_u t4, -16(a1) // t4 = fourth unaligned quad
+ extqh t3, a1, t3 // extract applicable bytes from t3
+ extql t4, a1, v0 // extract applicable bytes from t4
+ stq t2, -8(a0) // store quad #2
+ bis t3, v0, t3 // t3 = quad #3
+ ldq_u t5, -24(a1) // t5 = fifth unaligned quad
+ extqh t4, a1, t4 // extract applicable bytes from t4
+ extql t5, a1, v0 // extract applicable bytes from t5
+ stq t3, -16(a0) // store quad #3
+ bis t4, v0, t4 // t4 = quad #4
+ ldq_u a3, -32(a1) // a3 = sixth unaligned quad
+ extqh t5, a1, t5 // extract applicable bytes from t5
+ extql a3, a1, v0 // extract applicable bytes from a3
+ stq t4, -24(a0) // store quad #4
+ bis t5, v0, t5 // t5 = quad #5
+ ldq_u a4, -40(a1) // a4 = seventh unaligned quad
+ extqh a3, a1, a3 // extract applicable bytes from a3
+ extql a4, a1, v0 // extract applicable bytes from a4
+ stq t5, -32(a0) // store quad #5
+ bis a3, v0, a3 // a3 = quad #6
+ ldq_u a5, -48(a1) // a5 = eighth unaligned quad
+ extqh a4, a1, a4 // extract applicable bytes from a4
+ extql a5, a1, v0 // extract applicable bytes from a5
+ stq a3, -40(a0) // store quad #6
+ bis a4, v0, a4 // a4 = quad #7
+ ldq_u t1, -56(a1) // t1 = ninth unaligned = 1st of next
+ extqh a5, a1, a5 // extract applicable bytes from a5
+ extql t1, a1, v0 // extract applicable bytes from t1
+ stq a4, -48(a0) // store quad #7
+ bis a5, v0, a5 // a5 = quad #8
+ subq a1, 64, a1 // increment source pointer
+ stq a5, -56(a0) // store quad #8
+ subq a0, 64, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of blocks
+ bne t0, 130b // if ne, more blocks to move
+
+
+//
+// Move unaligned source quads to aligned destination quads
+//
+
+140:
+ srl a2, 3, t0 // t0 = number of quads to move
+ beq t0, 160f // if eq no quads to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+ ldq_u t1, 7(a1) // t1 = first unaligned quad
+
+150:
+ ldq_u t2, 0(a1) // t2 = second unaligned quad
+ subq a0, 8, a0 // decrement destination pointer
+ extqh t1, a1, t1 // extract applicable bytes from t1
+ extql t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quadword of data
+ stq t1, 8(a0) // store data to destination
+ subq a1, 8, a1 // decrement source pointer
+ subq t0, 1, t0 // decrement quads to move
+ bis t2, zero, t1 // t1 = first of next unaligned pair
+ bne t0, 150b // if ne, more quads to move
+
+//
+// Move remaining bytes to final quadword
+//
+
+160:
+ beq a2, 180f // if eq, no more bytes to move
+ ldq t2, 0(a0) // t2 = destination quadword
+ bis zero, 7, t0 // t0 = position for next insertion
+
+170:
+ subq a1, 1, a1 // decrement source pointer
+ ldq_u t1, 8(a1) // get unaligned source quad
+ extbl t1, a1, t1 // t1 = source byte
+ insbl t1, t0, t1 // t1 = source byte, in position
+ mskbl t2, t0, t2 // clear byte position
+ bis t2, t1, t2 // merge in source byte
+ subq t0, 1, t0 // decrement byte position for dest.
+ subq a2, 1, a2 // decrement bytes to move
+ bne a2, 170b // if ne, more bytes to move
+
+ stq t2, 0(a0) //
+
+//
+// Finish unaligned MoveBackward
+//
+
+180:
+ ret zero, (ra) // return
+
+ .end RtlMoveMemory
+
+ SBTTL("Zero Memory")
+//++
+//
+// VOID
+// RtlZeroMemory (
+// IN PVOID Destination,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function zeros memory by first aligning the destination address to
+// a quadword boundary, and then zeroing 64-byte blocks, followed by 8-byte
+// blocks, followed by any remaining bytes.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to zero.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be zeroed.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlZeroMemory)
+
+ bis zero, zero, a2 // set fill pattern
+ br zero, RtlpFillMemory //
+
+
+ SBTTL("Fill Memory")
+//++
+//
+// VOID
+// RtlFillMemory (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN UCHAR Fill
+// )
+//
+// Routine Description:
+//
+// This function fills memory by first aligning the destination address to
+// a longword boundary, and then filling 32-byte blocks, followed by 4-byte
+// blocks, followed by any remaining bytes.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to fill.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be filled.
+//
+// Fill (a2) - Supplies the fill byte.
+//
+// N.B. The alternate entry memset expects the length and fill arguments
+// to be reversed. It also returns the Destination pointer
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(memset)
+
+ bis a0, zero, v0 // set return value
+ bis a1, zero, a3 // swap length and fill arguments
+ bis a2, zero, a1 //
+ bis a3, zero, a2 //
+
+ ALTERNATE_ENTRY(RtlFillMemory)
+
+ and a2, 0xff, a2 // clear excess bits
+ sll a2, 8, t0 // duplicate fill byte
+ bis a2, t0, a2 // generate fill word
+ sll a2, 16, t0 // duplicate fill word
+ bis a2, t0, a2 // generate fill longword
+ sll a2, 32, t0 // duplicate fill longword
+ bis a2, t0, a2 // generate fill quadword
+
+.align 3 // ensure quadword aligned target
+//
+// Fill memory with the pattern specified in register a2.
+//
+
+RtlpFillMemory: //
+
+//
+// Align destination to quadword
+//
+
+ beq a1, 80f // anything to fill? (paranoia)
+ and a0, 8-1, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, then not quad aligned
+ br zero, 20f // if eq, then quad aligned
+
+5:
+ ldq_u t1, 0(a0) // get unaligned quadword
+ // for first group of bytes
+10:
+ beq a1, 15f // if eq no more bytes to fill
+ insbl a2, t0, t2 // get fill byte into position
+ mskbl t1, t0, t1 // clear byte for fill
+ bis t1, t2, t1 // put in fill byte
+ addq t0, 1, t0 // increment to next byte position
+ subq a1, 1, a1 // decrement bytes to fill
+ cmpeq t0, 8, t2 // t0 = 8?
+ beq t2, 10b // if eq [false] more bytes to do
+
+15:
+ stq_u t1, 0(a0) // store modified bytes
+ addq a0, 7, a0 // move a0 to next quadword
+ bic a0, 7, a0 // align a0 to quadword
+
+//
+// Check for 64-byte blocks
+//
+
+20:
+ srl a1, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq then no 64 byte blocks
+ and a1, 64-1, a1 // a1 = residual bytes to fill
+
+30:
+ stq a2, 0(a0) // store 64 bytes
+ stq a2, 8(a0) //
+ stq a2, 16(a0) //
+ stq a2, 24(a0) //
+ stq a2, 32(a0) //
+ stq a2, 40(a0) //
+ stq a2, 48(a0) //
+ stq a2, 56(a0) //
+
+ subq t0, 1, t0 // decrement blocks remaining
+ addq a0, 64, a0 // increment destination pointer
+ bne t0, 30b // more blocks to write
+
+
+
+//
+// Fill aligned quadwords
+//
+
+40:
+ srl a1, 3, t0 // t0 = number of quadwords
+ bne t0, 55f // if ne quadwords left to fill
+ br zero, 60f // if eq no quadwords left
+
+55:
+ and a1, 8-1, a1 // a1 = residual bytes to fill
+
+50:
+ stq a2, 0(a0) // store quadword
+ subq t0, 1, t0 // decrement quadwords remaining
+ addq a0, 8, a0 // next quadword
+ bne t0, 50b // more quadwords to write
+
+
+//
+// Fill bytes for last quadword
+//
+
+60:
+ bne a1, 65f // if ne bytes remain to be filled
+ br zero, 80f // if eq no more bytes to fill
+
+65:
+ ldq t1, 0(a0) // get last quadword
+ bis zero, zero, t0 // t0 = byte position to start fill
+
+70:
+ beq a1, 75f // if eq, no more bytes to fill
+ insbl a2, t0, t2 // get fill byte into position
+ mskbl t1, t0, t1 // clear fill byte position
+ bis t1, t2, t1 // insert fill byte
+ addq t0, 1, t0 // increment byte within quad
+ subq a1, 1, a1 // decrement bytes to fill
+ cmpeq t0, 8, t3 // t0 = 8? => finished quad
+ beq t3, 70b // if eq [false] more bytes to fill
+
+75:
+ stq t1, 0(a0) // write merged quadword
+
+//
+// Finish up
+//
+
+80:
+ ret zero, (ra) // return
+
+
+ .end RtlZeroMemory
+
+ SBTTL("Fill Memory Ulong")
+//++
+//
+// VOID
+// RtlFillMemoryUlong (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN ULONG Pattern
+// )
+//
+// Routine Description:
+//
+// This function fills memory with the specified longowrd pattern by
+// filling 64-byte blocks followed by 8-byte blocks and finally
+// 4-byte blocks.
+//
+// N.B. This routine assumes that the destination address is aligned
+// on a longword boundary and that the length is an even multiple
+// of longwords.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to fill.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be filled.
+//
+// Pattern (a2) - Supplies the fill pattern.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlFillMemoryUlong)
+
+ bic a1, 3, a1 // make sure length is an even number
+ // of longwords
+ sll a2, 32, a3 // a3 = long pattern in upper 32 bits
+ srl a3, 32, t0 // clear upper bits, pattern in lower 32
+ bis a3, t0, a3 // a3 = quad version of fill pattern
+
+//
+// Make destination address quad-aligned
+//
+
+ and a0, 4, t0 // is a0 quad aligned?
+ beq t0, 10f // if eq, then a0 quad aligned
+ stl a2, 0(a0) // fill first longword
+ addq a0, 4, a0 // quad align a0
+ subq a1, 4, a1 // bytes remaining to store
+
+//
+// Check for 64-byte blocks to fill
+//
+
+10:
+ srl a1, 6, t0 // t0 = # 64-byte blocks to fill
+ beq t0, 30f // if eq no 64 byte blocks
+ and a1, 64-1, a1 // a1 = residual bytes
+
+20:
+ stq a3, 0(a0) // store 64 bytes
+ stq a3, 8(a0) //
+ stq a3, 16(a0) //
+ stq a3, 24(a0) //
+ stq a3, 32(a0) //
+ stq a3, 40(a0) //
+ stq a3, 48(a0) //
+ stq a3, 56(a0) //
+ subq t0, 1, t0 // t0 = blocks remaining
+ addq a0, 64, a0 // increment address pointer
+ bne t0, 20b // if ne more blocks to fill
+
+//
+// Fill 8 bytes at a time while we can, a1 = bytes remaining
+//
+
+30:
+ srl a1, 3, t0 // t0 = # quadwords to fill
+ beq t0, 50f // if eq no quadwords left
+ and a1, 8-1, a1 // a1 = residual bytes
+40:
+ stq a3, 0(a0) // store quadword
+ subq t0, 1, t0 // t0 = quadwords remaining
+ addq a0, 8, a0 // increment address pointer
+ bne t0, 40b // if ne more quadwords to fill
+
+//
+// Fill last 4 bytes
+//
+
+50:
+ beq a1, 60f // if eq no longwords remain
+ stl a2, 0(a0) // fill last longword
+
+//
+// Finish up
+//
+
+60:
+ ret zero, (ra) // return to caller
+
+
+ .end RtlFillMemoryUlong
+
+ SBTTL("Copy Memory With Byte Granularity")
+//++
+//
+// VOID
+// RtlCopyBytes (
+// IN PVOID Destination,
+// IN PVOID Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function copies non-overlapping memory, aligned or unaligned, in
+// 64-byte blocks, followed by 8-byte blocks, followed by any remaining
+// bytes. Unlike RtlCopyMemory or RtlMoveMemory the copy is done such
+// that byte granularity is assured for all platforms.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the destination address of
+// the move operation.
+//
+// Source (a1) - Supplies a pointer to the source address of the move
+// operation.
+//
+// Length (a2) - Supplies the length, in bytes, of the memory to be moved.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlCopyBytes)
+
+//
+// Move memory forward aligned and unaligned.
+//
+
+ xor a0, a1, t0 // compare alignment bits
+ and t0, 0x7, t0 // isolate alignment comparison
+ bne t0, CopyForwardUnaligned // if ne, incompatible alignment
+
+//
+// Source and Destination buffers have the same alignment. Move
+// bytes until done or source and destination are quadword aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, not quad aligned
+ br zero, 20f // predicted taken
+5:
+ bis zero, zero, t1 // t4 = destination byte zap mask
+ bis zero, 1, t2
+ sll t2, t0, t2 // t2 = next bit to set in zap mask
+10:
+ beq a2, 15f // if eq, all bits set
+ bis t1, t2, t1 // set bit in zap mask
+ sll t2, 1, t2 // set next higher bit for zap mask
+ subq a2, 1, a2 // decrement bytes to move
+ addq t0, 1, t0 // increment byte within quad
+ cmpeq t0, 8, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+15:
+ ldq_u t2, 0(a1) // get unaligned quadword from source
+ zapnot t2, t1, t2 // clear source bytes
+ bic a0, 7, a3 // a3 = quadword base of destination
+retry1:
+ ldq_l t0, 0(a3) // load destination quadword
+ zap t0, t1, t0 // clear destination bytes
+ or t0, t2, t0 // merge in bytes from source
+ stq_c t0, 0(a3) // store merged quadword conditional
+ beq t0, retry1f // if eq, retry failed interlock
+
+ addq a0, 7, a0 // move to next quadword
+ bic a0, 7, a0 // aligned quadword
+
+ addq a1, 7, a1 // move to next quadword
+ bic a1, 7, a1 // aligned quadword
+
+//
+// Check for 64-byte block moves
+//
+
+20:
+ srl a2, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes
+
+30:
+ ldq t1, 0(a1) // load 64 bytes from source
+ addq a0, 64, a0 // increment destination pointer
+ ldq v0, 56(a1) //
+ ldq a3, 32(a1) //
+ stq t1, -64(a0) // write to destination
+ ldq t2, 8(a1) // into volatile registers
+ ldq t3, 16(a1) //
+ ldq t4, 24(a1) //
+ subq t0, 1, t0 // decrement number of blocks
+ stq t2, -56(a0) //
+ ldq a4, 40(a1) //
+ stq t3, -48(a0) //
+ ldq a5, 48(a1) //
+ stq t4, -40(a0) //
+ addq a1, 64, a1 // increment source pointer
+ stq a3, -32(a0) //
+ stq a4, -24(a0) //
+ stq a5, -16(a0) //
+ stq v0, -8(a0) //
+ bne t0, 30b // if ne, more blocks to copy
+
+//
+// Copy quadwords
+//
+
+40:
+ srl a2, 3, t0 // t0 = number of quadwords to move
+ beq t0, 60f // if eq no quadwords to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+50:
+ ldq t1, 0(a1) // load quadword from source
+ addq a1, 8, a1 // increment source pointer
+ stq t1, 0(a0) // store quadword to destination
+ addq a0, 8, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of quadwords
+ bne t0, 50b // if ne, more quadwords to move
+
+//
+// Move final residual bytes
+//
+
+60:
+ beq a2, 80f // if eq, no more bytes to move
+ mov a2, t0 // t0 = number of bytes to move
+ mov -1, t1 // t1 = bit mask
+ sll t0, 3, t0 // # of bytes to # of bits
+ srl t1, t0, t1 // clear t0 bits
+ sll t1, t0, t0 // move it back
+ ldq t1, 0(a1) // get last source quadword
+ bic t1, t0, t1 // clear bytes not copied
+ not t0, t0 // complement to clear destination
+retry2:
+ ldq_l t2, 0(a0) // get last destination quadword locked
+ bic t2, t0, t2 // clear bytes to be copied
+ bis t2, t1, t2 // move bytes from source
+ stq_c t2, 0(a0) // store merged quadword conditional
+ beq t2, retry2f // if eq, retry failed interlock
+
+//
+// Finish aligned MoveForward
+//
+
+80:
+ ret zero, (ra) // return
+
+//
+// Move memory forward unaligned.
+//
+
+CopyForwardUnaligned: //
+
+//
+// Move bytes until the destination is aligned
+//
+
+ and a0, 0x7, t0 // t0 = unaligned bits
+ beq t0, 100f // if eq, destination quad aligned
+ bis zero, zero, t1 // t4 = destination byte zap mask
+ bis zero, 1, t2
+ sll t2, t0, t2 // t2 = next bit to set in zap mask
+ mov zero, t4 // assemble destination bytes here
+90:
+ beq a2, 95f // if eq no more bytes to move
+ bis t1, t2, t1 // set bit in zap mask
+ sll t2, 1, t2 // set next higher bit for zap mask
+ ldq_u t5, 0(a1) // get unaligned quad from source
+ extbl t5, a1, t5 // extract source byte
+ insbl t5, t0, t5 // t5 = source byte, in position
+ or t4, t5, t4 // merge in source byte
+ addq t0, 1, t0 // increment byte position
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ cmpeq t0, 8, t3 // t0 = 8? => quad finished
+ beq t3, 90b // if eq [false], more bytes to move
+95:
+ bic a0, 0x7, a3 // a3 = quadword base of destination
+retry3:
+ ldq_l t0, 0(a3) // load destination quadword
+ zap t0, t1, t0 // clear destination bytes
+ or t0, t4, t0 // merge in bytes from source
+ stq_c t0, 0(a3) // store merged quadword conditional
+ beq t0, retry3f // if eq, retry failed interlock
+
+ addq a0, 7, a0 // increment to next quad
+ bic a0, 7, a0 // align next quadword
+
+//
+// Check for 64-byte blocks to move
+//
+
+100:
+ srl a2, 6, t0 // t0 = number of blocks to move
+ beq t0, 120f // if eq no blocks to move
+ and a2, 64-1, a2 // a2 = residual bytes to move
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+110:
+ // get source data and merge it
+ // as we go
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quad #1
+ ldq_u t3, 15(a1) // t3 = third unaligned quad
+ extql t2, a1, t2 // extract applicable bytes from t2
+ extqh t3, a1, v0 // extract applicable bytes from t3
+ stq t1, 0(a0) // store quad #1
+ bis t2, v0, t2 // t2 = quad #2
+ ldq_u t4, 23(a1) // t4 = fourth unaligned quad
+ extql t3, a1, t3 // extract applicable bytes from t3
+ extqh t4, a1, v0 // extract applicable bytes from t4
+ stq t2, 8(a0) // store quad #2
+ bis t3, v0, t3 // t3 = quad #3
+ ldq_u t5, 31(a1) // t5 = fifth unaligned quad
+ extql t4, a1, t4 // extract applicable bytes from t4
+ extqh t5, a1, v0 // extract applicable bytes from t5
+ stq t3, 16(a0) // store quad #3
+ bis t4, v0, t4 // t4 = quad #4
+ ldq_u a3, 39(a1) // a3 = sixth unaligned quad
+ extql t5, a1, t5 // extract applicable bytes from t5
+ extqh a3, a1, v0 // extract applicable bytes from a3
+ stq t4, 24(a0) // store quad #4
+ bis t5, v0, t5 // t5 = quad #5
+ ldq_u a4, 47(a1) // a4 = seventh unaligned quad
+ extql a3, a1, a3 // extract applicable bytes from a3
+ extqh a4, a1, v0 // extract applicable bytes from a4
+ stq t5, 32(a0) // store quad #5
+ bis a3, v0, a3 // a3 = quad #6
+ ldq_u a5, 55(a1) // a5 = eighth unaligned quad
+ extql a4, a1, a4 // extract applicable bytes from a4
+ extqh a5, a1, v0 // extract applicable bytes from a5
+ stq a3, 40(a0) // store quad #6
+ bis a4, v0, a4 // a4 = quad #7
+ ldq_u t1, 63(a1) // t1 = ninth unaligned = 1st of next
+ extql a5, a1, a5 // extract applicable bytes from a5
+ extqh t1, a1, v0 // extract applicable bytes from t1
+ stq a4, 48(a0) // store quad #7
+ bis a5, v0, a5 // a5 = quad #8
+ addq a1, 64, a1 // increment source pointer
+ stq a5, 56(a0) // store quad #8
+ addq a0, 64, a0 // increment destination pointer
+ subq t0, 1, t0 // decrement number of blocks
+ bne t0, 110b // if ne, more blocks to move
+
+//
+// Move unaligned source quads to aligned destination quads
+//
+
+120:
+ srl a2, 3, t0 // t0 = number of quads to move
+ beq t0, 140f // if eq no quads to move
+ and a2, 8-1, a2 // a2 = residual bytes
+
+
+ ldq_u t1, 0(a1) // t1 = first unaligned quad
+130:
+ ldq_u t2, 7(a1) // t2 = second unaligned quad
+ addq a0, 8, a0 // increment destination pointer
+ extql t1, a1, t1 // extract applicable bytes from t1
+ extqh t2, a1, v0 // extract applicable bytes from t2
+ bis t1, v0, t1 // t1 = quadword of data
+ stq t1, -8(a0) // store data to destination
+ addq a1, 8, a1 // increment source pointer
+ subq t0, 1, t0 // decrement quads to move
+ bis t2, zero, t1 // t1 = first of next unaligned pair
+ bne t0, 130b // if ne, more quads to move
+
+//
+// Move remaining bytes to final quadword
+//
+
+140:
+ beq a2, 160f // if eq no more bytes to move
+
+ mov zero, t3 // t3 = position for next insertion
+ mov zero, t4 // assemble destination bytes here
+ mov a2, t0 // t0 = number of bytes to move
+ mov -1, t1 // t1 = bit mask
+ sll t0, 3, t0 // # of bytes to # of bits
+ srl t1, t0, t1 // clear t0 bits
+ sll t1, t0, t0 // move it back
+ not t0, t0 // complement for destination clear mask
+150:
+ ldq_u t1, 0(a1) // get unaligned source quad
+ extbl t1, a1, t1 // t1 = source byte
+ insbl t1, t3, t1 // t1 = source byte, in position
+ bis t4, t1, t4 // merge in source byte
+ addq a1, 1, a1 // increment source pointer
+ subq a2, 1, a2 // decrement bytes to move
+ addq t3, 1, t3 // increment destination position
+ bne a2, 150b // more bytes to move
+retry4:
+ ldq_l t2, 0(a0) // get last destination quadword locked
+ bic t2, t0, t2 // clear bytes to be copied
+ bis t2, t4, t2 // move bytes from source
+ stq_c t2, 0(a0) // store merged quadword conditional
+ beq t2, retry4f // if eq, retry failed interlock
+
+//
+// Finish unaligned MoveForward
+//
+
+160:
+ ret zero, (ra) // return
+
+//
+// Out of line branches for failed store conditional.
+// Don't need to restore anything, just try again.
+//
+
+retry1f:
+ br retry1
+retry2f:
+ br retry2
+retry3f:
+ br retry3
+retry4f:
+ br retry4
+
+ .end RtlCopyBytes
+
+ SBTTL("Zero Bytes")
+//++
+//
+// VOID
+// RtlZeroBytes (
+// IN PVOID Destination,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function zeros memory by first aligning the destination address to
+// a quadword boundary, and then zeroing 64-byte blocks, followed by 8-byte
+// blocks, followed by any remaining bytes. Unlike RtlZeroMemory the copy is
+// done such that byte granularity is assured for all platforms.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to zero.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be zeroed.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlZeroBytes)
+
+ bis zero, zero, a2 // set fill pattern
+ br zero, RtlpFillBytes //
+
+
+ SBTTL("Fill Bytes")
+//++
+//
+// VOID
+// RtlFillBytes (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN UCHAR Fill
+// )
+//
+// Routine Description:
+//
+// This function fills memory by first aligning the destination address to
+// a longword boundary, and then filling 32-byte blocks, followed by 4-byte
+// blocks, followed by any remaining bytes. Unlike RtlFillMemory the copy is
+// done such that byte granularity is assured for all platforms.
+//
+// Arguments:
+//
+// Destination (a0) - Supplies a pointer to the memory to fill.
+//
+// Length (a1) - Supplies the length, in bytes, of the memory to be filled.
+//
+// Fill (a2) - Supplies the fill byte.
+//
+// N.B. The alternate entry memset expects the length and fill arguments
+// to be reversed. It also returns the Destination pointer
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(RtlFillBytes)
+
+ and a2, 0xff, a2 // clear excess bits
+ sll a2, 8, t0 // duplicate fill byte
+ bis a2, t0, a2 // generate fill word
+ sll a2, 16, t0 // duplicate fill word
+ bis a2, t0, a2 // generate fill longword
+ sll a2, 32, t0 // duplicate fill longword
+ bis a2, t0, a2 // generate fill quadword
+
+.align 3 // ensure quadword aligned target
+//
+// Fill memory with the pattern specified in register a2.
+//
+
+RtlpFillBytes: //
+
+//
+// Align destination to quadword
+//
+
+ beq a1, 80f // anything to fill? (paranoia)
+ and a0, 8-1, t0 // t0 = unaligned bits
+ bne t0, 5f // if ne, then not quad aligned
+ br zero, 20f // if eq, then quad aligned
+
+5:
+ bis zero, zero, t1 // t4 = destination byte zap mask
+ bis zero, 1, t2
+ sll t2, t0, t2 // t2 = next bit to set in zap mask
+10:
+ beq a1, 15f // if eq, all bits set
+ bis t1, t2, t1 // set bit in zap mask
+ sll t2, 1, t2 // set next higher bit for zap mask
+ subq a1, 1, a1 // decrement bytes to fill
+ addq t0, 1, t0 // increment byte within quad
+ cmpeq t0, 8, t3 // finished the quadword?
+ beq t3, 10b // if eq [false], do next byte
+15:
+ zapnot a2, t1, t2 // clear fill bytes
+ bic a0, 7, a3 // a3 = quadword base of destination
+retry5:
+ ldq_l t0, 0(a3) // load destination quadword
+ zap t0, t1, t0 // clear destination bytes
+ or t0, t2, t0 // merge in fill bytes
+ stq_c t0, 0(a3) // store merged quadword conditional
+ beq t0, retry5f // if eq, retry failed interlock
+
+ addq a0, 7, a0 // move a0 to next quadword
+ bic a0, 7, a0 // align a0 to quadword
+
+//
+// Check for 64-byte blocks
+//
+
+20:
+ srl a1, 6, t0 // t0 = number of 64 byte blocks
+ beq t0, 40f // if eq then no 64 byte blocks
+ and a1, 64-1, a1 // a1 = residual bytes to fill
+
+30:
+ stq a2, 0(a0) // store 64 bytes
+ stq a2, 8(a0) //
+ stq a2, 16(a0) //
+ stq a2, 24(a0) //
+ stq a2, 32(a0) //
+ stq a2, 40(a0) //
+ stq a2, 48(a0) //
+ stq a2, 56(a0) //
+
+ subq t0, 1, t0 // decrement blocks remaining
+ addq a0, 64, a0 // increment destination pointer
+ bne t0, 30b // more blocks to write
+
+
+
+//
+// Fill aligned quadwords
+//
+
+40:
+ srl a1, 3, t0 // t0 = number of quadwords
+ bne t0, 55f // if ne quadwords left to fill
+ br zero, 60f // if eq no quadwords left
+
+55:
+ and a1, 8-1, a1 // a1 = residual bytes to fill
+
+50:
+ stq a2, 0(a0) // store quadword
+ subq t0, 1, t0 // decrement quadwords remaining
+ addq a0, 8, a0 // next quadword
+ bne t0, 50b // more quadwords to write
+
+//
+// Fill bytes for last quadword
+//
+
+60:
+ beq a1, 80f // if eq no more bytes to fill
+
+ mov a1, t0 // t0 = number of bytes to move
+ mov -1, t1 // t1 = bit mask
+ sll t0, 3, t0 // # of bytes to # of bits
+ srl t1, t0, t1 // clear t0 bits
+ sll t1, t0, t0 // move it back
+ bic a2, t0, t1 // clear fill bytes not copied
+ not t0, t0 // complement to clear destination
+retry6:
+ ldq_l t2, 0(a0) // get last destination quadword locked
+ bic t2, t0, t2 // clear bytes to be copied
+ bis t2, t1, t2 // move bytes from source
+ stq_c t2, 0(a0) // store merged quadword conditional
+ beq t2, retry6f // if eq, retry failed interlock
+
+//
+// Finish up
+//
+
+80:
+ ret zero, (ra) // return
+
+//
+// Out of line branches for failed store conditional.
+// Don't need to restore anything, just try again.
+//
+
+retry5f:
+ br retry5
+retry6f:
+ br retry6
+
+ .end RtlZeroBytes
diff --git a/private/ntos/rtl/alpha/ntcurteb.s b/private/ntos/rtl/alpha/ntcurteb.s
new file mode 100644
index 000000000..22c9f1f88
--- /dev/null
+++ b/private/ntos/rtl/alpha/ntcurteb.s
@@ -0,0 +1,57 @@
+// TITLE("Get Current TEB Pointer")
+//++
+//
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// ntcurteb.s
+//
+// Abstract:
+//
+// This module implements the function to retrieve the current TEB pointer.
+//
+// Author:
+//
+// Joe Notarangelo 29-Jul-1992
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksalpha.h"
+
+//++
+//
+// PTEB
+// NtCurrentTeb(
+// VOID
+// )
+//
+// Routine Description:
+//
+// This function returns the current TEB pointer retrieved via an unprivileged
+// call pal. Since the call pal is unprivileged this routine is appropriate in
+// any mode.
+//
+// Arguments:
+//
+// None.
+//
+// Return Value:
+//
+// Current TEB pointer.
+//
+//--
+
+ LEAF_ENTRY(NtCurrentTeb)
+
+ GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) result in v0
+
+ ret zero, (ra) // return
+
+ .end NtCurrentTeb
diff --git a/private/ntos/rtl/alpha/ntrtlalp.h b/private/ntos/rtl/alpha/ntrtlalp.h
new file mode 100644
index 000000000..772cf749b
--- /dev/null
+++ b/private/ntos/rtl/alpha/ntrtlalp.h
@@ -0,0 +1,123 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ ntrtlalp.h
+
+Abstract:
+
+ Alpha specific parts of ntrtlp.h.
+
+Author:
+
+ David N. Cutler (davec) 19-Apr-90
+
+Revision History:
+
+ Thomas Van Baak (tvb) 5-May-1992
+
+ Adapted for Alpha AXP.
+
+--*/
+
+//
+// Define exception routine function prototypes.
+//
+
+EXCEPTION_DISPOSITION
+RtlpExecuteHandlerForException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN ULONG EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext,
+ IN PEXCEPTION_ROUTINE ExceptionRoutine
+ );
+
+EXCEPTION_DISPOSITION
+RtlpExecuteHandlerForUnwind (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN ULONG EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PDISPATCHER_CONTEXT DispatcherContext,
+ IN PEXCEPTION_ROUTINE ExceptionRoutine
+ );
+
+//
+// Define procedure prototypes for exception filter and termination handler
+// execution routines.
+//
+
+LONG
+RtlpExecuteExceptionFilter (
+ PEXCEPTION_POINTERS ExceptionPointers,
+ EXCEPTION_FILTER ExceptionFilter,
+ ULONG EstablisherFrame
+ );
+
+VOID
+RtlpExecuteTerminationHandler (
+ BOOLEAN AbnormalTermination,
+ TERMINATION_HANDLER TerminationHandler,
+ ULONG EstablisherFrame
+ );
+
+//
+// Define function prototype for restore context.
+//
+
+VOID
+RtlpRestoreContext (
+ IN PCONTEXT Context
+ );
+
+#if DBG
+
+//
+// Define global flags to debug/validate exception handling for Alpha.
+//
+
+extern ULONG RtlDebugFlags;
+
+//
+// Print exception records as delivered by PALcode (KiDispatchException).
+//
+
+#define RTL_DBG_PAL_EXCEPTION 0x00001
+
+//
+// Software raised exceptions (RtlRaiseException, RtlRaiseStatus).
+//
+
+#define RTL_DBG_RAISE_EXCEPTION 0x00002
+
+//
+// Find a handler to take the exception (RtlDispatchException).
+//
+
+#define RTL_DBG_DISPATCH_EXCEPTION 0x00030
+#define RTL_DBG_DISPATCH_EXCEPTION_DETAIL 0x00020
+
+//
+// Call handlers and unwind to a target frame (RtlUnwind).
+//
+
+#define RTL_DBG_UNWIND 0x00300
+#define RTL_DBG_UNWIND_DETAIL 0x00200
+
+//
+// Climb one frame up the call stack (RtlVirtualUnwind).
+//
+
+#define RTL_DBG_VIRTUAL_UNWIND 0x03000
+#define RTL_DBG_VIRTUAL_UNWIND_DETAIL 0x02000
+
+//
+// Find the function entry for a given PC (RtlLookupFunctionEntry).
+//
+
+#define RTL_DBG_FUNCTION_ENTRY 0x30000
+#define RTL_DBG_FUNCTION_ENTRY_DETAIL 0x20000
+
+#endif // DBG
diff --git a/private/ntos/rtl/alpha/setjmp.s b/private/ntos/rtl/alpha/setjmp.s
new file mode 100644
index 000000000..830f9465e
--- /dev/null
+++ b/private/ntos/rtl/alpha/setjmp.s
@@ -0,0 +1,148 @@
+// TITLE("Set Jump")
+//++
+//
+// Copyright (c) 1993 Microsoft Corporation
+// Copyright (c) 1993 Digital Equipment Corporation
+//
+// Module Name:
+//
+// setjmp.s
+//
+// Abstract:
+//
+// This module implements the Alpha acc compiler specific routine to
+// provide SAFE handling of setjmp/longjmp with respect to structured
+// exception handling.
+//
+// N.B. This function has been replaced by setjmp/setjmpex/longjmp in the
+// C runtime library. It remains here for backwards compatibility of
+// Beta 2 applications expecting setjmp and longjmp to be present in
+// ntdll, or for new acc compiled applications that link with ntdll
+// before libc.
+//
+// Author:
+//
+// David N. Cutler (davec) 2-Apr-1993
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 22-Apr-1993
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+ SBTTL("Set Jump")
+//++
+//
+// int
+// setjmp (
+// IN jmp_buf JumpBuffer
+// )
+//
+// Routine Description:
+//
+// This function implements a safe setjmp.
+//
+// Arguments:
+//
+// JumpBuffer (a0) - Supplies the address of a jump buffer to store the
+// jump information.
+//
+// N.B. This is an array of double's to force quadword alignment.
+//
+// Return Value:
+//
+// A value of zero is returned.
+//
+//--
+
+ .struct 0
+SjRa: .space 8 // saved return address
+SjS0: .space 8 // saved integer register s0
+SjFl: .space 8 // InFunction flag variable
+SjEf: .space 8 // EstablisherFrame(s) structure
+SjCx: .space ContextFrameLength // context frame
+SetjmpFrameLength:
+
+ NESTED_ENTRY(setjmp, SetjmpFrameLength, zero)
+
+ lda sp, -SetjmpFrameLength(sp) // allocate stack frame
+ stq ra, SjRa(sp) // save return address
+ stq s0, SjS0(sp) // save integer register s0
+
+ PROLOGUE_END
+
+//
+// Save the nonvolatile machine state.
+//
+
+ lda t0, SjCx(sp) // address of context record
+
+ .set noreorder
+ .set noat
+ stt f2, CxFltF2(t0) // save floating registers f2 - f9
+ stt f3, CxFltF3(t0) //
+ stt f4, CxFltF4(t0) //
+ stt f5, CxFltF5(t0) //
+ stt f6, CxFltF6(t0) //
+ stt f7, CxFltF7(t0) //
+ stt f8, CxFltF8(t0) //
+ stt f9, CxFltF9(t0) //
+
+ stq s0, CxIntS0(t0) // save integer registers s0 - fp/s6
+ stq s1, CxIntS1(t0) //
+ stq s2, CxIntS2(t0) //
+ stq s3, CxIntS3(t0) //
+ stq s4, CxIntS4(t0) //
+ stq s5, CxIntS5(t0) //
+ stq fp, CxIntFp(t0) //
+ .set at
+ .set reorder
+
+ stq gp, CxIntGp(t0) // save integer register gp
+ lda v0, SetjmpFrameLength(sp) // compute stack pointer of caller
+ stq v0, CxIntSp(t0) // save caller stack pointer
+ stq ra, CxIntRa(t0) // save return address
+ stq ra, CxFir(t0) // save continuation address
+ ldil t1, 2 // get acc safe setjmp flag
+ stl t1, JbType(a0) // set jump buffer context type
+ stl ra, JbPc(a0) // save target instruction address
+ mov a0, s0 // preserve jump buffer address
+
+//
+// Perform unwind to determine the virtual frame pointer of the caller.
+//
+
+ subl ra, 4, a0 // compute control PC address
+ bsr RtlLookupFunctionEntry // lookup function table address
+
+ ldq a0, SjRa(sp) // get return address
+ subl a0, 4, a0 // compute control PC address
+ mov v0, a1 // set address of function entry
+ lda a2, SjCx(sp) // address of context record
+ lda a3, SjFl(sp) // set address of in function variable
+ lda a4, SjEf(sp) // set frame pointers address
+ mov zero, a5 // set context pointer array address
+ bsr RtlVirtualUnwind // compute virtual frame pointer value
+
+//
+// Set return value, restore registers, deallocate stack frame, and return.
+//
+
+ ldl t0, SjEf(sp) // get virtual frame pointer
+ stl t0, JbFp(s0) // save virtual frame pointer address
+ mov zero, v0 // set return value
+
+ ldq s0, SjS0(sp) // restore integer register s0
+ ldq ra, SjRa(sp) // restore return address
+ lda sp, SetjmpFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end setjmp
diff --git a/private/ntos/rtl/alpha/tcmpmem.c b/private/ntos/rtl/alpha/tcmpmem.c
new file mode 100644
index 000000000..11ef83631
--- /dev/null
+++ b/private/ntos/rtl/alpha/tcmpmem.c
@@ -0,0 +1,105 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tcmpmem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlCompareMemory
+ function by running an exhaustive test of every case of string offset,
+ compare length, and return value up to and a little beyond one 32-byte
+ cache line. This represents over one million test cases. It is assumed
+ any bugs that exist will be found within this range. If only the error
+ count summary is desired, type "tcmpmem > nul" instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 11-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+#define BUFFER_SIZE (MAX_OFFSET + MAX_LENGTH)
+
+UCHAR String1[BUFFER_SIZE];
+UCHAR String2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Expected;
+ ULONG Length;
+ ULONG Offset1;
+ ULONG Offset2;
+ ULONG Result;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlCompareMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ for (Offset1 = 0; Offset1 <= MAX_OFFSET; Offset1 += 1) {
+
+ //
+ // Copy the test pattern to Offset1 in String1 and then for each
+ // possible offset of String1, for each possible offset of String2,
+ // for each possible string compare length, and for each expected
+ // return value, make a call RtlCompareMemory.
+ //
+
+ FillPattern(&String1[Offset1], MAX_LENGTH);
+ for (Offset2 = 0; Offset2 <= MAX_OFFSET; Offset2 += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+ for (Expected = 0; Expected <= Length; Expected += 1) {
+
+ //
+ // Copy the test pattern starting at Offset2 in String2,
+ // change one byte at location `Expected', call
+ // RtlCompareMemory, and check that the function value
+ // is in fact the expected value.
+ //
+
+ FillPattern(&String2[Offset2], MAX_LENGTH);
+ String2[Offset2 + Expected] = ' ';
+ Result = RtlCompareMemory(&String1[Offset1],
+ &String2[Offset2],
+ Length);
+ TestCases += 1;
+ if (Result != Expected) {
+ ErrorCount += 1;
+
+ //
+ // The function failed to return the proper value.
+ //
+
+ printf("ERROR: Offset1 = %d, Offset2 = %d, Length = %d, Expected = %d, Result = %d\n",
+ Offset1, Offset2, Length, Expected, Result);
+ printf(" String1[Offset1] = %lx: <%.*s>\n",
+ &String1[Offset1], Length, &String1[Offset1]);
+ printf(" String2[Offset2] = %lx: <%.*s>\n",
+ &String2[Offset2], Length, &String2[Offset2]);
+ printf("\n");
+ }
+ }
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlCompareMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/tfilmem.c b/private/ntos/rtl/alpha/tfilmem.c
new file mode 100644
index 000000000..0a347599c
--- /dev/null
+++ b/private/ntos/rtl/alpha/tfilmem.c
@@ -0,0 +1,79 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tfilmem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlFillMemory
+ function by running an exhaustive test of every case of string offset
+ and length up to and a little beyond one 32-byte cache line. This
+ represents several thousand test cases. It is assumed any bugs that
+ exist will be found within this range. If only the error count summary
+ is desired, type "tfilmem > nul" instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 13-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+#define BUFFER_SIZE (MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN)
+
+UCHAR Buffer1[BUFFER_SIZE];
+UCHAR Buffer2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Length;
+ ULONG Offset;
+ ULONG Result;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlFillMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+
+ FillPattern(Buffer1, BUFFER_SIZE);
+ FillPattern(Buffer2, BUFFER_SIZE);
+ LocalFillMemory(&Buffer1[Offset], Length, '@');
+ RtlFillMemory(&Buffer2[Offset], Length, '@');
+
+ Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE);
+
+ TestCases += 1;
+ if (Result != BUFFER_SIZE) {
+ ErrorCount += 1;
+
+ printf("ERROR: Offset = %d, Length = %d\n", Offset, Length);
+ printf("Buffers differ starting at byte %d:\n", Result);
+ printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1);
+ printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2);
+ printf("\n");
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlFillMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/tmovmem.c b/private/ntos/rtl/alpha/tmovmem.c
new file mode 100644
index 000000000..3b381d9ba
--- /dev/null
+++ b/private/ntos/rtl/alpha/tmovmem.c
@@ -0,0 +1,129 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tmovmem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlMoveMemory
+ function by running an exhaustive test of every case of string offset,
+ move length, and relative overlap of the two strings up to and a little
+ beyond one 32-byte cache line. This represents several hundred thousand
+ test cases. It is assumed any bugs that exist will be found within this
+ range. If only the error count summary is desired, type "tmovmem > nul"
+ instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 13-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+//
+// Two strings are defined within a large buffer. The target string is
+// initially below the source string with a small gap between the two
+// strings. A margin around the strings ensures any bytes accidentally
+// changed outside the strings are detectable. As the length of the strings
+// and the offset of the source string are varied, the target string wanders
+// from well below, through, and well above the source string.
+//
+
+#define MIN_OVERLAP (-(MAX_LENGTH + MAX_MARGIN))
+#define MAX_OVERLAP (MAX_LENGTH + MAX_MARGIN - 1)
+
+#define BUFFER_SIZE (MAX_MARGIN + MAX_LENGTH + MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN + MAX_LENGTH + MAX_MARGIN)
+
+UCHAR Buffer0[BUFFER_SIZE];
+UCHAR Buffer1[BUFFER_SIZE];
+UCHAR Buffer2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Length;
+ ULONG Offset;
+ LONG Overlap;
+ ULONG Result;
+ ULONG Source;
+ ULONG Target;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlMoveMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ //
+ // Make a call to RtlMoveMemory for all possible source string offsets
+ // within a cache line, for a large set of string lengths, and a wide
+ // range of positions of the target string relative to the source string,
+ // including all possible overlapping string configurations.
+ //
+
+ for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+ for (Overlap = MIN_OVERLAP; Overlap <= MAX_OVERLAP; Overlap += 1) {
+
+ //
+ // The same string configuration is made in two different
+ // buffers. RtlMoveMemory is used on the two strings in one
+ // buffer and the trusted LocalMoveMemory on the two strings
+ // in the other buffer. The entire buffers are compared to
+ // determine if the two move functions agree.
+ //
+
+ FillPattern(Buffer1, BUFFER_SIZE);
+ FillPattern(Buffer2, BUFFER_SIZE);
+
+ Source = MAX_MARGIN + MAX_LENGTH + MAX_MARGIN + Offset;
+ Target = Source + Overlap;
+
+ LocalMoveMemory(&Buffer1[Target], &Buffer1[Source], Length);
+ RtlMoveMemory(&Buffer2[Target], &Buffer2[Source], Length);
+
+ Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE);
+
+ TestCases += 1;
+ if (Result != BUFFER_SIZE) {
+ ErrorCount += 1;
+
+ printf("ERROR: Offset = %d, Length = %d, Overlap = %d\n",
+ Offset, Length, Overlap);
+ printf("RtlMoveMemory( &Buffer[ %d ], &Buffer[ %d ], %d )\n",
+ Target, Source, Length);
+
+ FillPattern(Buffer0, BUFFER_SIZE);
+ printf(" Original Source = %lx: <%.*s>\n",
+ &Buffer0[Source], Length, &Buffer0[Source]);
+ printf(" Expected Target = %lx: <%.*s>\n",
+ &Buffer1[Target], Length, &Buffer1[Target]);
+ printf(" Actual Target = %lx: <%.*s>\n",
+ &Buffer2[Target], Length, &Buffer2[Target]);
+ printf("\n");
+ printf("Buffers differ starting at byte %d:\n", Result);
+ printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1);
+ printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2);
+ printf("\n");
+ }
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlMoveMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/trampoln.s b/private/ntos/rtl/alpha/trampoln.s
new file mode 100644
index 000000000..b37d471e7
--- /dev/null
+++ b/private/ntos/rtl/alpha/trampoln.s
@@ -0,0 +1,524 @@
+// TITLE("Trampoline Code For User Mode APC and Exception Dispatching")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// trampoln.s
+//
+// Abstract:
+//
+// This module implements the trampoline code necessary to dispatch user
+// mode APCs and exceptions.
+//
+// Author:
+//
+// David N. Cutler (davec) 3-Apr-1990
+//
+// Environment:
+//
+// User mode only.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 11-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Define length of exception dispatcher stack frame.
+//
+
+#define ExceptionDispatcherFrameLength (ExceptionRecordLength + ContextFrameLength)
+
+ SBTTL("User APC Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the APC dispatcher.
+//
+//--
+
+//
+// N.B. This function specifies its own private exception handler.
+//
+ EXCEPTION_HANDLER(KiUserApcHandler)
+
+ NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero);
+
+ .set noreorder
+ .set noat
+ stq sp, CxIntSp(sp) // save stack pointer
+ stq ra, CxIntRa(sp) // save return address
+ stq ra, CxFir(sp) // set continuation address
+ stq fp, CxIntFp(sp) // save integer register fp
+ stq gp, CxIntGp(sp) // save integer register gp
+
+ stq s0, CxIntS0(sp) // save integer registers s0 - s5
+ stq s1, CxIntS1(sp) //
+ stq s2, CxIntS2(sp) //
+ stq s3, CxIntS3(sp) //
+ stq s4, CxIntS4(sp) //
+ stq s5, CxIntS5(sp) //
+
+ stt f2, CxFltF2(sp) // save floating registers f2 - f9
+ stt f3, CxFltF3(sp) //
+ stt f4, CxFltF4(sp) //
+ stt f5, CxFltF5(sp) //
+ stt f6, CxFltF6(sp) //
+ stt f7, CxFltF7(sp) //
+ stt f8, CxFltF8(sp) //
+ stt f9, CxFltF9(sp) //
+
+ mov sp, fp // set frame pointer
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+//++
+//
+// VOID
+// KiUserApcDispatcher (
+// IN PVOID NormalContext,
+// IN PVOID SystemArgument1,
+// IN PVOID SystemArgument2,
+// IN PKNORMAL_ROUTINE NormalRoutine
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to deliver an APC
+// in user mode. The context frame for this routine was built when the
+// APC interrupt was processed and contains the entire machine state of
+// the current thread. The specified APC routine is called and then the
+// machine state is restored and execution is continued.
+//
+// Arguments:
+//
+// a0 - Supplies the normal context parameter that was specified when the
+// APC was initialized.
+//
+// a1 - Supplies the first argument that was provided by the executive when
+// the APC was queued.
+//
+// a2 - Supplies the second argument that was provided by the executive
+// when the APC was queued.
+//
+// a3 - Supplies the address of the function that is to be called.
+//
+// N.B. Register sp supplies a pointer to a context frame.
+//
+// N.B. Register fp supplies the same value as sp and is used as a frame
+// pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Fir address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+ ALTERNATE_ENTRY(KiUserApcDispatcher)
+
+ jsr ra, (a3) // call specified APC routine
+
+ mov fp, a0 // set address of context frame
+ ldil a1, TRUE // set test alert argument true
+ bsr ra, ZwContinue // execute system service to continue
+ mov v0, s0 // save status value
+
+//
+// Unsuccessful completion after attempting to continue execution. Use the
+// return status as the exception code, set noncontinuable exception and
+// attempt to raise another exception. Note there is no return from raise
+// status.
+//
+
+10: mov s0, a0 // set status value
+ bsr ra, RtlRaiseStatus // raise exception
+ br zero, 10b // loop on return
+
+ .end KiUserApcDispatch
+
+ SBTTL("User APC Exception Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// KiUserApcHandler (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN ULONG EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PDISPATCHER_CONTEXT DispatcherContext
+//
+// Routine Description:
+//
+// This function is called when an exception occurs in an APC routine
+// or one of its dynamic descendents, or when an unwind through the
+// APC dispatcher is in progress. If an unwind is in progress, then test
+// alert is called to ensure that all currently queued APCs are executed.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// ExceptionContinueSearch is returned as the function value.
+//
+//--
+
+ .struct 0
+HdRa: .space 8 // saved return address
+ .space 1 * 8 // required for 16-byte stack alignment
+HandlerFrameLength: // length of handler frame
+
+ NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero)
+
+ lda sp, -HandlerFrameLength(sp) // allocate stack frame
+ stq ra, HdRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// The following code is equivalent to:
+//
+// EXCEPTION_DISPOSITION
+// KiUserApcHandler(IN PEXCEPTION_RECORD ExceptionRecord)
+// {
+// if (IS_UNWINDING(ExceptionRecord->ExceptionFlags)) {
+// NtTestAlert();
+// }
+// return ExceptionContinueSearch
+// }
+//
+
+ ldl t0, ErExceptionFlags(a0) // get exception flags
+ and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress
+ beq t0, 10f // if eq, no unwind in progress
+
+ bsr ra, ZwTestAlert // test for alert pending
+
+10: ldil v0, ExceptionContinueSearch // set disposition value
+ ldq ra, HdRa(sp) // restore return address
+ lda sp, HandlerFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end KiUserApcHandler
+
+
+ SBTTL("User Callback Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the exception dispatcher.
+//
+//--
+
+ NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero);
+.set noreorder
+ stq sp, CkSp(sp)
+ stq ra, CkRa(sp)
+.set reorder
+ PROLOGUE_END
+//++
+//
+// VOID
+// KiUserCallbackDispatcher (
+// VOID
+// )
+//
+// Routine Description:
+//
+// This routine is entered on a callout from kernel mode to execute a
+// user mode callback function. All arguments for this function have
+// been placed on the stack.
+//
+// Arguments:
+//
+// (sp + 16) - Supplies a value of zero for alignment.
+//
+// (sp + 24) - Supplies the API number of the callback function that is
+// executed.
+//
+// (sp + 32) - Supplies a pointer to the input buffer.
+//
+// (sp + 40) - Supplies the input buffer length.
+//
+// Return Value:
+//
+// This function returns to kernel mode.
+//
+//--
+
+ ALTERNATE_ENTRY(KiUserCallbackDispatcher)
+
+ ldl a0, CkBuffer(sp) // get input buffer address
+ ldl a1, CkLength(sp) // get input buffer length
+ ldl t0, CkApiNumber(sp) // get API number
+ GET_THREAD_ENVIRONMENT_BLOCK // get TEB in v0
+ ldl t5, TePeb(v0) // get PEB in t5
+ ldl t2, PeKernelCallbackTable(t5) // get address of callback table
+ s4addl t0, t2, t3 // get address of callback
+ ldl t4, 0(t3) // get callback pointer
+ jsr ra, (t4) // call specified function
+
+//
+// If a return from the callback function occurs, then the output buffer
+// address and length are returned as NULL.
+//
+
+ bis zero,zero,a0 // set zero buffer address
+ bis zero,zero,a1 // set zero buffer length
+ bis v0, zero, a2 // set completion status
+ bsr ra, ZwCallbackReturn // return to kernel mode
+
+//
+// Unsuccessful completion after attempting to return to kernel mode. Use
+// the return status as the exception code, set noncontinuable exception and
+// attempt to raise another exception. Note there is no return from raise
+// status.
+//
+
+ bis v0, zero, s0 // save status value
+10: bis s0, zero, a0 // set status value
+ bsr ra, RtlRaiseStatus // raise exception
+ br zero, 10b // loop on return
+
+ .end KiUserCallbackDispatch
+
+ SBTTL("User Exception Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the exception dispatcher.
+//
+// When reverse executed, this prologue will restore all integer registers,
+// rather than just the non-volatile registers. This is necessary for proper
+// unwinding through the call to the exception dispatcher when non-standard
+// calls have been used in frames at or above the exception frame. Non-leaf
+// functions using a non-standard call are allowed to save the return address
+// register in another integer register instead of on the stack.
+//
+//--
+
+ NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrameLength, zero);
+
+ .set noreorder
+ .set noat
+ stq sp, CxIntSp(sp) // save stack pointer
+ stq ra, CxIntRa(sp) // save return address
+ stq ra, CxFir(sp) // set continuation address
+
+ stq v0, CxIntV0(sp) // save integer register v0
+ stq t0, CxIntT0(sp) // save integer registers t0 - t6
+ stq t1, CxIntT1(sp) //
+ stq t2, CxIntT2(sp) //
+ stq t3, CxIntT3(sp) //
+ stq t4, CxIntT4(sp) //
+ stq t5, CxIntT5(sp) //
+ stq t6, CxIntT6(sp) //
+ stq t7, CxIntT7(sp) //
+
+ stq s0, CxIntS0(sp) // save integer registers s0 - s5
+ stq s1, CxIntS1(sp) //
+ stq s2, CxIntS2(sp) //
+ stq s3, CxIntS3(sp) //
+ stq s4, CxIntS4(sp) //
+ stq s5, CxIntS5(sp) //
+ stq fp, CxIntFp(sp) // save integer register fp
+
+ stq a0, CxIntA0(sp) // save integer registers a0 - a5
+ stq a1, CxIntA1(sp) //
+ stq a2, CxIntA2(sp) //
+ stq a3, CxIntA3(sp) //
+ stq a4, CxIntA4(sp) //
+ stq a5, CxIntA5(sp) //
+
+ stq t8, CxIntT8(sp) // save integer registers t8 - t11
+ stq t9, CxIntT9(sp) //
+ stq t10, CxIntT10(sp) //
+ stq t11, CxIntT11(sp) //
+
+ stq t12, CxIntT12(sp) // save integer register t12
+ stq AT, CxIntAt(sp) // save integer register AT
+ stq gp, CxIntGp(sp) // save integer register gp
+
+ stt f2, CxFltF2(sp) // save floating registers f2 - f9
+ stt f3, CxFltF3(sp) //
+ stt f4, CxFltF4(sp) //
+ stt f5, CxFltF5(sp) //
+ stt f6, CxFltF6(sp) //
+ stt f7, CxFltF7(sp) //
+ stt f8, CxFltF8(sp) //
+ stt f9, CxFltF9(sp) //
+
+ mov sp, fp // set frame pointer
+ .set at
+ .set reorder
+
+ PROLOGUE_END
+
+//++
+//
+// VOID
+// KiUserExceptionDispatcher (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to dispatch a user
+// mode exception. If a frame based handler handles the exception, then
+// the execution is continued. Otherwise last chance processing is performed.
+//
+// Arguments:
+//
+// s0 - Supplies a pointer to an exception record.
+//
+// s1 - Supplies a pointer to a context frame.
+//
+// fp - Supplies the same value as sp and is used as a frame pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Fir address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+
+ ALTERNATE_ENTRY(KiUserExceptionDispatcher)
+
+ mov s0, a0 // set address of exception record
+ mov s1, a1 // set address of context frame
+ bsr ra, RtlDispatchException // attempt to dispatch the exception
+
+//
+// If the return status is TRUE, then the exception was handled and execution
+// should be continued with the NtContinue service in case the context was
+// changed. If the return status is FALSE, then the exception was not handled
+// and NtRaiseException is called to perform last chance exception processing.
+//
+
+ beq v0, 10f // if eq [false], perform last chance processing
+
+//
+// Continue execution.
+//
+
+ mov s1, a0 // set address of context frame
+ ldil a1, FALSE // set test alert argument false
+ bsr ra, ZwContinue // execute system service to continue
+ br zero, 20f // join common code
+
+//
+// Last chance processing.
+//
+
+10: mov s0, a0 // set address of exception record
+ mov s1, a1 // set address of context frame
+ ldil a2, FALSE // set first chance argument false
+ bsr ra, ZwRaiseException // perform last chance processing
+
+//
+// Common code for unsuccessful completion of the continue or last chance
+// service. Use the return status (which is now in v0) as the exception code,
+// set noncontinuable exception and attempt to raise another exception. Note
+// the stack grows and eventually this loop will end.
+//
+
+20: lda sp, -ExceptionRecordLength(sp) // allocate exception record
+ mov sp, a0 // get address of actual record
+ stl v0, ErExceptionCode(a0) // set exception code
+ ldil t0, EXCEPTION_NONCONTINUABLE // set noncontinuable flag
+ stl t0, ErExceptionFlags(a0) // store exception flags
+ stl s0, ErExceptionRecord(a0) // set associated exception record
+ stl zero, ErNumberParameters(a0) // set number of parameters
+ bsr ra, RtlRaiseException // raise exception
+ br zero, 20b // loop on error
+
+ .end KiUserExceptionDispatch
+
+//++
+//
+// NTSTATUS
+// KiRaiseUserExceptionDispatcher (
+// IN NTSTATUS ExceptionCode
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to raise a user
+// mode exception.
+//
+// Arguments:
+//
+// v0 - Supplies the status code to be raised.
+//
+// Return Value:
+//
+// ExceptionCode
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Fir address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+
+ .struct 0
+RaiseRa: .space 8 // saved return address
+RaiseV0: .space 8 // saved S0
+RaiseExr: .space ExceptionRecordLength // exception record for RtlRaiseException
+RaiseFrameLength: // length of handler frame
+
+ NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero)
+ lda sp, -RaiseFrameLength(sp) // allocate stack frame
+ stq ra, RaiseRa(sp) // save return address
+ PROLOGUE_END
+
+ stq v0, RaiseV0(sp) // save function return status
+ stl v0, ErExceptionCode+RaiseExr(sp) // set exception code
+ stl zero, ErExceptionFlags+RaiseExr(sp) // set exception flags
+ stl zero, ErExceptionRecord+RaiseExr(sp) // set exception record
+ stl ra, ErExceptionAddress+RaiseExr(sp) // set exception address
+ stl zero, ErNumberParameters+RaiseExr(sp)
+
+ lda a0, RaiseExr(sp) // set argument to RtlRaiseException
+ bsr ra, RtlRaiseException // attempt to raise the exception
+
+ ldq v0, RaiseV0(sp) // return status
+
+ ldq ra, RaiseRa(sp) // restore ra
+ lda sp, RaiseFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end KiRaiseUserExceptionDispatch
diff --git a/private/ntos/rtl/alpha/tzermem.c b/private/ntos/rtl/alpha/tzermem.c
new file mode 100644
index 000000000..c56f1620a
--- /dev/null
+++ b/private/ntos/rtl/alpha/tzermem.c
@@ -0,0 +1,79 @@
+/*++
+
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ tzermem.c
+
+Abstract:
+
+ This module implements a test of the operation of the RtlZeroMemory
+ function by running an exhaustive test of every case of string offset
+ and length up to and a little beyond one 32-byte cache line. This
+ represents several thousand test cases. It is assumed any bugs that
+ exist will be found within this range. If only the error count summary
+ is desired, type "tzermem > nul" instead.
+
+Author:
+
+ Thomas Van Baak (tvb) 13-Jan-1993
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <stdio.h>
+#include "localrtl.h"
+
+#define BUFFER_SIZE (MAX_MARGIN + MAX_OFFSET + MAX_LENGTH + MAX_MARGIN)
+
+UCHAR Buffer1[BUFFER_SIZE];
+UCHAR Buffer2[BUFFER_SIZE];
+
+void
+_CRTAPI1
+main()
+{
+ ULONG ErrorCount;
+ ULONG Length;
+ ULONG Offset;
+ ULONG Result;
+ ULONG TestCases;
+
+ fprintf(stderr, "Testing RtlZeroMemory\n");
+ ErrorCount = 0;
+ TestCases = 0;
+
+ for (Offset = 0; Offset <= MAX_OFFSET; Offset += 1) {
+ for (Length = 0; Length <= MAX_LENGTH; Length += 1) {
+
+ FillPattern(Buffer1, BUFFER_SIZE);
+ FillPattern(Buffer2, BUFFER_SIZE);
+ LocalZeroMemory(&Buffer1[Offset], Length);
+ RtlZeroMemory(&Buffer2[Offset], Length);
+
+ Result = LocalCompareMemory(Buffer1, Buffer2, BUFFER_SIZE);
+
+ TestCases += 1;
+ if (Result != BUFFER_SIZE) {
+ ErrorCount += 1;
+
+ printf("ERROR: Offset = %d, Length = %d\n", Offset, Length);
+ printf("Buffers differ starting at byte %d:\n", Result);
+ printf("Buffer1 = <%*s>\n", BUFFER_SIZE, Buffer1);
+ printf("Buffer2 = <%*s>\n", BUFFER_SIZE, Buffer2);
+ printf("\n");
+ }
+ }
+ }
+
+ fprintf(stderr, "Test of RtlZeroMemory completed: ");
+ fprintf(stderr, "%d test cases, %d errors found.\n", TestCases, ErrorCount);
+}
diff --git a/private/ntos/rtl/alpha/unwindr.c b/private/ntos/rtl/alpha/unwindr.c
new file mode 100644
index 000000000..ff8c4a562
--- /dev/null
+++ b/private/ntos/rtl/alpha/unwindr.c
@@ -0,0 +1,871 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+Copyright (c) 1993 Digital Equipment Corporation
+
+Module Name:
+
+ unwindr.c
+
+Abstract:
+
+ This module implements two alternate versions of the unwind function
+ required by Alpha AXP during the GEM compiler transition period. The
+ code is adapted from RtlUnwind (exdsptch.c). These functions can be
+ deleted if GEM uses scope table based structured exception handling
+ and can materialize virtual frame pointers.
+
+Author:
+
+ Thomas Van Baak (tvb) 18-Nov-1992
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Define local macros.
+//
+// Raise noncontinuable exception with associated exception record.
+//
+
+#define RAISE_EXCEPTION(Status, ExceptionRecordt) { \
+ EXCEPTION_RECORD ExceptionRecordn; \
+ \
+ ExceptionRecordn.ExceptionCode = Status; \
+ ExceptionRecordn.ExceptionFlags = EXCEPTION_NONCONTINUABLE; \
+ ExceptionRecordn.ExceptionRecord = ExceptionRecordt; \
+ ExceptionRecordn.NumberParameters = 0; \
+ RtlRaiseException(&ExceptionRecordn); \
+ }
+
+//
+// The low 2 bits of ExceptionHandler are flags bits and not part of the
+// exception handler address.
+//
+
+#define IS_HANDLER_DEFINED(FunctionEntry) \
+ (((ULONG)FunctionEntry->ExceptionHandler & ~0x3) != 0)
+
+#if DBG
+
+//
+// Maintain a short history of PC's for malformed function table errors.
+//
+
+#define PC_HISTORY_DEPTH 4
+
+//
+// Definition of global flag to debug/validate exception handling.
+// See ntrtlalp.h for the bit definitions in this flag word.
+//
+
+ULONG RtlDebugFlags;
+
+#endif
+
+#define Virtual VirtualFramePointer
+#define Real RealFramePointer
+
+VOID
+RtlUnwindRfp (
+ IN PVOID TargetRealFrame OPTIONAL,
+ IN PVOID TargetIp OPTIONAL,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. If the TargetRealFrame parameter is not specified, then the exit
+ unwind flag is also set in the exception flags of the exception record.
+ A backward scan through the procedure call frames is then performed to
+ find the target of the unwind operation.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ This function is identical to RtlUnwind except that the TargetRealFrame
+ parameter is the real frame pointer instead of the virtual frame pointer.
+
+Arguments:
+
+ TargetRealFrame - Supplies an optional pointer to the call frame that is
+ the target of the unwind. If this parameter is not specified, then an
+ exit unwind is performed.
+
+ TargetIp - Supplies an optional instruction address that specifies the
+ continuation address of the unwind. This address is ignored if the
+ target frame parameter is not specified.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ CONTEXT ContextRecord2;
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LastPc;
+ ULONG LowLimit;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("\nRtlUnwindRfp(TargetRealFrame = %lx, TargetIp = %lx,, ReturnValue = %lx)\n",
+ TargetRealFrame, TargetIp, ReturnValue);
+ }
+#endif
+ //
+ // Get current stack limits, capture the current context, virtually
+ // unwind to the caller of this routine, get the initial PC value, and
+ // set the unwind target address.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlCaptureContext(&ContextRecord1);
+ ControlPc = (ULONG)ContextRecord1.IntRa;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = LastPc;
+ ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp;
+
+ //
+ // If an exception record is not specified, then build a local exception
+ // record for use in calling exception handlers during the unwind operation.
+ //
+
+ if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
+ ExceptionRecord = &ExceptionRecord1;
+ ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // If the target frame of the unwind is specified, then a normal unwind
+ // is being performed. Otherwise, an exit unwind is being performed.
+ //
+
+ ExceptionFlags = EXCEPTION_UNWINDING;
+ if (ARGUMENT_PRESENT(TargetRealFrame) == FALSE) {
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_EXIT_UNWIND;
+ }
+
+ //
+ // Scan backward through the call frame hierarchy and call exception
+ // handlers until the target frame of the unwind is reached.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindRfp: Loop: FrameDepth = %d, Rfp = %lx, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, EstablisherFrame.Real, ContextRecord1.IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then copy the
+ // context record, virtually unwind to the caller of the current
+ // routine to obtain the real frame pointer of the establisher and
+ // check if there is an exception handler for the frame.
+ //
+
+ if (FunctionEntry != NULL) {
+ RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT));
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // If the real and virtual frame pointers are not within the
+ // specified stack limits, the frame pointers are unaligned, or
+ // the target frame is below the real frame and an exit unwind is
+ // not being performed, then raise the exception STATUS_BAD_STACK.
+ // Otherwise, check to determine if the current routine has an
+ // exception handler.
+ //
+
+ if ((EstablisherFrame.Real < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ (EstablisherFrame.Real > EstablisherFrame.Virtual) ||
+ ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) &&
+ ((ULONG)TargetRealFrame < EstablisherFrame.Real)) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0) ||
+ ((EstablisherFrame.Real & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
+ DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" TargetRealFrame = %08lx\n", TargetRealFrame);
+ if ((ARGUMENT_PRESENT(TargetRealFrame) != FALSE) &&
+ ((ULONG)TargetRealFrame < EstablisherFrame.Real)) {
+ DbgPrint(" TargetRealFrame is below EstablisherFrame.Real!\n");
+ }
+ DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
+ (ULONG)ContextRecord2.IntSp);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n",
+ LastPc, ControlPc);
+ DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
+#endif
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlUnwindRfp: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+}
+#endif
+
+ //
+ // The frame has an exception handler.
+ //
+ // The control PC, establisher frame pointer, the address
+ // of the function table entry, and the address of the
+ // context record are all stored in the dispatcher context.
+ // This information is used by the unwind linkage routine
+ // and can be used by the exception handler itself.
+ //
+ // A linkage routine written in assembler is used to actually
+ // call the actual exception handler. This is required by the
+ // exception handler that is associated with the linkage
+ // routine so it can have access to two sets of dispatcher
+ // context when it is called.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = &ContextRecord2;
+
+ //
+ // Call the exception handler.
+ //
+
+ do {
+
+ //
+ // If the establisher frame is the target of the unwind
+ // operation, then set the target unwind flag.
+ //
+
+ if ((ULONG)TargetRealFrame == EstablisherFrame.Real) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindRfp: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ &ContextRecord2,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindRfp: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ //
+ // Clear target unwind and collided unwind flags.
+ //
+
+ ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
+ EXCEPTION_TARGET_UNWIND);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search.
+ //
+ // Continue the search for a handler or continue
+ // execution.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is collided unwind.
+ //
+ // Set the target of the current unwind to the context
+ // record of the previous unwind, virtually unwind to
+ // the caller of the old routine, and reexecute the
+ // exception handler from the collided frame with the
+ // collided unwind flag set in the exception record.
+ //
+
+ case ExceptionCollidedUnwind :
+ ControlPc = DispatcherContext.ControlPc;
+ FunctionEntry = DispatcherContext.FunctionEntry;
+ RtlMoveMemory(&ContextRecord1,
+ DispatcherContext.ContextRecord,
+ sizeof(CONTEXT));
+
+ ContextRecord1.Fir = (ULONGLONG)(LONG)TargetIp;
+ RtlMoveMemory(&ContextRecord2,
+ &ContextRecord1,
+ sizeof(CONTEXT));
+
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+
+ } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ LastPc = (ULONG)ContextRecord1.IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (LastPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (unwind).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+ DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
+#endif
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = LastPc;
+
+ } while ((EstablisherFrame.Real < HighLimit) &&
+ (EstablisherFrame.Real != (ULONG)TargetRealFrame));
+
+ //
+ // If the establisher stack pointer is equal to the target frame
+ // pointer, then continue execution. Otherwise, an exit unwind was
+ // performed or the target of the unwind did not exist and the
+ // debugger and subsystem are given a second chance to handle the
+ // unwind.
+ //
+
+ if (EstablisherFrame.Real == (ULONG)TargetRealFrame) {
+ ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindRfp: finished unwinding, and calling RtlpRestoreContext\n");
+ }
+#endif
+ RtlpRestoreContext(&ContextRecord2);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindRfp: finished unwinding, but calling ZwRaiseException\n");
+ }
+#endif
+ ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE);
+ }
+}
+
+VOID
+RtlUnwindReturn (
+ IN PVOID TargetFrame,
+ IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL,
+ IN PVOID ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function initiates an unwind of procedure call frames. The machine
+ state at the time of the call to unwind is captured in a context record
+ and the unwinding flag is set in the exception flags of the exception
+ record. A backward scan through the procedure call frames is then
+ performed to find the target of the unwind operation. When the target
+ frame is reached, a return is made to the caller of the target frame
+ with the return value specified by the return value parameter.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called.
+
+ This function is identical to RtlUnwind except control resumes in the
+ caller of the target frame, not in the target frame itself.
+
+Arguments:
+
+ TargetFrame - Supplies an optional pointer to the call frame that is the
+ target of the unwind.
+
+ ExceptionRecord - Supplies an optional pointer to an exception record.
+
+ ReturnValue - Supplies a value that is to be placed in the integer
+ function return register just before continuing execution.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ CONTEXT ContextRecord2;
+ ULONG ControlPc;
+#if DBG
+ ULONG ControlPcHistory[PC_HISTORY_DEPTH];
+ ULONG ControlPcHistoryIndex = 0;
+#endif
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ FRAME_POINTERS EstablisherFrame;
+ ULONG ExceptionFlags;
+ EXCEPTION_RECORD ExceptionRecord1;
+#if DBG
+ LONG FrameDepth = 0;
+#endif
+ PRUNTIME_FUNCTION FunctionEntry;
+ ULONG HighLimit;
+ BOOLEAN InFunction;
+ ULONG LastPc;
+ ULONG LowLimit;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("\nRtlUnwindReturn(TargetFrame = %lx,, ReturnValue = %lx)\n",
+ TargetFrame, ReturnValue);
+ }
+#endif
+ //
+ // Get current stack limits, capture the current context, virtually
+ // unwind to the caller of this routine, get the initial PC value, and
+ // set the unwind target address.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+ RtlCaptureContext(&ContextRecord1);
+ ControlPc = (ULONG)ContextRecord1.IntRa;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ ControlPc = LastPc;
+ ContextRecord1.Fir = 0;
+
+ //
+ // If an exception record is not specified, then build a local exception
+ // record for use in calling exception handlers during the unwind operation.
+ //
+
+ if (ARGUMENT_PRESENT(ExceptionRecord) == FALSE) {
+ ExceptionRecord = &ExceptionRecord1;
+ ExceptionRecord1.ExceptionCode = STATUS_UNWIND;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = (PVOID)ControlPc;
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // A target frame of the unwind is specified so a normal unwind is
+ // being performed.
+ //
+
+ ExceptionFlags = EXCEPTION_UNWINDING;
+
+ //
+ // Scan backward through the call frame hierarchy and call exception
+ // handlers until the target frame of the unwind is reached.
+ //
+
+ do {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindReturn: Loop: FrameDepth = %d, sp = %lx, ControlPc = %lx\n",
+ FrameDepth, ContextRecord1.IntSp, ControlPc);
+ FrameDepth -= 1;
+ }
+#endif
+
+ //
+ // Lookup the function table entry using the point at which control
+ // left the procedure.
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+
+ //
+ // If there is a function table entry for the routine, then copy the
+ // context record, virtually unwind to the caller of the current
+ // routine to obtain the virtual frame pointer of the establisher and
+ // check if there is an exception handler for the frame.
+ //
+
+ if (FunctionEntry != NULL) {
+ RtlMoveMemory(&ContextRecord2, &ContextRecord1, sizeof(CONTEXT));
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+
+ //
+ // If the virtual frame pointer is not within the specified stack
+ // limits, the virtual frame pointer is unaligned, or the target
+ // frame is below the virtual frame and an exit unwind is not being
+ // performed, then raise the exception STATUS_BAD_STACK. Otherwise,
+ // check to determine if the current routine has an exception
+ // handler.
+ //
+
+ if ((EstablisherFrame.Virtual < LowLimit) ||
+ (EstablisherFrame.Virtual > HighLimit) ||
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual) ||
+ ((EstablisherFrame.Virtual & 0xF) != 0)) {
+#if DBG
+ DbgPrint("\n****** Warning - bad stack or target frame (unwind).\n");
+ DbgPrint(" EstablisherFrame Virtual = %08lx, Real = %08lx\n",
+ EstablisherFrame.Virtual, EstablisherFrame.Real);
+ DbgPrint(" TargetFrame = %08lx\n", TargetFrame);
+ if ((ARGUMENT_PRESENT(TargetFrame) != FALSE) &&
+ ((ULONG)TargetFrame < EstablisherFrame.Virtual)) {
+ DbgPrint(" TargetFrame is below EstablisherFrame!\n");
+ }
+ DbgPrint(" Previous EstablisherFrame (sp) = %08lx\n",
+ (ULONG)ContextRecord2.IntSp);
+ DbgPrint(" LowLimit = %08lx, HighLimit = %08lx\n",
+ LowLimit, HighLimit);
+ DbgPrint(" LastPc = %08lx, ControlPc = %08lx\n",
+ LastPc, ControlPc);
+ DbgPrint(" Now raising STATUS_BAD_STACK exception.\n");
+#endif
+ RAISE_EXCEPTION(STATUS_BAD_STACK, ExceptionRecord);
+
+ } else if (IS_HANDLER_DEFINED(FunctionEntry) && InFunction) {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_DISPATCH_EXCEPTION_DETAIL) {
+ DbgPrint("RtlUnwindReturn: ExceptionHandler = %lx, HandlerData = %lx\n",
+ FunctionEntry->ExceptionHandler, FunctionEntry->HandlerData);
+}
+#endif
+
+ //
+ // The frame has an exception handler.
+ //
+ // The control PC, establisher frame pointer, the address
+ // of the function table entry, and the address of the
+ // context record are all stored in the dispatcher context.
+ // This information is used by the unwind linkage routine
+ // and can be used by the exception handler itself.
+ //
+ // A linkage routine written in assembler is used to actually
+ // call the actual exception handler. This is required by the
+ // exception handler that is associated with the linkage
+ // routine so it can have access to two sets of dispatcher
+ // context when it is called.
+ //
+
+ DispatcherContext.ControlPc = ControlPc;
+ DispatcherContext.FunctionEntry = FunctionEntry;
+ DispatcherContext.EstablisherFrame = EstablisherFrame.Virtual;
+ DispatcherContext.ContextRecord = &ContextRecord2;
+
+ //
+ // Call the exception handler.
+ //
+
+ do {
+
+ //
+ // If the establisher frame is the target of the unwind
+ // operation, then set the target unwind flag.
+ //
+
+ if ((ULONG)TargetFrame == EstablisherFrame.Virtual) {
+ ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ }
+
+ ExceptionRecord->ExceptionFlags = ExceptionFlags;
+
+ //
+ // Set the specified return value in case the exception
+ // handler directly continues execution.
+ //
+
+ ContextRecord2.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindReturn: calling RtlpExecuteHandlerForUnwind, ControlPc = %lx\n", ControlPc);
+ }
+#endif
+ Disposition =
+ RtlpExecuteHandlerForUnwind(ExceptionRecord,
+ EstablisherFrame.Virtual,
+ &ContextRecord2,
+ &DispatcherContext,
+ FunctionEntry->ExceptionHandler);
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND_DETAIL) {
+ DbgPrint("RtlUnwindReturn: RtlpExecuteHandlerForUnwind returned Disposition = %lx\n", Disposition);
+ }
+#endif
+
+ //
+ // Clear target unwind and collided unwind flags.
+ //
+
+ ExceptionFlags &= ~(EXCEPTION_COLLIDED_UNWIND |
+ EXCEPTION_TARGET_UNWIND);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search.
+ //
+ // Continue the search for a handler or continue
+ // execution.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is collided unwind.
+ //
+ // Set the target of the current unwind to the context
+ // record of the previous unwind, virtually unwind to
+ // the caller of the old routine, and reexecute the
+ // exception handler from the collided frame with the
+ // collided unwind flag set in the exception record.
+ //
+
+ case ExceptionCollidedUnwind :
+ ControlPc = DispatcherContext.ControlPc;
+ FunctionEntry = DispatcherContext.FunctionEntry;
+ RtlMoveMemory(&ContextRecord1,
+ DispatcherContext.ContextRecord,
+ sizeof(CONTEXT));
+
+ ContextRecord1.Fir = 0;
+ RtlMoveMemory(&ContextRecord2,
+ &ContextRecord1,
+ sizeof(CONTEXT));
+
+ ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
+ LastPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL);
+ break;
+
+ //
+ // All other disposition values are invalid.
+ //
+ // Raise invalid disposition exception.
+ //
+
+ default :
+ RAISE_EXCEPTION(STATUS_INVALID_DISPOSITION, ExceptionRecord);
+ }
+
+ } while ((ExceptionFlags & EXCEPTION_COLLIDED_UNWIND) != 0);
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+ LastPc = (ULONG)ContextRecord1.IntRa - 4;
+
+ //
+ // If the next control PC is the same as the old control PC, then
+ // the function table is not correctly formed.
+ //
+
+ if (LastPc == ControlPc) {
+#if DBG
+ ULONG Count;
+ DbgPrint("\n****** Warning - malformed function table (unwind).\n");
+ DbgPrint("ControlPc = %08lx, %08lx", LastPc, ControlPc);
+ for (Count = 0; Count < PC_HISTORY_DEPTH; Count += 1) {
+ if (ControlPcHistoryIndex > 0) {
+ ControlPcHistoryIndex -= 1;
+ ControlPc = ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH];
+ DbgPrint(", %08lx", ControlPc);
+ }
+ }
+ DbgPrint(ControlPcHistoryIndex == 0 ? ".\n" : ", ...\n");
+ DbgPrint(" Now raising STATUS_BAD_FUNCTION_TABLE exception.\n");
+#endif
+ RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE);
+ }
+ }
+
+ //
+ // Set point at which control left the previous routine.
+ //
+
+#if DBG
+ ControlPcHistory[ControlPcHistoryIndex % PC_HISTORY_DEPTH] = ControlPc;
+ ControlPcHistoryIndex += 1;
+#endif
+ ControlPc = LastPc;
+
+ } while ((EstablisherFrame.Virtual < HighLimit) &&
+ (EstablisherFrame.Virtual != (ULONG)TargetFrame));
+
+ //
+ // If the establisher stack pointer is equal to the target frame pointer,
+ // then continue execution at the point where the call to the target frame
+ // was made. Otherwise the target of the unwind did not exist and the
+ // debugger and subsystem are given a second chance to handle the unwind.
+ //
+
+ if ((ULONG)ContextRecord1.IntSp == (ULONG)TargetFrame) {
+ ContextRecord1.IntV0 = (ULONGLONG)(LONG)ReturnValue;
+
+ //
+ // Set the continuation address to the address after the point where
+ // control left the previous frame and entered the target frame.
+ //
+
+ ContextRecord1.Fir = (ULONGLONG)(LONG)LastPc + 4;
+
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindReturn: finished unwinding, and calling RtlpRestoreContext\n");
+ }
+#endif
+ RtlpRestoreContext(&ContextRecord1);
+
+ } else {
+#if DBG
+ if (RtlDebugFlags & RTL_DBG_UNWIND) {
+ DbgPrint("RtlUnwindReturn: finished unwinding, but calling ZwRaiseException\n");
+ }
+#endif
+ ZwRaiseException(ExceptionRecord, &ContextRecord1, FALSE);
+ }
+}
diff --git a/private/ntos/rtl/alpha/xcptmisc.s b/private/ntos/rtl/alpha/xcptmisc.s
new file mode 100644
index 000000000..45141b0d5
--- /dev/null
+++ b/private/ntos/rtl/alpha/xcptmisc.s
@@ -0,0 +1,501 @@
+// TITLE("Miscellaneous Exception Handling")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+// Copyright (c) 1992 Digital Equipment Corporation
+//
+// Module Name:
+//
+// xcptmisc.s
+//
+// Abstract:
+//
+// This module implements miscellaneous routines that are required to
+// support exception handling. Functions are provided to call an exception
+// handler for an exception, call an exception handler for unwinding, call
+// an exception filter, call a termination handler, and get the caller's
+// stack limits.
+//
+// Author:
+//
+// David N. Cutler (davec) 12-Sep-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Thomas Van Baak (tvb) 7-May-1992
+//
+// Adapted for Alpha AXP.
+//
+//--
+
+#include "ksalpha.h"
+
+//
+// Define call frame for calling exception handlers.
+//
+
+ .struct 0
+CfRa: .space 8 // saved return address
+CfA3: .space 8 // save area for argument a3
+ .space 0 * 8 // 16-byte stack alignment
+CfFrameLength: // length of stack frame
+
+ SBTTL("Execute Handler for Exception")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpExecuteHandlerForException (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN ULONG EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PDISPATCHER_CONTEXT DispatcherContext,
+// IN PEXCEPTION_ROUTINE ExceptionRoutine
+// )
+//
+// Routine Description:
+//
+// This function allocates a call frame, stores the establisher frame
+// pointer in the frame, establishes an exception handler, and then calls
+// the specified exception handler as an exception handler. If a nested
+// exception occurs, then the exception handler of this function is called
+// and the establisher frame pointer is returned to the exception dispatcher
+// via the dispatcher context parameter. If control is returned to this
+// routine, then the frame is deallocated and the disposition status is
+// returned to the exception dispatcher.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of the exception handler that is to be called.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// ExceptionRoutine (a4) - Supplies a pointer to the exception handler that
+// is to be called.
+//
+// Return Value:
+//
+// The disposition value returned by the specified exception handler is
+// returned as the function value.
+//
+//--
+
+//
+// N.B. This function specifies its own private exception handler.
+//
+
+ EXCEPTION_HANDLER(RtlpExceptionHandler)
+
+ NESTED_ENTRY(RtlpExecuteHandlerForException, CfFrameLength, zero)
+
+ lda sp, -CfFrameLength(sp) // allocate stack frame
+ stq ra, CfRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// Save the address of the dispatcher context record in our stack frame so
+// that our own exception handler (not the one we're calling) can retrieve it.
+//
+
+ stq a3, CfA3(sp) // save address of dispatcher context
+
+//
+// Now call the exception handler and return its return value as ours.
+//
+
+ bic a4, 3, a4 // clear low-order bits (IEEE mode)
+ jsr ra, (a4) // call exception handler
+
+ ldq ra, CfRa(sp) // restore return address
+ lda sp, CfFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end RtlpExecuteHandlerForException
+
+ SBTTL("Local Exception Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpExceptionHandler (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN ULONG EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PDISPATCHER_CONTEXT DispatcherContext
+// )
+//
+// Routine Description:
+//
+// This function is called when a nested exception occurs. Its function
+// is to retrieve the establisher frame pointer from its establisher's
+// call frame, store this information in the dispatcher context record,
+// and return a disposition value of nested exception.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// A disposition value ExceptionNestedException is returned if an unwind
+// is not in progress. Otherwise a value of ExceptionContinueSearch is
+// returned.
+//
+//--
+
+ LEAF_ENTRY(RtlpExceptionHandler)
+
+ ldl t0, ErExceptionFlags(a0) // get exception flags
+ and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress
+ bne t0, 10f // if neq, unwind in progress
+
+//
+// Unwind is not in progress - return nested exception disposition.
+//
+
+//
+// Convert the given establisher virtual frame pointer (a1) to a real frame
+// pointer (the value of a1 minus CfFrameLength) and retrieve the pointer to
+// the dispatcher context that earlier was stored in the stack frame.
+//
+
+ ldq t0, -CfFrameLength + CfA3(a1) // get dispatcher context address
+
+ ldl t1, DcEstablisherFrame(t0) // copy the establisher frame pointer
+ stl t1, DcEstablisherFrame(a3) // to current dispatcher context
+
+ ldil v0, ExceptionNestedException // set disposition value
+ ret zero, (ra) // return
+
+//
+// Unwind is in progress - return continue search disposition.
+//
+
+10: ldil v0, ExceptionContinueSearch // set disposition value
+ ret zero, (ra) // return
+
+ .end RtlpExceptionHandler
+
+ SBTTL("Execute Handler for Unwind")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpExecuteHandlerForUnwind (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN PVOID EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PVOID DispatcherContext,
+// IN PEXCEPTION_ROUTINE ExceptionRoutine
+// )
+//
+// Routine Description:
+//
+// This function allocates a call frame, stores the establisher frame
+// pointer and the context record address in the frame, establishes an
+// exception handler, and then calls the specified exception handler as
+// an unwind handler. If a collided unwind occurs, then the exception
+// handler of this function is called and the establisher frame pointer
+// and context record address are returned to the unwind dispatcher via
+// the dispatcher context parameter. If control is returned to this routine,
+// then the frame is deallocated and the disposition status is returned to
+// the unwind dispatcher.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of the exception handler that is to be called.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// ExceptionRoutine (a4) - Supplies a pointer to the exception handler that
+// is to be called.
+//
+// Return Value:
+//
+// The disposition value returned by the specified exception handler is
+// returned as the function value.
+//
+//--
+
+//
+// N.B. This function specifies its own private exception handler.
+//
+
+ EXCEPTION_HANDLER(RtlpUnwindHandler)
+
+ NESTED_ENTRY(RtlpExecuteHandlerForUnwind, CfFrameLength, zero)
+
+ lda sp, -CfFrameLength(sp) // allocate stack frame
+ stq ra, CfRa(sp) // save return address
+
+ PROLOGUE_END
+
+//
+// Save the address of the dispatcher context record in our stack frame so
+// that our own exception handler (not the one we're calling) can retrieve it.
+//
+
+ stq a3, CfA3(sp) // save address of dispatcher context
+
+//
+// Now call the exception handler and return its return value as our return
+// value.
+//
+
+ bic a4, 3, a4 // clear low-order bits (IEEE mode)
+ jsr ra, (a4) // call exception handler
+
+ ldq ra, CfRa(sp) // restore return address
+ lda sp, CfFrameLength(sp) // deallocate stack frame
+ ret zero, (ra) // return
+
+ .end RtlpExecuteHandlerForUnwind
+
+ SBTTL("Local Unwind Handler")
+//++
+//
+// EXCEPTION_DISPOSITION
+// RtlpUnwindHandler (
+// IN PEXCEPTION_RECORD ExceptionRecord,
+// IN PVOID EstablisherFrame,
+// IN OUT PCONTEXT ContextRecord,
+// IN OUT PVOID DispatcherContext
+// )
+//
+// Routine Description:
+//
+// This function is called when a collided unwind occurs. Its function
+// is to retrieve the establisher dispatcher context, copy it to the
+// current dispatcher context, and return a disposition value of nested
+// unwind.
+//
+// Arguments:
+//
+// ExceptionRecord (a0) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (a1) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (a2) - Supplies a pointer to a context record.
+//
+// DispatcherContext (a3) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// A disposition value ExceptionCollidedUnwind is returned if an unwind is
+// in progress. Otherwise, a value of ExceptionContinueSearch is returned.
+//
+//--
+
+ LEAF_ENTRY(RtlpUnwindHandler)
+
+ ldl t0, ErExceptionFlags(a0) // get exception flags
+ and t0, EXCEPTION_UNWIND, t0 // check if unwind in progress
+ beq t0, 10f // if eq, unwind not in progress
+
+//
+// Unwind is in progress - return collided unwind disposition.
+//
+
+//
+// Convert the given establisher virtual frame pointer (a1) to a real frame
+// pointer (the value of a1 minus CfFrameLength) and retrieve the pointer to
+// the dispatcher context that earlier was stored in the stack frame.
+//
+
+ ldq t0, -CfFrameLength + CfA3(a1) // get dispatcher context address
+
+ ldl t1, DcControlPc(t0) // copy the entire dispatcher
+ ldl t2, DcFunctionEntry(t0) // context of the establisher
+ ldl t3, DcEstablisherFrame(t0) // frame...
+ ldl t4, DcContextRecord(t0) //
+
+ stl t1, DcControlPc(a3) // to the current dispatcher
+ stl t2, DcFunctionEntry(a3) // context (it's four words
+ stl t3, DcEstablisherFrame(a3) // long).
+ stl t4, DcContextRecord(a3) //
+
+ ldil v0, ExceptionCollidedUnwind // set disposition value
+ ret zero, (ra) // return
+
+//
+// Unwind is not in progress - return continue search disposition.
+//
+
+10: ldil v0, ExceptionContinueSearch // set disposition value
+ ret zero, (ra) // return
+
+ .end RtlpUnwindHandler
+
+ SBTTL("Execute Exception Filter")
+//++
+//
+// ULONG
+// RtlpExecuteExceptionFilter (
+// PEXCEPTION_POINTERS ExceptionPointers,
+// EXCEPTION_FILTER ExceptionFilter,
+// ULONG EstablisherFrame
+// )
+//
+// Routine Description:
+//
+// This function sets the static link and transfers control to the specified
+// exception filter routine.
+//
+// Arguments:
+//
+// ExceptionPointers (a0) - Supplies a pointer to the exception pointers
+// structure.
+//
+// ExceptionFilter (a1) - Supplies the address of the exception filter
+// routine.
+//
+// EstablisherFrame (a2) - Supplies the establisher frame pointer.
+//
+// Return Value:
+//
+// The value returned by the exception filter routine.
+//
+//--
+
+ LEAF_ENTRY(RtlpExecuteExceptionFilter)
+
+//
+// The protocol for calling exception filters used by the acc C-compiler is
+// that the uplevel frame pointer is passed in register v0 and the pointer
+// to the exception pointers structure is passed in register a0. The Gem
+// compiler expects the static link in t0. Here we do both.
+//
+
+ mov a2, v0 // set static link
+ mov a2, t0 // set alternate static link
+ jmp zero, (a1) // transfer control to exception filter
+
+ .end RtlpExecuteExceptionFilter
+
+ SBTTL("Execute Termination Handler")
+//++
+//
+// VOID
+// RtlpExecuteTerminationHandler (
+// BOOLEAN AbnormalTermination,
+// TERMINATION_HANDLER TerminationHandler,
+// ULONG EstablisherFrame
+// )
+//
+// Routine Description:
+//
+// This function sets the static link and transfers control to the specified
+// termination handler routine.
+//
+// Arguments:
+//
+// AbnormalTermination (a0) - Supplies a boolean value that determines
+// whether the termination is abnormal.
+//
+// TerminationHandler (a1) - Supplies the address of the termination handler
+// routine.
+//
+// EstablisherFrame (a2) - Supplies the establisher frame pointer.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlpExecuteTerminationHandler)
+
+//
+// The protocol for calling termination handlers used by the acc C-compiler
+// is that the uplevel frame pointer is passed in register v0 and the boolean
+// abnormal termination value is passed in register a0. The Gem compiler
+// expects the static link in t0. Here we do both.
+//
+
+ mov a2, v0 // set static link
+ mov a2, t0 // set alternate static link
+ jmp zero, (a1) // transfer control to termination handler
+
+ .end RtlpExecuteTerminationHandler
+
+ SBTTL("Get Stack Limits")
+//++
+//
+// VOID
+// RtlpGetStackLimits (
+// OUT PULONG LowLimit,
+// OUT PULONG HighLimit
+// )
+//
+// Routine Description:
+//
+// This function returns the current stack limits based on the current
+// processor mode.
+//
+// Arguments:
+//
+// LowLimit (a0) - Supplies a pointer to a variable that is to receive
+// the low limit of the stack.
+//
+// HighLimit (a1) - Supplies a pointer to a variable that is to receive
+// the high limit of the stack.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY(RtlpGetStackLimits)
+#if defined(NTOS_KERNEL_RUNTIME)
+
+//
+// Current mode is kernel - compute stack limits.
+//
+
+ GET_INITIAL_KERNEL_STACK // get initial kernel stack in v0
+
+ mov v0, t1 // copy high limit of kernel stack
+ GET_CURRENT_THREAD // get current thread in v0
+ ldl t2, ThStackLimit(v0) // get low limit of kernel stack
+#else
+
+//
+// Current mode is user - get stack limits from the TEB.
+//
+
+ GET_THREAD_ENVIRONMENT_BLOCK // get address of TEB in v0
+
+ ldl t1, TeStackBase(v0) // get high limit of user stack
+ ldl t2, TeStackLimit(v0) // get low limit of user stack
+#endif
+
+ stl t2, 0(a0) // store low stack limit
+ stl t1, 0(a1) // store high stack limit
+ ret zero, (ra) // return
+
+ .end RtlpGetStackLimits
diff --git a/private/ntos/rtl/assert.c b/private/ntos/rtl/assert.c
new file mode 100644
index 000000000..b3eb28b3c
--- /dev/null
+++ b/private/ntos/rtl/assert.c
@@ -0,0 +1,82 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ assert.c
+
+Abstract:
+
+ This module implements the RtlAssert function that is referenced by the
+ debugging version of the ASSERT macro defined in NTDEF.H
+
+Author:
+
+ Steve Wood (stevewo) 03-Oct-1989
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <zwapi.h>
+
+VOID
+RtlAssert(
+ IN PVOID FailedAssertion,
+ IN PVOID FileName,
+ IN ULONG LineNumber,
+ IN PCHAR Message OPTIONAL
+ )
+{
+#if DBG
+ char Response[ 2 ];
+ CONTEXT Context;
+
+#ifndef BLDR_KERNEL_RUNTIME
+ RtlCaptureContext( &Context );
+#endif
+
+ while (TRUE) {
+ DbgPrint( "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n",
+ Message ? Message : "",
+ FailedAssertion,
+ FileName,
+ LineNumber
+ );
+
+ DbgPrompt( "Break, Ignore, Terminate Process or Terminate Thread (bipt)? ",
+ Response,
+ sizeof( Response )
+ );
+ switch (Response[0]) {
+ case 'B':
+ case 'b':
+ DbgPrint( "Execute '!cxr %lx' to dump context\n",
+ &Context
+ );
+ DbgBreakPoint();
+ break;
+
+ case 'I':
+ case 'i':
+ return;
+
+ case 'P':
+ case 'p':
+ ZwTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL );
+ break;
+
+ case 'T':
+ case 't':
+ ZwTerminateThread( NtCurrentThread(), STATUS_UNSUCCESSFUL );
+ break;
+ }
+ }
+
+ DbgBreakPoint();
+ ZwTerminateProcess( NtCurrentProcess(), STATUS_UNSUCCESSFUL );
+#endif
+}
diff --git a/private/ntos/rtl/atom.c b/private/ntos/rtl/atom.c
new file mode 100644
index 000000000..b91e7e6ae
--- /dev/null
+++ b/private/ntos/rtl/atom.c
@@ -0,0 +1,911 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ atom.c
+
+Abstract:
+
+ This file contains the common code to implement atom tables. It is called
+ by both the user mode Win32 Atom API functions (Local/GlobalxxxAtom) and
+ by the kernel mode window manager code to access global atoms.
+
+Author:
+
+ Steve Wood (stevewo) 26-Oct-1990
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "atom.h"
+
+ULONG RtlpAtomAllocateTag;
+
+PVOID
+RtlpAllocateAtom(
+ IN ULONG NumberOfBytes
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ return ExAllocatePoolWithTag( PagedPool, NumberOfBytes, RtlpAtomAllocateTag );
+#else
+ return RtlAllocateHeap( RtlProcessHeap(), RtlpAtomAllocateTag, NumberOfBytes );
+#endif
+}
+
+
+void
+RtlpFreeAtom(
+ IN PVOID p
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ ExFreePool( p );
+#else
+ RtlFreeHeap( RtlProcessHeap(), 0, p );
+#endif
+ return;
+}
+
+
+void
+RtlpInitializeLockAtomTable(
+ IN OUT PRTL_ATOM_TABLE AtomTable
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ ExInitializeFastMutex( &AtomTable->FastMutex );
+#else
+ RtlInitializeCriticalSection( &AtomTable->CriticalSection );
+#endif
+ return;
+}
+
+BOOLEAN
+RtlpLockAtomTable(
+ IN PRTL_ATOM_TABLE AtomTable
+ )
+{
+ if (AtomTable == NULL || AtomTable->Signature != RTL_ATOM_TABLE_SIGNATURE) {
+ return FALSE;
+ }
+
+#if defined(NTOS_KERNEL_RUNTIME)
+ ExAcquireFastMutex( &AtomTable->FastMutex );
+#else
+ RtlEnterCriticalSection( &AtomTable->CriticalSection );
+#endif
+
+ return TRUE;
+}
+
+void
+RtlpUnlockAtomTable(
+ IN PRTL_ATOM_TABLE AtomTable
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ ExReleaseFastMutex( &AtomTable->FastMutex );
+#else
+ RtlLeaveCriticalSection( &AtomTable->CriticalSection );
+#endif
+}
+
+
+void
+RtlpDestroyLockAtomTable(
+ IN OUT PRTL_ATOM_TABLE AtomTable
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+#else
+ RtlDeleteCriticalSection( &AtomTable->CriticalSection );
+#endif
+}
+
+
+BOOLEAN
+RtlpInitializeHandleTableForAtomTable(
+ PRTL_ATOM_TABLE AtomTable
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ AtomTable->ExHandleTable = ExCreateHandleTable( NULL, 64, 32 );
+ if (AtomTable->ExHandleTable != NULL) {
+ //
+ // Make sure atom handle tables are NOT part of object handle enumeration
+ //
+
+ ExRemoveHandleTable( AtomTable->ExHandleTable );
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+#else
+ RtlInitializeHandleTable( (ULONG)(USHORT)~RTL_ATOM_MAXIMUM_INTEGER_ATOM,
+ sizeof( RTL_ATOM_HANDLE_TABLE_ENTRY ),
+ &AtomTable->RtlHandleTable
+ );
+ return TRUE;
+#endif
+}
+
+void
+RtlpDestroyHandleTableForAtomTable(
+ PRTL_ATOM_TABLE AtomTable
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ ExDestroyHandleTable( AtomTable->ExHandleTable, NULL );
+#else
+ RtlDestroyHandleTable( &AtomTable->RtlHandleTable );
+#endif
+ return;
+}
+
+PRTL_ATOM_TABLE_ENTRY
+RtlpAtomMapAtomToHandleEntry(
+ IN PRTL_ATOM_TABLE AtomTable,
+ IN ULONG HandleIndex
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ PHANDLE_ENTRY ExHandleEntry;
+ PRTL_ATOM_TABLE_ENTRY a;
+
+ ExHandleEntry = ExMapHandleToPointer( AtomTable->ExHandleTable,
+ INDEX_TO_HANDLE( HandleIndex ),
+ TRUE
+ );
+ if (ExHandleEntry != NULL) {
+ a = ExHandleEntry->Object;
+ ExUnlockHandleTableShared( AtomTable->ExHandleTable );
+ return a;
+ }
+#else
+ PRTL_ATOM_HANDLE_TABLE_ENTRY HandleEntry;
+
+ if (RtlIsValidIndexHandle( &AtomTable->RtlHandleTable,
+ HandleIndex,
+ (PRTL_HANDLE_TABLE_ENTRY *)&HandleEntry
+ )
+ ) {
+ return HandleEntry->Atom;
+ }
+#endif
+ return NULL;
+}
+
+BOOLEAN
+RtlpCreateHandleForAtom(
+ PRTL_ATOM_TABLE p,
+ PRTL_ATOM_TABLE_ENTRY a
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ HANDLE ExHandle;
+ HANDLE_ENTRY ExHandleEntry;
+
+ ExHandleEntry.Object = a;
+ ExHandleEntry.Attributes = 0;
+ ExHandle = ExCreateHandle( p->ExHandleTable, &ExHandleEntry );
+ if (ExHandle != NULL) {
+ a->HandleIndex = (USHORT)HANDLE_TO_INDEX( ExHandle );
+ a->Atom = (RTL_ATOM)((USHORT)a->HandleIndex | RTL_ATOM_MAXIMUM_INTEGER_ATOM);
+ return TRUE;
+ }
+#else
+ PRTL_ATOM_HANDLE_TABLE_ENTRY HandleEntry;
+ ULONG HandleIndex;
+
+ HandleEntry = (PRTL_ATOM_HANDLE_TABLE_ENTRY)RtlAllocateHandle( &p->RtlHandleTable,
+ &HandleIndex
+ );
+ if (HandleEntry != NULL) {
+ if (HandleIndex < RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ a->HandleIndex = (USHORT)HandleIndex;
+ a->Atom = (RTL_ATOM)((USHORT)HandleIndex | RTL_ATOM_MAXIMUM_INTEGER_ATOM);
+ HandleEntry->Atom = a;
+ HandleEntry->LockCount = 0;
+ HandleEntry->Flags = RTL_HANDLE_ALLOCATED;
+ return TRUE;
+ }
+
+ RtlFreeHandle( &p->RtlHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry );
+ }
+#endif
+ return FALSE;
+}
+
+void
+RtlpFreeHandleForAtom(
+ PRTL_ATOM_TABLE p,
+ PRTL_ATOM_TABLE_ENTRY a
+ )
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ ExDestroyHandle( p->ExHandleTable, INDEX_TO_HANDLE( a->HandleIndex ), FALSE );
+#else
+ PRTL_ATOM_HANDLE_TABLE_ENTRY HandleEntry;
+
+ if (RtlIsValidIndexHandle( &p->RtlHandleTable,
+ a->HandleIndex,
+ (PRTL_HANDLE_TABLE_ENTRY *)&HandleEntry
+ )
+ ) {
+ RtlFreeHandle( &p->RtlHandleTable, (PRTL_HANDLE_TABLE_ENTRY)HandleEntry );
+ }
+#endif
+ return;
+}
+
+NTSTATUS
+RtlInitializeAtomPackage(
+ IN ULONG AllocationTag
+ )
+{
+ RtlpAtomAllocateTag = AllocationTag;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RtlCreateAtomTable(
+ IN ULONG NumberOfBuckets,
+ OUT PVOID *AtomTableHandle
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p;
+ ULONG Size;
+
+ Status = STATUS_SUCCESS;
+ if (*AtomTableHandle == NULL) {
+ if (NumberOfBuckets <= 1) {
+ NumberOfBuckets = RTL_ATOM_TABLE_DEFAULT_NUMBER_OF_BUCKETS;
+ }
+
+ Size = sizeof( RTL_ATOM_TABLE ) +
+ (sizeof( RTL_ATOM_TABLE_ENTRY ) * (NumberOfBuckets-1));
+
+ p = (PRTL_ATOM_TABLE)RtlpAllocateAtom( Size );
+ if (p == NULL) {
+ Status = STATUS_NO_MEMORY;
+ }
+ else {
+ RtlZeroMemory( p, Size );
+ p->NumberOfBuckets = NumberOfBuckets;
+ if (RtlpInitializeHandleTableForAtomTable( p )) {
+ RtlpInitializeLockAtomTable( p );
+ p->Signature = RTL_ATOM_TABLE_SIGNATURE;
+ *AtomTableHandle = p;
+ }
+ else {
+ Status = STATUS_NO_MEMORY;
+ RtlpFreeAtom( p );
+ }
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+RtlDestroyAtomTable(
+ IN PVOID AtomTableHandle
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a, aNext, *pa;
+ ULONG i;
+
+ Status = STATUS_SUCCESS;
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ pa = &p->Buckets[ 0 ];
+ for (i=0; i<p->NumberOfBuckets; i++) {
+ aNext = *pa;
+ *pa++ = NULL;
+ while ((a = aNext) != NULL) {
+ aNext = a->HashLink;
+ a->HashLink = NULL;
+ RtlpFreeAtom( a );
+ }
+ }
+ p->Signature = 0;
+ RtlpUnlockAtomTable( p );
+
+ RtlpDestroyHandleTableForAtomTable( p );
+ RtlpDestroyLockAtomTable( p );
+ RtlZeroMemory( p, sizeof( RTL_ATOM_TABLE ) );
+ RtlpFreeAtom( p );
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ return Status;
+}
+
+NTSTATUS
+RtlEmptyAtomTable(
+ IN PVOID AtomTableHandle,
+ IN BOOLEAN IncludePinnedAtoms
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a, aNext, *pa, *pa1;
+ ULONG i;
+
+ Status = STATUS_SUCCESS;
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ pa = &p->Buckets[ 0 ];
+ for (i=0; i<p->NumberOfBuckets; i++) {
+ pa1 = pa++;
+ while ((a = *pa1) != NULL) {
+ if (IncludePinnedAtoms || !(a->Flags & RTL_ATOM_PINNED)) {
+ *pa1 = a->HashLink;
+ a->HashLink = NULL;
+ RtlpFreeHandleForAtom( p, a );
+ RtlpFreeAtom( a );
+ }
+ else {
+ pa1 = &a->HashLink;
+ }
+ }
+ }
+
+ RtlpUnlockAtomTable( p );
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ return Status;
+}
+
+BOOLEAN
+RtlpGetIntegerAtom(
+ PWSTR Name,
+ PRTL_ATOM Atom OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING UnicodeString;
+ PWSTR s;
+ ULONG n;
+ RTL_ATOM Temp;
+
+ if (((ULONG)Name & 0xFFFF0000) == 0) {
+ Temp = (RTL_ATOM)(USHORT)Name;
+ if (Temp >= RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ return FALSE;
+ }
+ else {
+ if (Temp == RTL_ATOM_INVALID_ATOM) {
+ Temp = RTL_ATOM_MAXIMUM_INTEGER_ATOM;
+ }
+
+ if (ARGUMENT_PRESENT( Atom )) {
+ *Atom = Temp;
+ }
+
+ return TRUE;
+ }
+ }
+ else
+ if (*Name != L'#') {
+ return FALSE;
+ }
+
+ s = ++Name;
+ while (*s != UNICODE_NULL) {
+ if (*s < L'0' || *s > L'9') {
+ return FALSE;
+ }
+ else {
+ s++;
+ }
+ }
+
+ n = 0;
+ UnicodeString.Buffer = Name;
+ UnicodeString.Length = (USHORT)((PCHAR)s - (PCHAR)Name);
+ UnicodeString.MaximumLength = UnicodeString.Length;
+ Status = RtlUnicodeStringToInteger( &UnicodeString, 10, &n );
+ if (NT_SUCCESS( Status )) {
+ if (ARGUMENT_PRESENT( Atom )) {
+ if (n == 0 || n > RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ *Atom = RTL_ATOM_MAXIMUM_INTEGER_ATOM;
+ }
+ else {
+ *Atom = (RTL_ATOM)n;
+ }
+ }
+
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+PRTL_ATOM_TABLE_ENTRY
+RtlpHashStringToAtom(
+ IN PRTL_ATOM_TABLE p,
+ IN PWSTR Name,
+ OUT PRTL_ATOM_TABLE_ENTRY **PreviousAtom OPTIONAL,
+ OUT PULONG NameLength
+ )
+{
+ ULONG Length, Hash;
+ WCHAR c;
+ PWCH s;
+ RTL_ATOM Atom;
+ PRTL_ATOM_TABLE_ENTRY *pa, a;
+
+ if (((ULONG)Name & 0xFFFF0000) == 0) {
+ Atom = (RTL_ATOM)(USHORT)Name;
+ a = NULL;
+ if (Atom >= RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ a = RtlpAtomMapAtomToHandleEntry( p,
+ (ULONG)(Atom & (USHORT)~RTL_ATOM_MAXIMUM_INTEGER_ATOM)
+ );
+ }
+
+ if (ARGUMENT_PRESENT( PreviousAtom )) {
+ *PreviousAtom = NULL;
+ }
+
+ return a;
+ }
+
+ s = Name;
+ Hash = 0;
+ while (*s != UNICODE_NULL) {
+ c = RtlUpcaseUnicodeChar( *s++ );
+ Hash = Hash + (c << 1) + (c >> 1) + c;
+ }
+ Length = s - Name;
+ if (Length > RTL_ATOM_MAXIMUM_NAME_LENGTH) {
+ pa = NULL;
+ }
+ else {
+ pa = &p->Buckets[ Hash % p->NumberOfBuckets ];
+ while (a = *pa) {
+ if (a->NameLength == Length && !_wcsicmp( a->Name, Name )) {
+ break;
+ }
+ else {
+ pa = &a->HashLink;
+ }
+ }
+ }
+
+ if (ARGUMENT_PRESENT( PreviousAtom )) {
+ *PreviousAtom = pa;
+ }
+
+ if (a == NULL && ARGUMENT_PRESENT( NameLength )) {
+ *NameLength = Length * sizeof( WCHAR );
+ }
+
+ return a;
+}
+
+
+NTSTATUS
+RtlAddAtomToAtomTable(
+ IN PVOID AtomTableHandle,
+ IN PWSTR AtomName OPTIONAL,
+ IN OUT PRTL_ATOM Atom OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a, *pa;
+ ULONG NameLength;
+ RTL_ATOM Temp;
+
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ if (RtlpGetIntegerAtom( AtomName, &Temp )) {
+ if (Temp >= RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ Temp = RTL_ATOM_INVALID_ATOM;
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ }
+
+ if (ARGUMENT_PRESENT( Atom )) {
+ *Atom = Temp;
+ }
+ }
+ else
+ if (*AtomName == UNICODE_NULL) {
+ Status = STATUS_OBJECT_NAME_INVALID;
+ }
+ else {
+ a = RtlpHashStringToAtom( p, AtomName, &pa, &NameLength );
+ if (a == NULL) {
+ if (pa != NULL) {
+ Status = STATUS_NO_MEMORY;
+ a = RtlpAllocateAtom( FIELD_OFFSET( RTL_ATOM_TABLE_ENTRY, Name ) +
+ NameLength + sizeof( UNICODE_NULL )
+ );
+ if (a != NULL) {
+ a->HashLink = NULL;
+ a->ReferenceCount = 1;
+ a->Flags = 0;
+ RtlMoveMemory( a->Name, AtomName, NameLength );
+ a->NameLength = (UCHAR)(NameLength / sizeof( WCHAR ));
+ a->Name[ a->NameLength ] = UNICODE_NULL;
+ if (RtlpCreateHandleForAtom( p, a )) {
+ a->Atom = (RTL_ATOM)a->HandleIndex | RTL_ATOM_MAXIMUM_INTEGER_ATOM;
+ *pa = a;
+ if (ARGUMENT_PRESENT( Atom )) {
+ *Atom = a->Atom;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+ else {
+ RtlpFreeAtom( a );
+ }
+ }
+ }
+ else {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+ else {
+ if (!(a->Flags & RTL_ATOM_PINNED)) {
+ if (a->ReferenceCount == 0xFFFF) {
+ KdPrint(( "RTL: Pinning atom (%x) as reference count about to wrap\n", Atom ));
+ a->Flags |= RTL_ATOM_PINNED;
+ }
+ else {
+ a->ReferenceCount += 1;
+ }
+ }
+
+ if (ARGUMENT_PRESENT( Atom )) {
+ *Atom = a->Atom;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ RtlpUnlockAtomTable( p );
+
+ return Status;
+}
+
+NTSTATUS
+RtlLookupAtomInAtomTable(
+ IN PVOID AtomTableHandle,
+ IN PWSTR AtomName,
+ OUT PRTL_ATOM Atom OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a;
+ RTL_ATOM Temp;
+
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ if (RtlpGetIntegerAtom( AtomName, &Temp )) {
+ if (Temp >= RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ Temp = RTL_ATOM_INVALID_ATOM;
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ }
+
+ if (ARGUMENT_PRESENT( Atom )) {
+ *Atom = Temp;
+ }
+ }
+ else
+ if (*AtomName == UNICODE_NULL) {
+ Status = STATUS_OBJECT_NAME_INVALID;
+ }
+ else {
+ a = RtlpHashStringToAtom( p, AtomName, NULL, NULL );
+ if (a == NULL) {
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ else {
+ if (RtlpAtomMapAtomToHandleEntry( p, (ULONG)a->HandleIndex ) != NULL) {
+ Status = STATUS_SUCCESS;
+ if (ARGUMENT_PRESENT( Atom )) {
+ *Atom = a->Atom;
+ }
+ }
+ else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+ }
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ RtlpUnlockAtomTable( p );
+
+ return Status;
+}
+
+
+NTSTATUS
+RtlDeleteAtomFromAtomTable(
+ IN PVOID AtomTableHandle,
+ IN RTL_ATOM Atom
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a, *pa;
+
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ Status = STATUS_INVALID_HANDLE;
+ if (Atom >= RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ a = RtlpAtomMapAtomToHandleEntry( p,
+ (ULONG)(Atom & (USHORT)~RTL_ATOM_MAXIMUM_INTEGER_ATOM)
+ );
+ if (a != NULL && a->Atom == Atom) {
+ Status = STATUS_SUCCESS;
+ if (a->Flags & RTL_ATOM_PINNED) {
+ KdPrint(( "RTL: Ignoring attempt to delete a pinned atom (%x)\n", Atom ));
+ Status = STATUS_WAS_LOCKED; // This is a success status code!
+ }
+ else
+ if (--a->ReferenceCount == 0) {
+ a = RtlpHashStringToAtom( p, a->Name, &pa, NULL );
+ if (a != NULL) {
+ *pa = a->HashLink;
+ RtlpFreeHandleForAtom( p, a );
+ RtlpFreeAtom( a );
+ }
+ }
+ }
+ }
+ else
+ if (Atom != RTL_ATOM_INVALID_ATOM) {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ RtlpUnlockAtomTable( p );
+
+ return Status;
+}
+
+NTSTATUS
+RtlPinAtomInAtomTable(
+ IN PVOID AtomTableHandle,
+ IN RTL_ATOM Atom
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a, *pa;
+
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ Status = STATUS_INVALID_HANDLE;
+ if (Atom >= RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ a = RtlpAtomMapAtomToHandleEntry( p,
+ (ULONG)(Atom & (USHORT)~RTL_ATOM_MAXIMUM_INTEGER_ATOM)
+ );
+ if (a != NULL && a->Atom == Atom) {
+ Status = STATUS_SUCCESS;
+ a->Flags |= RTL_ATOM_PINNED;
+ }
+ }
+ else
+ if (Atom != RTL_ATOM_INVALID_ATOM) {
+ Status = STATUS_SUCCESS;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ RtlpUnlockAtomTable( p );
+
+ return Status;
+}
+
+NTSTATUS
+RtlQueryAtomInAtomTable(
+ IN PVOID AtomTableHandle,
+ IN RTL_ATOM Atom,
+ OUT PULONG AtomUsage OPTIONAL,
+ OUT PULONG AtomFlags OPTIONAL,
+ IN OUT PWSTR AtomName OPTIONAL,
+ IN OUT PULONG AtomNameLength OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a;
+ WCHAR AtomNameBuffer[ 16 ];
+ ULONG CopyLength;
+
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ try {
+ if (Atom < RTL_ATOM_MAXIMUM_INTEGER_ATOM) {
+ if (Atom == RTL_ATOM_INVALID_ATOM) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ if (ARGUMENT_PRESENT( AtomUsage )) {
+ *AtomUsage = 1;
+ }
+
+ if (ARGUMENT_PRESENT( AtomFlags )) {
+ *AtomFlags = RTL_ATOM_PINNED;
+ }
+
+ if (ARGUMENT_PRESENT( AtomName )) {
+ CopyLength = _snwprintf( AtomNameBuffer,
+ sizeof( AtomNameBuffer ) / sizeof( WCHAR ),
+ L"#%u",
+ Atom
+ ) * sizeof( WCHAR );
+ if (CopyLength >= *AtomNameLength) {
+ if (*AtomNameLength >= sizeof( UNICODE_NULL )) {
+ CopyLength = *AtomNameLength - sizeof( UNICODE_NULL );
+ }
+ else {
+ CopyLength = 0;
+ }
+ }
+
+ if (CopyLength != 0) {
+ RtlMoveMemory( AtomName, AtomNameBuffer, CopyLength );
+ AtomName[ CopyLength / sizeof( WCHAR ) ] = UNICODE_NULL;
+ *AtomNameLength = CopyLength;
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ }
+ }
+ else {
+ a = RtlpAtomMapAtomToHandleEntry( p,
+ (ULONG)(Atom & (USHORT)~RTL_ATOM_MAXIMUM_INTEGER_ATOM)
+ );
+ if (a != NULL && a->Atom == Atom) {
+ Status = STATUS_SUCCESS;
+ if (ARGUMENT_PRESENT( AtomUsage )) {
+ *AtomUsage = a->ReferenceCount;
+ }
+
+ if (ARGUMENT_PRESENT( AtomFlags )) {
+ *AtomFlags = a->Flags;
+ }
+
+ if (ARGUMENT_PRESENT( AtomName )) {
+ //
+ // Fill in as much of the atom string as possible, and
+ // always zero terminate. This is what win3.1 does.
+ //
+
+ CopyLength = a->NameLength * sizeof( WCHAR );
+ if (CopyLength >= *AtomNameLength) {
+ if (*AtomNameLength >= sizeof( UNICODE_NULL )) {
+ CopyLength = *AtomNameLength - sizeof( UNICODE_NULL );
+ }
+ else {
+ CopyLength = 0;
+ }
+ }
+ if (CopyLength != 0) {
+ RtlMoveMemory( AtomName, a->Name, CopyLength );
+ AtomName[ CopyLength / sizeof( WCHAR ) ] = UNICODE_NULL;
+ *AtomNameLength = CopyLength;
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ }
+ else {
+ Status = STATUS_INVALID_HANDLE;
+ }
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ RtlpUnlockAtomTable( p );
+
+ return Status;
+}
+
+NTSTATUS
+RtlQueryAtomsInAtomTable(
+ IN PVOID AtomTableHandle,
+ IN ULONG MaximumNumberOfAtoms,
+ OUT PULONG NumberOfAtoms,
+ OUT PRTL_ATOM Atoms
+ )
+{
+ NTSTATUS Status;
+ PRTL_ATOM_TABLE p = (PRTL_ATOM_TABLE)AtomTableHandle;
+ PRTL_ATOM_TABLE_ENTRY a;
+ ULONG i;
+ ULONG CurrentAtomIndex;
+
+ if (!RtlpLockAtomTable( p )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Status = STATUS_SUCCESS;
+ try {
+ CurrentAtomIndex = 0;
+ for (i=0; i<p->NumberOfBuckets; i++) {
+ a = p->Buckets[ i ];
+ while (a) {
+ if (CurrentAtomIndex < MaximumNumberOfAtoms) {
+ Atoms[ CurrentAtomIndex ] = a->Atom;
+ }
+ else {
+ Status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ CurrentAtomIndex += 1;
+ a = a->HashLink;
+ }
+ }
+
+ *NumberOfAtoms = CurrentAtomIndex;
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ RtlpUnlockAtomTable( p );
+
+ return Status;
+}
diff --git a/private/ntos/rtl/bitmap.c b/private/ntos/rtl/bitmap.c
new file mode 100644
index 000000000..3e4235ce2
--- /dev/null
+++ b/private/ntos/rtl/bitmap.c
@@ -0,0 +1,2975 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ BitMap.c
+
+Abstract:
+
+ Implementation of the bit map routines for the NT rtl.
+
+ Bit numbers within the bit map are zero based. The first is numbered
+ zero.
+
+ The bit map routines keep track of the number of bits clear or set by
+ subtracting or adding the number of bits operated on as bit ranges
+ are cleared or set; individual bit states are not tested.
+ This means that if a range of bits is set,
+ it is assumed that the total range is currently clear.
+
+Author:
+
+ Gary Kimura (GaryKi) & Lou Perazzoli (LouP) 29-Jan-1990
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlInitializeBitMap)
+#endif
+
+#define RightShiftUlong(E1,E2) ((E2) < 32 ? (E1) >> (E2) : 0)
+#define LeftShiftUlong(E1,E2) ((E2) < 32 ? (E1) << (E2) : 0)
+
+#if DBG
+VOID
+DumpBitMap (
+ PRTL_BITMAP BitMap
+ )
+{
+ ULONG i;
+ BOOLEAN AllZeros, AllOnes;
+
+ DbgPrint(" BitMap:%08lx", BitMap);
+
+ KdPrint((" (%08x)", BitMap->SizeOfBitMap));
+ KdPrint((" %08lx\n", BitMap->Buffer));
+
+ AllZeros = FALSE;
+ AllOnes = FALSE;
+
+ for (i = 0; i < ((BitMap->SizeOfBitMap + 31) / 32); i += 1) {
+
+ if (BitMap->Buffer[i] == 0) {
+
+ if (AllZeros) {
+
+ NOTHING;
+
+ } else {
+
+ DbgPrint("%4d:", i);
+ DbgPrint(" %08lx\n", BitMap->Buffer[i]);
+ }
+
+ AllZeros = TRUE;
+
+ } else if (BitMap->Buffer[i] == 0xFFFFFFFF) {
+
+ if (AllOnes) {
+
+ NOTHING;
+
+ } else {
+
+ DbgPrint("%4d:", i);
+ DbgPrint(" %08lx\n", BitMap->Buffer[i]);
+ }
+
+ AllOnes = TRUE;
+
+ } else {
+
+ AllZeros = FALSE;
+ AllOnes = FALSE;
+
+ DbgPrint("%4d:", i);
+ DbgPrint(" %08lx\n", BitMap->Buffer[i]);
+ }
+ }
+}
+
+#endif
+
+
+//
+// There are three macros to make reading the bytes in a bitmap easier.
+//
+
+#define GET_BYTE_DECLARATIONS() \
+ PUCHAR _CURRENT_POSITION;
+
+#define GET_BYTE_INITIALIZATION(RTL_BITMAP,BYTE_INDEX) { \
+ _CURRENT_POSITION = &((PUCHAR)((RTL_BITMAP)->Buffer))[BYTE_INDEX]; \
+}
+
+#define GET_BYTE(THIS_BYTE) ( \
+ THIS_BYTE = *(_CURRENT_POSITION++) \
+)
+
+
+//
+// Lookup table that tells how many contiguous bits are clear (i.e., 0) in
+// a byte
+//
+
+CCHAR RtlpBitsClearAnywhere[] =
+ { 8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,
+ 4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 5,4,3,3,2,2,2,2,3,2,2,2,2,2,2,2,
+ 4,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,
+ 6,5,4,4,3,3,3,3,3,2,2,2,2,2,2,2,
+ 4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
+ 5,4,3,3,2,2,2,2,3,2,1,1,2,1,1,1,
+ 4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
+ 7,6,5,5,4,4,4,4,3,3,3,3,3,3,3,3,
+ 4,3,2,2,2,2,2,2,3,2,2,2,2,2,2,2,
+ 5,4,3,3,2,2,2,2,3,2,1,1,2,1,1,1,
+ 4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
+ 6,5,4,4,3,3,3,3,3,2,2,2,2,2,2,2,
+ 4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,1,
+ 5,4,3,3,2,2,2,2,3,2,1,1,2,1,1,1,
+ 4,3,2,2,2,1,1,1,3,2,1,1,2,1,1,0 };
+
+//
+// Lookup table that tells how many contiguous LOW order bits are clear
+// (i.e., 0) in a byte
+//
+
+CCHAR RtlpBitsClearLow[] =
+ { 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 };
+
+//
+// Lookup table that tells how many contiguous HIGH order bits are clear
+// (i.e., 0) in a byte
+//
+
+CCHAR RtlpBitsClearHigh[] =
+ { 8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,
+ 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+//
+// Lookup table that tells how many clear bits (i.e., 0) there are in a byte
+//
+
+CCHAR RtlpBitsClearTotal[] =
+ { 8,7,7,6,7,6,6,5,7,6,6,5,6,5,5,4,
+ 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,
+ 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,
+ 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,
+ 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,
+ 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,
+ 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,
+ 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1,
+ 7,6,6,5,6,5,5,4,6,5,5,4,5,4,4,3,
+ 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,
+ 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,
+ 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1,
+ 6,5,5,4,5,4,4,3,5,4,4,3,4,3,3,2,
+ 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1,
+ 5,4,4,3,4,3,3,2,4,3,3,2,3,2,2,1,
+ 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 };
+
+//
+// Bit Mask for clearing and setting bits within bytes
+//
+
+static UCHAR FillMask[] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
+
+static UCHAR ZeroMask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 };
+
+
+VOID
+RtlInitializeBitMap (
+ IN PRTL_BITMAP BitMapHeader,
+ IN PULONG BitMapBuffer,
+ IN ULONG SizeOfBitMap
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure initializes a bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the BitMap Header to initialize
+
+ BitMapBuffer - Supplies a pointer to the buffer that is to serve as the
+ BitMap. This must be an a multiple number of longwords in size.
+
+ SizeOfBitMap - Supplies the number of bits required in the Bit Map.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Initialize the BitMap header.
+ //
+
+ BitMapHeader->SizeOfBitMap = SizeOfBitMap;
+ BitMapHeader->Buffer = BitMapBuffer;
+
+ //
+ // And return to our caller
+ //
+
+ //DbgPrint("InitializeBitMap"); DumpBitMap(BitMapHeader);
+ return;
+}
+
+
+VOID
+RtlClearAllBits (
+ IN PRTL_BITMAP BitMapHeader
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure clears all bits in the specified Bit Map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Clear all the bits
+ //
+
+ RtlZeroMemory( BitMapHeader->Buffer,
+ ((BitMapHeader->SizeOfBitMap + 31) / 32) * 4
+ );
+
+ //
+ // And return to our caller
+ //
+
+ //DbgPrint("ClearAllBits"); DumpBitMap(BitMapHeader);
+ return;
+}
+
+
+VOID
+RtlSetAllBits (
+ IN PRTL_BITMAP BitMapHeader
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure sets all bits in the specified Bit Map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Set all the bits
+ //
+
+ RtlFillMemoryUlong( BitMapHeader->Buffer,
+ ((BitMapHeader->SizeOfBitMap + 31) / 32) * 4,
+ 0xffffffff
+ );
+
+ //
+ // And return to our caller
+ //
+
+ //DbgPrint("SetAllBits"); DumpBitMap(BitMapHeader);
+ return;
+}
+
+
+ULONG
+RtlFindClearBits (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG NumberToFind,
+ IN ULONG HintIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure searches the specified bit map for the specified
+ contiguous region of clear bits. If a run is not found from the
+ hint to the end of the bitmap, we will search again from the
+ beginning of the bitmap.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ NumberToFind - Supplies the size of the contiguous region to find.
+
+ HintIndex - Supplies the index (zero based) of where we should start
+ the search from within the bitmap.
+
+Return Value:
+
+ ULONG - Receives the starting index (zero based) of the contiguous
+ region of clear bits found. If not such a region cannot be found
+ a -1 (i.e. 0xffffffff) is returned.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG HintBit;
+ ULONG MainLoopIndex;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // To make the loops in our test run faster we'll extract the
+ // fields from the bitmap header
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we won't count them. We do
+ // this by first checking if there is any odd bits in the last byte.
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 1's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] |=
+ ZeroMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Calculate from the hint index where the hint byte is and set ourselves
+ // up to read the hint on the next call to GET_BYTE. To make the
+ // algorithm run fast we'll only honor hints down to the byte level of
+ // granularity. There is a possibility that we'll need to execute
+ // our main logic twice. Once to test from the hint byte to the end of
+ // the bitmap and the other to test from the start of the bitmap. First
+ // we need to make sure the Hint Index is within range.
+ //
+
+ if (HintIndex >= SizeOfBitMap) {
+
+ HintIndex = 0;
+ }
+
+ HintBit = HintIndex % 8;
+
+ for (MainLoopIndex = 0; MainLoopIndex < 2; MainLoopIndex += 1) {
+
+ ULONG StartByteIndex;
+ ULONG EndByteIndex;
+
+ UCHAR CurrentByte;
+
+ //
+ // Check for the first time through the main loop, which indicates
+ // that we are going to start our search at our hint byte
+ //
+
+ if (MainLoopIndex == 0) {
+
+ StartByteIndex = HintIndex / 8;
+ EndByteIndex = SizeInBytes;
+
+ //
+ // This is the second time through the loop, make sure there is
+ // actually something to check before the hint byte
+ //
+
+ } else if (HintIndex != 0) {
+
+ //
+ // The end index for the second time around is based on the
+ // number of bits we need to find. We need to use this inorder
+ // to take the case where the preceding byte to the hint byte
+ // is the start of our run, and the run includes the hint byte
+ // and some following bytes, based on the number of bits needed
+ // The computation is to take the number of bits needed minus
+ // 2 divided by 8 and then add 2. This will take in to account
+ // the worst possible case where we have one bit hanging off
+ // of each end byte, and all intervening bytes are all zero.
+ //
+
+ if (NumberToFind < 2) {
+
+ EndByteIndex = 0;
+
+ } else {
+
+ EndByteIndex = (HintIndex / 8) + ((NumberToFind - 2) / 8) + 2;
+
+ //
+ // Make sure we don't overrun the end of the bitmap
+ //
+
+ if (EndByteIndex > SizeInBytes) {
+
+ EndByteIndex = SizeInBytes;
+ }
+ }
+
+ HintIndex = 0;
+ HintBit = 0;
+ StartByteIndex = 0;
+
+ //
+ // Otherwise we already did a complete loop through the bitmap
+ // so we should simply return -1 to say nothing was found
+ //
+
+ } else {
+
+ return 0xffffffff;
+ }
+
+ //
+ // Set ourselves up to get the next byte
+ //
+
+ GET_BYTE_INITIALIZATION(BitMapHeader, StartByteIndex);
+
+ //
+ // Get the first byte, and set any bits before the hint bit.
+ //
+
+ GET_BYTE( CurrentByte );
+
+ CurrentByte |= FillMask[HintBit];
+
+ //
+ // If the number of bits can only fit in 1 or 2 bytes (i.e., 9 bits or
+ // less) we do the following test case.
+ //
+
+ if (NumberToFind <= 9) {
+
+ ULONG CurrentBitIndex;
+ UCHAR PreviousByte;
+
+ PreviousByte = 0xff;
+
+ //
+ // Examine all the bytes within our test range searching
+ // for a fit
+ //
+
+ CurrentBitIndex = StartByteIndex * 8;
+
+ while (TRUE) {
+
+ //
+ // If this is the first itteration of the loop, mask Current
+ // byte with the real hint.
+ //
+
+ //
+ // The current byte does not satisfy our requirements so we'll
+ // check the previous and current byte together to see if
+ // we'll fit. To check uses the high part of the previous
+ // byte and low part of the current byte.
+ //
+
+ if (((ULONG)RtlpBitsClearHigh[PreviousByte] +
+ (ULONG)RtlpBitsClearLow[CurrentByte]) >= NumberToFind) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these two bytes, so we can compute
+ // the starting index. This is done by taking the
+ // index of the current byte (bit 0) and subtracting the
+ // number of bits its takes to get to the first cleared
+ // high bit.
+ //
+
+ StartingIndex = CurrentBitIndex -
+ (LONG)RtlpBitsClearHigh[PreviousByte];
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // Check to see if a single byte will satisfy the requirement
+ //
+
+ if ((ULONG)RtlpBitsClearAnywhere[CurrentByte] >= NumberToFind) {
+
+ UCHAR BitMask;
+ ULONG i;
+
+ //
+ // It all fits in a single byte, so calculate the bit
+ // number. We do this by taking a mask of the appropriate
+ // size and shifting it over until it fits. It fits when
+ // we can bitwise-and the current byte with the bitmask
+ // and get a zero back.
+ //
+
+ BitMask = FillMask[ NumberToFind ];
+ for (i = 0; (BitMask & CurrentByte) != 0; i += 1) {
+
+ BitMask <<= 1;
+ }
+
+ //
+ // return to our caller the located bit index, and the
+ // number that we found.
+ //
+
+ return CurrentBitIndex + i;
+ }
+
+ //
+ // For the next iteration through our loop we need to make
+ // the current byte into the previous byte, and go to the
+ // top of the loop again.
+ //
+
+ PreviousByte = CurrentByte;
+
+ //
+ // Increment our Bit Index, and either exit, or get the
+ // next byte.
+ //
+
+ CurrentBitIndex += 8;
+
+ if ( CurrentBitIndex < EndByteIndex * 8 ) {
+
+ GET_BYTE( CurrentByte );
+
+ } else {
+
+ break;
+ }
+
+ } // end loop CurrentBitIndex
+
+ //
+ // The number to find is greater than 9 but if it is less than 15
+ // then we know it can be satisfied with at most 2 bytes, or 3 bytes
+ // if the middle byte (of the 3) is all zeros.
+ //
+
+ } else if (NumberToFind < 15) {
+
+ ULONG CurrentBitIndex;
+
+ UCHAR PreviousPreviousByte;
+ UCHAR PreviousByte;
+
+ PreviousPreviousByte = 0xff;
+ PreviousByte = 0xff;
+
+ //
+ // Examine all the bytes within our test range searching
+ // for a fit
+ //
+
+ CurrentBitIndex = StartByteIndex * 8;
+
+ while (TRUE) {
+
+ //
+ // Check to see if the Previous byte and current byte
+ // together satisfy the request.
+ //
+
+ if (((ULONG)RtlpBitsClearHigh[PreviousByte] +
+ (ULONG)RtlpBitsClearLow[CurrentByte]) >= NumberToFind) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these two bytes, so we can compute
+ // the starting index. This is done by taking the
+ // index of the current byte (bit 0) and subtracting the
+ // number of bits its takes to get to the first cleared
+ // high bit.
+ //
+
+ StartingIndex = CurrentBitIndex -
+ (LONG)RtlpBitsClearHigh[PreviousByte];
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // if the previous byte is all zeros then maybe the
+ // request can be satisfied using the Previous Previous Byte
+ // Previous Byte, and the Current Byte.
+ //
+
+ if ((PreviousByte == 0)
+
+ &&
+
+ (((ULONG)RtlpBitsClearHigh[PreviousPreviousByte] + 8 +
+ (ULONG)RtlpBitsClearLow[CurrentByte]) >= NumberToFind)) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these three bytes, so we can compute
+ // the starting index. This is done by taking the
+ // index of the previous byte (bit 0) and subtracting
+ // the number of bits its takes to get to the first
+ // cleared high bit.
+ //
+
+ StartingIndex = (CurrentBitIndex - 8) -
+ (LONG)RtlpBitsClearHigh[PreviousPreviousByte];
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // For the next iteration through our loop we need to make
+ // the current byte into the previous byte, the previous
+ // byte into the previous previous byte, and go to the
+ // top of the loop again.
+ //
+
+ PreviousPreviousByte = PreviousByte;
+ PreviousByte = CurrentByte;
+
+ //
+ // Increment our Bit Index, and either exit, or get the
+ // next byte.
+ //
+
+ CurrentBitIndex += 8;
+
+ if ( CurrentBitIndex < EndByteIndex * 8 ) {
+
+ GET_BYTE( CurrentByte );
+
+ } else {
+
+ break;
+ }
+
+ } // end loop CurrentBitIndex
+
+ //
+ // The number to find is greater than or equal to 15. This request
+ // has to have at least one byte of all zeros to be satisfied
+ //
+
+ } else {
+
+ ULONG CurrentByteIndex;
+
+ ULONG ZeroBytesNeeded;
+ ULONG ZeroBytesFound;
+
+ UCHAR StartOfRunByte;
+ LONG StartOfRunIndex;
+
+ //
+ // First precalculate how many zero bytes we're going to need
+ //
+
+ ZeroBytesNeeded = (NumberToFind - 7) / 8;
+
+ //
+ // Indicate for the first time through our loop that we haven't
+ // found a zero byte yet, and indicate that the start of the
+ // run is the byte just before the start byte index
+ //
+
+ ZeroBytesFound = 0;
+ StartOfRunByte = 0xff;
+ StartOfRunIndex = StartByteIndex - 1;
+
+ //
+ // Examine all the bytes in our test range searching for a fit
+ //
+
+ CurrentByteIndex = StartByteIndex;
+
+ while (TRUE) {
+
+ //
+ // If the number of zero bytes fits our minimum requirements
+ // then we can do the additional test to see if we
+ // actually found a fit
+ //
+
+ if ((ZeroBytesFound >= ZeroBytesNeeded)
+
+ &&
+
+ ((ULONG)RtlpBitsClearHigh[StartOfRunByte] + ZeroBytesFound*8 +
+ (ULONG)RtlpBitsClearLow[CurrentByte]) >= NumberToFind) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these bytes, so we can compute
+ // the starting index. This is done by taking the
+ // StartOfRunIndex times 8 and adding the number of bits
+ // it takes to get to the first cleared high bit.
+ //
+
+ StartingIndex = (StartOfRunIndex * 8) +
+ (8 - (LONG)RtlpBitsClearHigh[StartOfRunByte]);
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // Check to see if the byte is zero and increment
+ // the number of zero bytes found
+ //
+
+ if (CurrentByte == 0) {
+
+ ZeroBytesFound += 1;
+
+ //
+ // The byte isn't a zero so we need to start over again
+ // looking for zero bytes.
+ //
+
+ } else {
+
+ ZeroBytesFound = 0;
+ StartOfRunByte = CurrentByte;
+ StartOfRunIndex = CurrentByteIndex;
+ }
+
+ //
+ // Increment our Byte Index, and either exit, or get the
+ // next byte.
+ //
+
+ CurrentByteIndex += 1;
+
+ if ( CurrentByteIndex < EndByteIndex ) {
+
+ GET_BYTE( CurrentByte );
+
+ } else {
+
+ break;
+ }
+
+ } // end loop CurrentByteIndex
+ }
+ }
+
+ //
+ // We never found a fit so we'll return -1
+ //
+
+ return 0xffffffff;
+}
+
+
+ULONG
+RtlFindSetBits (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG NumberToFind,
+ IN ULONG HintIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure searches the specified bit map for the specified
+ contiguous region of set bits.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ NumberToFind - Supplies the size of the contiguous region to find.
+
+ HintIndex - Supplies the index (zero based) of where we should start
+ the search from within the bitmap.
+
+Return Value:
+
+ ULONG - Receives the starting index (zero based) of the contiguous
+ region of set bits found. If such a region cannot be found then
+ a -1 (i.e., 0xffffffff) is returned.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG HintByte;
+
+ ULONG MainLoopIndex;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // To make the loops in our test run faster we'll extract the
+ // fields from the bitmap header
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we won't count them. We do
+ // this by first checking if there is any odd bits in the last byte.
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 0's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] &=
+ FillMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Calculate from the hint index where the hint byte is and set ourselves
+ // up to read the hint on the next call to GET_BYTE. To make the
+ // algorithm run fast we'll only honor hints down to the byte level of
+ // granularity. There is a possibility that we'll need to execute
+ // our main logic twice. Once to test from the hint byte to the end of
+ // the bitmap and the other to test from the start of the bitmap. First
+ // we need to make sure the Hint Index is within range.
+ //
+
+ if (HintIndex >= SizeOfBitMap) {
+
+ HintIndex = 0;
+ }
+
+ HintByte = HintIndex / 8;
+
+ for (MainLoopIndex = 0; MainLoopIndex < 2; MainLoopIndex += 1) {
+
+ ULONG StartByteIndex;
+ ULONG EndByteIndex;
+
+ //
+ // Check for the first time through the main loop, which indicates
+ // that we are going to start our search at our hint byte
+ //
+
+ if (MainLoopIndex == 0) {
+
+ StartByteIndex = HintByte;
+ EndByteIndex = SizeInBytes;
+
+ //
+ // This is the second time through the loop, make sure there is
+ // actually something to check before the hint byte
+ //
+
+ } else if (HintByte != 0) {
+
+ StartByteIndex = 0;
+
+ //
+ // The end index for the second time around is based on the
+ // number of bits we need to find. We need to use this inorder
+ // to take the case where the preceding byte to the hint byte
+ // is the start of our run, and the run includes the hint byte
+ // and some following bytes, based on the number of bits needed
+ // The computation is to take the number of bits needed minus
+ // 2 divided by 8 and then add 2. This will take in to account
+ // the worst possible case where we have one bit hanging off
+ // of each end byte, and all intervening bytes are all zero.
+ // We only need to add one in the following equation because
+ // HintByte is already counted.
+ //
+
+ if (NumberToFind < 2) {
+
+ EndByteIndex = HintByte;
+
+ } else {
+
+ EndByteIndex = HintByte + ((NumberToFind - 2) / 8) + 1;
+
+ //
+ // Make sure we don't overrun the end of the bitmap
+ //
+
+ if (EndByteIndex > SizeInBytes) {
+
+ EndByteIndex = SizeInBytes;
+ }
+ }
+
+ //
+ // Otherwise we already did a complete loop through the bitmap
+ // so we should simply return -1 to say nothing was found
+ //
+
+ } else {
+
+ return 0xffffffff;
+
+ }
+
+ //
+ // Set ourselves up to get the next byte
+ //
+
+ GET_BYTE_INITIALIZATION(BitMapHeader, StartByteIndex);
+
+ //
+ // If the number of bits can only fit in 1 or 2 bytes (i.e., 9 bits or
+ // less) we do the following test case.
+ //
+
+ if (NumberToFind <= 9) {
+
+ ULONG CurrentBitIndex;
+
+ UCHAR PreviousByte;
+ UCHAR CurrentByte;
+
+ PreviousByte = 0x00;
+
+ //
+ // Examine all the bytes within our test range searching
+ // for a fit
+ //
+
+ for (CurrentBitIndex = StartByteIndex * 8;
+ CurrentBitIndex < EndByteIndex * 8;
+ CurrentBitIndex += 8) {
+
+ //
+ // Get the current byte
+ //
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // Check to see if a single byte will satisfy the requirement
+ //
+
+ if ((ULONG)RtlpBitSetAnywhere(CurrentByte) >= NumberToFind) {
+
+ UCHAR BitMask;
+ ULONG i;
+
+ //
+ // It all fits in a single byte, so calculate the bit
+ // number. We do this by taking a mask of the appropriate
+ // size and shifting it over until it fits. It fits when
+ // we can bitwise-and the current byte with the bit mask
+ // and get back the bit mask.
+ //
+
+ BitMask = FillMask[ NumberToFind ];
+ for (i = 0; (BitMask & CurrentByte) != BitMask; i += 1) {
+
+ BitMask <<= 1;
+ }
+
+ //
+ // return to our caller the located bit index, and the
+ // number that we found.
+ //
+
+ return CurrentBitIndex + i;
+ }
+
+ //
+ // The current byte does not satisfy our requirements so we'll
+ // check the previous and current byte together to see if
+ // we'll fit. To check uses the high part of the previous
+ // byte and low part of the current byte.
+ //
+
+ if (((ULONG)RtlpBitsSetHigh(PreviousByte) +
+ (ULONG)RtlpBitsSetLow(CurrentByte)) >= NumberToFind) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these two bytes, so we can compute
+ // the starting index. This is done by taking the
+ // index of the current byte (bit 0) and subtracting the
+ // number of bits its takes to get to the first set
+ // high bit.
+ //
+
+ StartingIndex = CurrentBitIndex -
+ (LONG)RtlpBitsSetHigh(PreviousByte);
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // For the next iteration through our loop we need to make
+ // the current byte into the previous byte, and go to the
+ // top of the loop again.
+ //
+
+ PreviousByte = CurrentByte;
+
+ } // end loop CurrentBitIndex
+
+ //
+ // The number to find is greater than 9 but if it is less than 15
+ // then we know it can be satisfied with at most 2 bytes, or 3 bytes
+ // if the middle byte (of the 3) is all ones.
+ //
+
+ } else if (NumberToFind < 15) {
+
+ ULONG CurrentBitIndex;
+
+ UCHAR PreviousPreviousByte;
+ UCHAR PreviousByte;
+ UCHAR CurrentByte;
+
+ PreviousPreviousByte = 0x00;
+ PreviousByte = 0x00;
+
+ //
+ // Examine all the bytes within our test range searching
+ // for a fit
+ //
+
+ for (CurrentBitIndex = StartByteIndex * 8;
+ CurrentBitIndex < EndByteIndex * 8;
+ CurrentBitIndex += 8) {
+
+ //
+ // Get the current byte
+ //
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // Check to see if the Previous byte and current byte
+ // together satisfy the request.
+ //
+
+ if (((ULONG)RtlpBitsSetHigh(PreviousByte) +
+ (ULONG)RtlpBitsSetLow(CurrentByte)) >= NumberToFind) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these two bytes, so we can compute
+ // the starting index. This is done by taking the
+ // index of the current byte (bit 0) and subtracting the
+ // number of bits its takes to get to the first set
+ // high bit.
+ //
+
+ StartingIndex = CurrentBitIndex -
+ (LONG)RtlpBitsSetHigh(PreviousByte);
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // if the previous byte is all ones then maybe the
+ // request can be satisfied using the Previous Previous Byte
+ // Previous Byte, and the Current Byte.
+ //
+
+ if ((PreviousByte == 0xff)
+
+ &&
+
+ (((ULONG)RtlpBitsSetHigh(PreviousPreviousByte) + 8 +
+ (ULONG)RtlpBitsSetLow(CurrentByte)) >= NumberToFind)) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these three bytes, so we can compute
+ // the starting index. This is done by taking the
+ // index of the previous byte (bit 0) and subtracting
+ // the number of bits its takes to get to the first
+ // set high bit.
+ //
+
+ StartingIndex = (CurrentBitIndex - 8) -
+ (LONG)RtlpBitsSetHigh(PreviousPreviousByte);
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // For the next iteration through our loop we need to make
+ // the current byte into the previous byte, the previous
+ // byte into the previous previous byte, and go to the
+ // top of the loop again.
+ //
+
+ PreviousPreviousByte = PreviousByte;
+ PreviousByte = CurrentByte;
+
+ } // end loop CurrentBitIndex
+
+ //
+ // The number to find is greater than or equal to 15. This request
+ // has to have at least one byte of all ones to be satisfied
+ //
+
+ } else {
+
+ ULONG CurrentByteIndex;
+
+ UCHAR CurrentByte;
+
+ ULONG OneBytesNeeded;
+ ULONG OneBytesFound;
+
+ UCHAR StartOfRunByte;
+ LONG StartOfRunIndex;
+
+ //
+ // First precalculate how many one bytes we're going to need
+ //
+
+ OneBytesNeeded = (NumberToFind - 7) / 8;
+
+ //
+ // Indicate for the first time through our loop that we haven't
+ // found a one byte yet, and indicate that the start of the
+ // run is the byte just before the start byte index
+ //
+
+ OneBytesFound = 0;
+ StartOfRunByte = 0x00;
+ StartOfRunIndex = StartByteIndex - 1;
+
+ //
+ // Examine all the bytes in our test range searching for a fit
+ //
+
+ for (CurrentByteIndex = StartByteIndex;
+ CurrentByteIndex < EndByteIndex;
+ CurrentByteIndex += 1) {
+
+ //
+ // Get the current byte
+ //
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // If the number of zero bytes fits our minimum requirements
+ // then we can do the additional test to see if we
+ // actually found a fit
+ //
+
+ if ((OneBytesFound >= OneBytesNeeded)
+
+ &&
+
+ ((ULONG)RtlpBitsSetHigh(StartOfRunByte) + OneBytesFound*8 +
+ (ULONG)RtlpBitsSetLow(CurrentByte)) >= NumberToFind) {
+
+ ULONG StartingIndex;
+
+ //
+ // It all fits in these bytes, so we can compute
+ // the starting index. This is done by taking the
+ // StartOfRunIndex times 8 and adding the number of bits
+ // it takes to get to the first set high bit.
+ //
+
+ StartingIndex = (StartOfRunIndex * 8) +
+ (8 - (LONG)RtlpBitsSetHigh(StartOfRunByte));
+
+ //
+ // Now make sure the total size isn't beyond the bitmap
+ //
+
+ if ((StartingIndex + NumberToFind) <= SizeOfBitMap) {
+
+ return StartingIndex;
+ }
+ }
+
+ //
+ // Check to see if the byte is all ones and increment
+ // the number of one bytes found
+ //
+
+ if (CurrentByte == 0xff) {
+
+ OneBytesFound += 1;
+
+ //
+ // The byte isn't all ones so we need to start over again
+ // looking for one bytes.
+ //
+
+ } else {
+
+ OneBytesFound = 0;
+ StartOfRunByte = CurrentByte;
+ StartOfRunIndex = CurrentByteIndex;
+ }
+
+ } // end loop CurrentByteIndex
+ }
+ }
+
+ //
+ // We never found a fit so we'll return -1
+ //
+
+ return 0xffffffff;
+}
+
+
+ULONG
+RtlFindClearBitsAndSet (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG NumberToFind,
+ IN ULONG HintIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure searches the specified bit map for the specified
+ contiguous region of clear bits, sets the bits and returns the
+ number of bits found, and the starting bit number which was clear
+ then set.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ NumberToFind - Supplies the size of the contiguous region to find.
+
+ HintIndex - Supplies the index (zero based) of where we should start
+ the search from within the bitmap.
+
+Return Value:
+
+ ULONG - Receives the starting index (zero based) of the contiguous
+ region found. If such a region cannot be located a -1 (i.e.,
+ 0xffffffff) is returned.
+
+--*/
+
+{
+ ULONG StartingIndex;
+
+ //
+ // First look for a run of clear bits that equals the size requested
+ //
+
+ StartingIndex = RtlFindClearBits( BitMapHeader,
+ NumberToFind,
+ HintIndex );
+
+ //DbgPrint("FindClearBits %08lx, ", NumberToFind);
+ //DbgPrint("%08lx", StartingIndex);
+ //DumpBitMap(BitMapHeader);
+
+ if (StartingIndex != 0xffffffff) {
+
+ //
+ // We found a large enough run of clear bits so now set them
+ //
+
+ RtlSetBits( BitMapHeader, StartingIndex, NumberToFind );
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return StartingIndex;
+
+}
+
+
+ULONG
+RtlFindSetBitsAndClear (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG NumberToFind,
+ IN ULONG HintIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure searches the specified bit map for the specified
+ contiguous region of set bits, clears the bits and returns the
+ number of bits found and the starting bit number which was set then
+ clear.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ NumberToFind - Supplies the size of the contiguous region to find.
+
+ HintIndex - Supplies the index (zero based) of where we should start
+ the search from within the bitmap.
+
+Return Value:
+
+ ULONG - Receives the starting index (zero based) of the contiguous
+ region found. If such a region cannot be located a -1 (i.e.,
+ 0xffffffff) is returned.
+
+
+--*/
+
+{
+ ULONG StartingIndex;
+
+ //
+ // First look for a run of set bits that equals the size requested
+ //
+
+ if ((StartingIndex = RtlFindSetBits( BitMapHeader,
+ NumberToFind,
+ HintIndex )) != 0xffffffff) {
+
+ //
+ // We found a large enough run of set bits so now clear them
+ //
+
+ RtlClearBits( BitMapHeader, StartingIndex, NumberToFind );
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return StartingIndex;
+}
+
+
+VOID
+RtlClearBits (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG StartingIndex,
+ IN ULONG NumberToClear
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure clears the specified range of bits within the
+ specified bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized Bit Map.
+
+ StartingIndex - Supplies the index (zero based) of the first bit to clear.
+
+ NumberToClear - Supplies the number of bits to clear.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG BitOffset;
+ PULONG CurrentLong;
+
+ //DbgPrint("ClearBits %08lx, ", NumberToClear);
+ //DbgPrint("%08lx", StartingIndex);
+
+ ASSERT( StartingIndex + NumberToClear <= BitMapHeader->SizeOfBitMap );
+
+ //
+ // Special case the situation where the number of bits to clear is
+ // zero. Turn this into a noop.
+ //
+
+ if (NumberToClear == 0) {
+
+ return;
+ }
+
+ BitOffset = StartingIndex % 32;
+
+ //
+ // Get a pointer to the first longword that needs to be zeroed out
+ //
+
+ CurrentLong = &BitMapHeader->Buffer[ StartingIndex / 32 ];
+
+ //
+ // Check if we can only need to clear out one longword.
+ //
+
+ if ((BitOffset + NumberToClear) <= 32) {
+
+ //
+ // To build a mask of bits to clear we shift left to get the number
+ // of bits we're clearing and then shift right to put it in position.
+ // We'll typecast the right shift to ULONG to make sure it doesn't
+ // do a sign extend.
+ //
+
+ *CurrentLong &= ~LeftShiftUlong(RightShiftUlong(((ULONG)0xFFFFFFFF),(32 - NumberToClear)),
+ BitOffset);
+
+ //
+ // And return to our caller
+ //
+
+ //DumpBitMap(BitMapHeader);
+
+ return;
+ }
+
+ //
+ // We can clear out to the end of the first longword so we'll
+ // do that right now.
+ //
+
+ *CurrentLong &= ~LeftShiftUlong(0xFFFFFFFF, BitOffset);
+
+ //
+ // And indicate what the next longword to clear is and how many
+ // bits are left to clear
+ //
+
+ CurrentLong += 1;
+ NumberToClear -= 32 - BitOffset;
+
+ //
+ // The bit position is now long aligned, so we can continue
+ // clearing longwords until the number to clear is less than 32
+ //
+
+ while (NumberToClear >= 32) {
+
+ *CurrentLong = 0;
+ CurrentLong += 1;
+ NumberToClear -= 32;
+ }
+
+ //
+ // And now we can clear the remaining bits, if there are any, in the
+ // last longword
+ //
+
+ if (NumberToClear > 0) {
+
+ *CurrentLong &= LeftShiftUlong(0xFFFFFFFF, NumberToClear);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ //DumpBitMap(BitMapHeader);
+
+ return;
+}
+
+VOID
+RtlSetBits (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG StartingIndex,
+ IN ULONG NumberToSet
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure sets the specified range of bits within the
+ specified bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialied BitMap.
+
+ StartingIndex - Supplies the index (zero based) of the first bit to set.
+
+ NumberToSet - Supplies the number of bits to set.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG BitOffset;
+ PULONG CurrentLong;
+
+ //DbgPrint("SetBits %08lx, ", NumberToSet);
+ //DbgPrint("%08lx", StartingIndex);
+
+ ASSERT( StartingIndex + NumberToSet <= BitMapHeader->SizeOfBitMap );
+
+ //
+ // Special case the situation where the number of bits to set is
+ // zero. Turn this into a noop.
+ //
+
+ if (NumberToSet == 0) {
+
+ return;
+ }
+
+ BitOffset = StartingIndex % 32;
+
+ //
+ // Get a pointer to the first longword that needs to be set
+ //
+
+ CurrentLong = &BitMapHeader->Buffer[ StartingIndex / 32 ];
+
+ //
+ // Check if we can only need to set one longword.
+ //
+
+ if ((BitOffset + NumberToSet) <= 32) {
+
+ //
+ // To build a mask of bits to set we shift left to get the number
+ // of bits we're setting and then shift right to put it in position.
+ // We'll typecast the right shift to ULONG to make sure it doesn't
+ // do a sign extend.
+ //
+
+ *CurrentLong |= LeftShiftUlong(RightShiftUlong(((ULONG)0xFFFFFFFF),(32 - NumberToSet)),
+ BitOffset);
+
+ //
+ // And return to our caller
+ //
+
+ //DumpBitMap(BitMapHeader);
+
+ return;
+ }
+
+ //
+ // We can set bits out to the end of the first longword so we'll
+ // do that right now.
+ //
+
+ *CurrentLong |= LeftShiftUlong(0xFFFFFFFF, BitOffset);
+
+ //
+ // And indicate what the next longword to set is and how many
+ // bits are left to set
+ //
+
+ CurrentLong += 1;
+ NumberToSet -= 32 - BitOffset;
+
+ //
+ // The bit position is now long aligned, so we can continue
+ // setting longwords until the number to set is less than 32
+ //
+
+ while (NumberToSet >= 32) {
+
+ *CurrentLong = 0xffffffff;
+ CurrentLong += 1;
+ NumberToSet -= 32;
+ }
+
+ //
+ // And now we can set the remaining bits, if there are any, in the
+ // last longword
+ //
+
+ if (NumberToSet > 0) {
+
+ *CurrentLong |= ~LeftShiftUlong(0xFFFFFFFF, NumberToSet);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ //DumpBitMap(BitMapHeader);
+
+ return;
+}
+
+
+ULONG
+RtlFindLongestRunClear (
+ IN PRTL_BITMAP BitMapHeader,
+ OUT PULONG StartingIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure finds the largest contiguous range of clear bits
+ within the specified bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ StartingIndex - Receives the index (zero based) of the first run
+ equal to the longest run of clear bits in the BitMap.
+
+Return Value:
+
+ ULONG - Receives the number of bits contained in the largest contiguous
+ run of clear bits.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG LongestRunSize;
+ ULONG LongestRunIndex;
+
+ ULONG CurrentRunSize;
+ ULONG CurrentRunIndex;
+ ULONG CurrentByteIndex;
+ UCHAR CurrentByte;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // Reference the bitmap header to make the loop run faster
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we won't count them. We do
+ // this by first checking if there is any odd bits in the last byte.
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 1's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] |=
+ ZeroMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Set it up so we can the use GET_BYTE macro
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, 0);
+
+ //
+ // Set our longest and current run variables
+ //
+
+ LongestRunSize = 0;
+ LongestRunIndex = 0;
+
+ CurrentRunSize = 0;
+ CurrentRunIndex = 0;
+
+ //
+ // Examine every byte in the BitMap
+ //
+
+ for (CurrentByteIndex = 0;
+ CurrentByteIndex < SizeInBytes;
+ CurrentByteIndex += 1) {
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // If the current byte is not all zeros we need to
+ // (1) check if the current run is big enough to supercede the
+ // longest run, and (2) check if the current byte inside of
+ // itself can supercede the longest run, and (3) start a new
+ // current run
+ //
+
+ if (CurrentByte != 0x00) {
+
+ UCHAR Temp;
+
+ //
+ // Compute the final size of the current run
+ //
+
+ CurrentRunSize += RtlpBitsClearLow[CurrentByte];
+
+ //
+ // Check if the current run is larger than the longest run that
+ // we've found so far
+ //
+
+ if (CurrentRunSize > LongestRunSize) {
+
+ LongestRunSize = CurrentRunSize;
+ LongestRunIndex = CurrentRunIndex;
+ }
+
+ //
+ // The next run starts with the remaining clear bits in the
+ // current byte. We set this up before we check inside the
+ // current byte for a longer run, because the latter test
+ // might require extra work.
+ //
+
+ CurrentRunSize = RtlpBitsClearHigh[ CurrentByte ];
+ CurrentRunIndex = (CurrentByteIndex * 8) + (8 - CurrentRunSize);
+
+ //
+ // Check if the current byte contains a run inside of it that
+ // is both greater than the longest run size, and the current
+ // run size. But we'll only both with this test if the
+ // longest run size, and current run size are both less than 8.
+ //
+
+ if ((LongestRunSize < 8) && (CurrentRunSize < 8) &&
+ ((ULONG)(Temp = RtlpBitsClearAnywhere[CurrentByte]) > LongestRunSize) &&
+ ((ULONG)Temp > CurrentRunSize)) {
+
+ UCHAR BitMask;
+ ULONG i;
+
+ //
+ // Somewhere in the current byte is a run longer than the
+ // longest run, or the current run. All we need to do now
+ // is find the index for this new longest run.
+ //
+
+ BitMask = FillMask[ Temp ];
+
+ for (i = 0; (BitMask & CurrentByte) != 0; i += 1) {
+
+ BitMask <<= 1;
+ }
+
+ LongestRunIndex = (CurrentByteIndex * 8) + i;
+ LongestRunSize = Temp;
+ }
+
+ //
+ // Otherwise the current byte is all zeros and
+ // we simply continue with the current run
+ //
+
+ } else {
+
+ CurrentRunSize += 8;
+ }
+ }
+
+ //
+ // See if we finished looking over the bitmap with an open current
+ // run that is longer than the longest saved run
+ //
+
+ if (CurrentRunSize > LongestRunSize) {
+
+ LongestRunSize = CurrentRunSize;
+ LongestRunIndex = CurrentRunIndex;
+ }
+
+ //
+ // Set our output variables and return to our caller
+ //
+
+ *StartingIndex = LongestRunIndex;
+ return LongestRunSize;
+}
+
+
+ULONG
+RtlFindLongestRunSet (
+ IN PRTL_BITMAP BitMapHeader,
+ OUT PULONG StartingIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure finds the largest contiguous range of set bits
+ within the specified bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ StartingIndex - Receives the index (zero based) of the first run
+ equal to the longest run of set bits in the BitMap.
+
+Return Value:
+
+ ULONG - Receives the number of bits contained in the largest contiguous
+ run of set bits.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG LongestRunSize;
+ ULONG LongestRunIndex;
+
+ ULONG CurrentRunSize;
+ ULONG CurrentRunIndex;
+ ULONG CurrentByteIndex;
+ UCHAR CurrentByte;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // Reference the bitmap header to make the loop run faster
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we won't count them. We do
+ // this by first checking if there is any odd bits in the last byte.
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 0's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] &=
+ FillMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Set it up so we can the use GET_BYTE macro
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, 0);
+
+ //
+ // Set our longest and current run variables
+ //
+
+ LongestRunSize = 0;
+ LongestRunIndex = 0;
+
+ CurrentRunSize = 0;
+ CurrentRunIndex = 0;
+
+ //
+ // Examine every byte in the BitMap
+ //
+
+ for (CurrentByteIndex = 0;
+ CurrentByteIndex < SizeInBytes;
+ CurrentByteIndex += 1) {
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // If the current byte is not all ones we need to
+ // (1) check if the current run is big enough to supercede the
+ // longest run, and (2) check if the current byte inside of
+ // itself can supercede the longest run, and (3) start a new
+ // current run
+ //
+
+ if (CurrentByte != 0xff) {
+
+ UCHAR Temp;
+
+ //
+ // Compute the final size of the current run
+ //
+
+ CurrentRunSize += RtlpBitsSetLow(CurrentByte);
+
+ //
+ // Check if the current run is larger than the longest run that
+ // we've found so far
+ //
+
+ if (CurrentRunSize > LongestRunSize) {
+
+ LongestRunSize = CurrentRunSize;
+ LongestRunIndex = CurrentRunIndex;
+ }
+
+ //
+ // The next run starts with the remaining set bits in the
+ // current byte. We set this up before we check inside the
+ // current byte for a longer run, because the latter test
+ // might require extra work.
+ //
+
+ CurrentRunSize = RtlpBitsSetHigh( CurrentByte );
+ CurrentRunIndex = (CurrentByteIndex * 8) + (8 - CurrentRunSize);
+
+ //
+ // Check if the current byte contains a run inside of it that
+ // is both greater than the longest run size, and the current
+ // run size. But we'll only both with this test if the
+ // longest run size, and current run size are both less than 8.
+ //
+
+ if ((LongestRunSize < 8) && (CurrentRunSize < 8) &&
+ ((ULONG)(Temp = RtlpBitSetAnywhere(CurrentByte)) > LongestRunSize) &&
+ ((ULONG)Temp > CurrentRunSize)) {
+
+ UCHAR BitMask;
+ ULONG i;
+
+ //
+ // Somewhere in the current byte is a run longer than the
+ // longest run, or the current run. All we need to do now
+ // is find the index for this new longest run.
+ //
+
+ BitMask = FillMask[ Temp ];
+
+ for (i = 0; (BitMask & CurrentByte) != BitMask; i += 1) {
+
+ BitMask <<= 1;
+ }
+
+ LongestRunIndex = (CurrentByteIndex * 8) + i;
+ LongestRunSize = Temp;
+ }
+
+ //
+ // Otherwise the current byte is all ones and
+ // we simply continue with the current run
+ //
+
+ } else {
+
+ CurrentRunSize += 8;
+ }
+ }
+
+ //
+ // See if we finished looking over the bitmap with an open current
+ // run that is longer than the longest saved run
+ //
+
+ if (CurrentRunSize > LongestRunSize) {
+
+ LongestRunSize = CurrentRunSize;
+ LongestRunIndex = CurrentRunIndex;
+ }
+
+ //
+ // Set our output variables and return to our caller
+ //
+
+ *StartingIndex = LongestRunIndex;
+ return LongestRunSize;
+}
+
+
+ULONG
+RtlFindFirstRunClear (
+ IN PRTL_BITMAP BitMapHeader,
+ OUT PULONG StartingIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure finds the first contiguous range of clear bits
+ within the specified bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ StartingIndex - Receives the index (zero based) of the first run
+ equal to the longest run of clear bits in the BitMap.
+
+Return Value:
+
+ ULONG - Receives the number of bits contained in the first contiguous
+ run of clear bits.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG CurrentRunSize;
+ ULONG CurrentRunIndex;
+ ULONG CurrentByteIndex;
+ UCHAR CurrentByte;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // Reference the bitmap header to make the loop run faster
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we won't count them. We do
+ // this by first checking if there is any odd bits in the last byte.
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 1's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] |=
+ ZeroMask[SizeOfBitMap % 8];
+
+ }
+
+ //
+ // Set it up so we can the use GET_BYTE macro
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, 0);
+
+ //
+ // Set our current run variables
+ //
+
+ CurrentRunSize = 0;
+ CurrentRunIndex = 0xffffffff;
+
+ //
+ // Examine every byte in the BitMap. We'll also break out of this
+ // loop if ever we finish off a run.
+ //
+
+ for (CurrentByteIndex = 0;
+ CurrentByteIndex < SizeInBytes;
+ CurrentByteIndex += 1) {
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // If the current byte is all ones and the run size is zero then
+ // skip over this byte because we haven't found the start of a
+ // run yet.
+ //
+
+ if ((CurrentByte == 0xff) && (CurrentRunSize == 0)) {
+
+ NOTHING;
+
+ //
+ // See if the current byte is all zeros, because if it is then
+ // we simply continue with the current run
+ //
+
+ } else if (CurrentByte == 0x00) {
+
+ CurrentRunSize += 8;
+
+ if (CurrentRunIndex == 0xffffffff) {
+
+ CurrentRunIndex = (CurrentByteIndex * 8);
+ }
+
+ //
+ // Otherwise the current byte is not all zeros, so we need to
+ // (1) check if we have a current run, or (2) check if the
+ // current byte inside of itself is a run, or (3) start a
+ // current run.
+ //
+ // Check if we have a current run, we do that by checking
+ // if the low bits are clear.
+ //
+
+ } else if (CurrentRunSize != 0) {
+
+ CurrentRunSize += RtlpBitsClearLow[CurrentByte];
+ break;
+
+ //
+ // Check if we have an internal run, we do that by checking
+ // if the high bits are not clear. This check actually cheats
+ // a bit in that the current byte might have an inside run
+ // but we'll skip over it because we can start a run at the
+ // end of the byte.
+ //
+
+ } else if (RtlpBitsClearHigh[CurrentByte] == 0) {
+
+ UCHAR BitMask;
+ ULONG i;
+
+ ASSERT( RtlpBitsClearAnywhere[CurrentByte] != 0 );
+
+ CurrentRunSize = RtlpBitsClearAnywhere[CurrentByte];
+
+ //
+ // Somewhere in the current byte is a run. All we need to do now
+ // is find the index for this new run.
+ //
+
+ BitMask = FillMask[ CurrentRunSize ];
+
+ for (i = 0; (BitMask & CurrentByte) != 0; i += 1) {
+
+ BitMask <<= 1;
+ }
+
+ CurrentRunIndex = (CurrentByteIndex * 8) + i;
+ break;
+
+ //
+ // Otherwise we start a new current run. It starts with the
+ // remaining clear bits in the current byte.
+ //
+
+ } else {
+
+ CurrentRunSize = RtlpBitsClearHigh[ CurrentByte ];
+ CurrentRunIndex = (CurrentByteIndex * 8) + (8 - CurrentRunSize);
+ }
+ }
+
+ //
+ // Set our output variables and return to our caller
+ //
+
+ *StartingIndex = CurrentRunIndex;
+ return CurrentRunSize;
+}
+
+
+ULONG
+RtlFindFirstRunSet (
+ IN PRTL_BITMAP BitMapHeader,
+ OUT PULONG StartingIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure finds the first contiguous range of set bits
+ within the specified bit map.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+
+ StartingIndex - Receives the index (zero based) of the first run
+ equal to the longest run of set bits in the BitMap.
+
+Return Value:
+
+ ULONG - Receives the number of bits contained in the largest contiguous
+ run of set bits.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG CurrentRunSize;
+ ULONG CurrentRunIndex;
+ ULONG CurrentByteIndex;
+ UCHAR CurrentByte;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // Reference the bitmap header to make the loop run faster
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we won't count them. We do
+ // this by first checking if there is any odd bits in the last byte.
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 0's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] &=
+ FillMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Set it up so we can the use GET_BYTE macro
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, 0);
+
+ //
+ // Set our current run variables
+ //
+
+ CurrentRunSize = 0;
+ CurrentRunIndex = 0xffffffff;
+
+ //
+ // Examine every byte in the BitMap. We'll also break out of this
+ // loop if ever we finish off a run.
+ //
+
+ for (CurrentByteIndex = 0;
+ CurrentByteIndex < SizeInBytes;
+ CurrentByteIndex += 1) {
+
+ GET_BYTE( CurrentByte );
+
+ //
+ // If the current byte is all zeros and the run size is zero then
+ // skip over this byte because we haven't found the start of a
+ // run yet.
+ //
+
+ if ((CurrentByte == 0x00) && (CurrentRunSize == 0)) {
+
+ NOTHING;
+
+ //
+ // See if the current byte is all ones, because if it is then
+ // we simply continue with the current run
+ //
+
+ } else if (CurrentByte == 0xff) {
+
+ CurrentRunSize += 8;
+
+ if (CurrentRunIndex == 0xffffffff) {
+
+ CurrentRunIndex = (CurrentByteIndex * 8);
+ }
+
+ //
+ // Otherwise the current byte is not all ones, so we need to
+ // (1) check if we have the current run, or (2) check if the
+ // current byte inside of itself is a run, or (3) start a
+ // current run.
+ //
+ // Check if we have a current run, we do that by checking
+ // if the low bits are set.
+ //
+
+ } else if (CurrentRunSize != 0) {
+
+ CurrentRunSize += RtlpBitsSetLow(CurrentByte);
+ break;
+
+ //
+ // Check if we have an internal run, we do that by checking
+ // if the high bits are not set. This check actually cheats
+ // a bit in that the current byte might have an inside run
+ // but we'll skip over it because we can start a run at the
+ // end of the byte.
+ //
+
+ } else if (RtlpBitsSetHigh(CurrentByte) == 0) {
+
+ UCHAR BitMask;
+ ULONG i;
+
+ ASSERT( RtlpBitSetAnywhere(CurrentByte) != 0 );
+
+ CurrentRunSize = RtlpBitSetAnywhere(CurrentByte);
+
+ //
+ // Somewhere in the current byte is a run. All we need to do now
+ // is find the index for this new run.
+ //
+
+ BitMask = FillMask[ CurrentRunSize ];
+
+ for (i = 0; (BitMask & CurrentByte) != BitMask; i += 1) {
+
+ BitMask <<= 1;
+ }
+
+ CurrentRunIndex = (CurrentByteIndex * 8) + i;
+ break;
+
+ //
+ // Otherwise we start a new current run. It starts with the
+ // remaining set bits in the current byte.
+ //
+
+ } else {
+
+ CurrentRunSize = RtlpBitsSetHigh( CurrentByte );
+ CurrentRunIndex = (CurrentByteIndex * 8) + (8 - CurrentRunSize);
+ }
+ }
+
+ //
+ // Set our output variables and return to our caller
+ //
+
+ *StartingIndex = CurrentRunIndex;
+ return CurrentRunSize;
+}
+
+
+ULONG
+RtlNumberOfClearBits (
+ IN PRTL_BITMAP BitMapHeader
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure counts and returns the number of clears bits within
+ the specified bitmap.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized bitmap.
+
+Return Value:
+
+ ULONG - The total number of clear bits in the bitmap
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG i;
+ UCHAR CurrentByte;
+
+ ULONG TotalClear;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // Reference the bitmap header to make the loop run faster
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Set any unused bits in the last byte so we don't count them. We
+ // do this by first checking if there are any odd bits in the last byte
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 1's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] |=
+ ZeroMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Set if up so we can use the GET_BYTE macro
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, 0 );
+
+ //
+ // Examine every byte in the bitmap
+ //
+
+ TotalClear = 0;
+ for (i = 0; i < SizeInBytes; i += 1) {
+
+ GET_BYTE( CurrentByte );
+
+ TotalClear += RtlpBitsClearTotal[CurrentByte];
+ }
+
+ return TotalClear;
+}
+
+
+ULONG
+RtlNumberOfSetBits (
+ IN PRTL_BITMAP BitMapHeader
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure counts and returns the number of set bits within
+ the specified bitmap.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized bitmap.
+
+Return Value:
+
+ ULONG - The total number of set bits in the bitmap
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG i;
+ UCHAR CurrentByte;
+
+ ULONG TotalSet;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // Reference the bitmap header to make the loop run faster
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // Clear any unused bits in the last byte so we don't count them. We
+ // do this by first checking if there are any odd bits in the last byte
+ //
+
+ if ((SizeOfBitMap % 8) != 0) {
+
+ //
+ // The last byte has some odd bits so we'll set the high unused
+ // bits in the last byte to 0's
+ //
+
+ ((PUCHAR)BitMapHeader->Buffer)[SizeInBytes - 1] &=
+ FillMask[SizeOfBitMap % 8];
+ }
+
+ //
+ // Set if up so we can use the GET_BYTE macro
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, 0 );
+
+ //
+ // Examine every byte in the bitmap
+ //
+
+ TotalSet = 0;
+ for (i = 0; i < SizeInBytes; i += 1) {
+
+ GET_BYTE( CurrentByte );
+
+ TotalSet += RtlpBitsSetTotal(CurrentByte);
+ }
+
+ return TotalSet;
+}
+
+
+BOOLEAN
+RtlAreBitsClear (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG StartingIndex,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure determines if the range of specified bits are all clear.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized bitmap.
+
+ StartingIndex - Supplies the starting bit index to examine
+
+ Length - Supplies the number of bits to examine
+
+Return Value:
+
+ BOOLEAN - TRUE if the specified bits in the bitmap are all clear, and
+ FALSE if any are set or if the range is outside the bitmap or if
+ Length is zero.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG EndingIndex;
+
+ ULONG StartingByte;
+ ULONG EndingByte;
+
+ ULONG StartingOffset;
+ ULONG EndingOffset;
+
+ ULONG i;
+ UCHAR Byte;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // To make the loops in our test run faster we'll extract the fields
+ // from the bitmap header
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // First make sure that the specified range is contained within the
+ // bitmap, and the length is not zero.
+ //
+
+ if ((StartingIndex + Length > SizeOfBitMap) || (Length == 0)) {
+
+ return FALSE;
+ }
+
+ //
+ // Compute the ending index, starting and ending byte, and the starting
+ // and ending offset within each byte
+ //
+
+ EndingIndex = StartingIndex + Length - 1;
+
+ StartingByte = StartingIndex / 8;
+ EndingByte = EndingIndex / 8;
+
+ StartingOffset = StartingIndex % 8;
+ EndingOffset = EndingIndex % 8;
+
+ //
+ // Set ourselves up to get the next byte
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, StartingByte );
+
+ //
+ // Special case the situation where the starting byte and ending
+ // byte are one in the same
+ //
+
+ if (StartingByte == EndingByte) {
+
+ //
+ // Get the single byte we are to look at
+ //
+
+ GET_BYTE( Byte );
+
+ //
+ // Now we compute the mask of bits we're after and then AND it with
+ // the byte. If it is zero then the bits in question are all clear
+ // otherwise at least one of them is set.
+ //
+
+ if ((ZeroMask[StartingOffset] & FillMask[EndingOffset+1] & Byte) == 0) {
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+ }
+
+ } else {
+
+ //
+ // Get the first byte that we're after, and then
+ // compute the mask of bits we're after for the first byte then
+ // AND it with the byte itself.
+ //
+
+ GET_BYTE( Byte );
+
+ if ((ZeroMask[StartingOffset] & Byte) != 0) {
+
+ return FALSE;
+ }
+
+ //
+ // Now for every whole byte inbetween read in the byte,
+ // and make sure it is all zeros
+ //
+
+ for (i = StartingByte+1; i < EndingByte; i += 1) {
+
+ GET_BYTE( Byte );
+
+ if (Byte != 0) {
+
+ return FALSE;
+ }
+ }
+
+ //
+ // Get the last byte we're after, and then
+ // compute the mask of bits we're after for the last byte then
+ // AND it with the byte itself.
+ //
+
+ GET_BYTE( Byte );
+
+ if ((FillMask[EndingOffset+1] & Byte) != 0) {
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+RtlAreBitsSet (
+ IN PRTL_BITMAP BitMapHeader,
+ IN ULONG StartingIndex,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure determines if the range of specified bits are all set.
+
+Arguments:
+
+ BitMapHeader - Supplies a pointer to the previously initialized bitmap.
+
+ StartingIndex - Supplies the starting bit index to examine
+
+ Length - Supplies the number of bits to examine
+
+Return Value:
+
+ BOOLEAN - TRUE if the specified bits in the bitmap are all set, and
+ FALSE if any are clear or if the range is outside the bitmap or if
+ Length is zero.
+
+--*/
+
+{
+ ULONG SizeOfBitMap;
+ ULONG SizeInBytes;
+
+ ULONG EndingIndex;
+
+ ULONG StartingByte;
+ ULONG EndingByte;
+
+ ULONG StartingOffset;
+ ULONG EndingOffset;
+
+ ULONG i;
+ UCHAR Byte;
+
+ GET_BYTE_DECLARATIONS();
+
+ //
+ // To make the loops in our test run faster we'll extract the fields
+ // from the bitmap header
+ //
+
+ SizeOfBitMap = BitMapHeader->SizeOfBitMap;
+ SizeInBytes = (SizeOfBitMap + 7) / 8;
+
+ //
+ // First make sure that the specified range is contained within the
+ // bitmap, and the length is not zero.
+ //
+
+ if ((StartingIndex + Length > SizeOfBitMap) || (Length == 0)) {
+
+ return FALSE;
+ }
+
+ //
+ // Compute the ending index, starting and ending byte, and the starting
+ // and ending offset within each byte
+ //
+
+ EndingIndex = StartingIndex + Length - 1;
+
+ StartingByte = StartingIndex / 8;
+ EndingByte = EndingIndex / 8;
+
+ StartingOffset = StartingIndex % 8;
+ EndingOffset = EndingIndex % 8;
+
+ //
+ // Set ourselves up to get the next byte
+ //
+
+ GET_BYTE_INITIALIZATION( BitMapHeader, StartingByte );
+
+ //
+ // Special case the situation where the starting byte and ending
+ // byte are one in the same
+ //
+
+ if (StartingByte == EndingByte) {
+
+ //
+ // Get the single byte we are to look at
+ //
+
+ GET_BYTE( Byte );
+
+ //
+ // Now we compute the mask of bits we're after and then AND it with
+ // the complement of the byte If it is zero then the bits in question
+ // are all clear otherwise at least one of them is clear.
+ //
+
+ if ((ZeroMask[StartingOffset] & FillMask[EndingOffset+1] & ~Byte) == 0) {
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+ }
+
+ } else {
+
+ //
+ // Get the first byte that we're after, and then
+ // compute the mask of bits we're after for the first byte then
+ // AND it with the complement of the byte itself.
+ //
+
+ GET_BYTE( Byte );
+
+ if ((ZeroMask[StartingOffset] & ~Byte) != 0) {
+
+ return FALSE;
+ }
+
+ //
+ // Now for every whole byte inbetween read in the byte,
+ // and make sure it is all ones
+ //
+
+ for (i = StartingByte+1; i < EndingByte; i += 1) {
+
+ GET_BYTE( Byte );
+
+ if (Byte != 0xff) {
+
+ return FALSE;
+ }
+ }
+
+ //
+ // Get the last byte we're after, and then
+ // compute the mask of bits we're after for the last byte then
+ // AND it with the complement of the byte itself.
+ //
+
+ GET_BYTE( Byte );
+
+ if ((FillMask[EndingOffset+1] & ~Byte) != 0) {
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+//ULONG
+//RtlCheckBit (
+// IN PRTL_BITMAP BitMapHeader,
+// IN ULONG BitPosition
+// )
+//
+///*++
+//
+//Routine Description:
+//
+// This procedure returns the state of the specified bit within the
+// specified bit map.
+//
+//Arguments:
+//
+// BitMapHeader - Supplies a pointer to the previously initialized BitMap.
+//
+// BitPosition - Supplies the bit number of which to return the state.
+//
+//Return Value:
+//
+// ULONG - The state of the specified bit.
+//
+//--*/
+//
+//{
+// ULONG results;
+//
+// results = ((BitMapHeader->Buffer[BitPosition / 32]) >> (BitPosition % 32)) & 0x00000001;
+//
+// //DbgPrint("CheckBit %08lx", BitPosition);
+// //DbgPrint(" %08lx", results);
+// //DumpBitMap(BitMapHeader);
+//
+// return results;
+//}
+
diff --git a/private/ntos/rtl/boot/alpha/sources b/private/ntos/rtl/boot/alpha/sources
new file mode 100644
index 000000000..71bd37fb0
--- /dev/null
+++ b/private/ntos/rtl/boot/alpha/sources
@@ -0,0 +1,5 @@
+ALPHA_SOURCES=..\alpha\debugstb.s \
+ ..\alpha\largeint.s \
+ ..\alpha\lzntaxp.s \
+ ..\alpha\ntcurteb.s \
+ ..\alpha\mvmem.s
diff --git a/private/ntos/rtl/boot/i386/sources b/private/ntos/rtl/boot/i386/sources
new file mode 100644
index 000000000..031b3f447
--- /dev/null
+++ b/private/ntos/rtl/boot/i386/sources
@@ -0,0 +1,9 @@
+i386_SOURCES=..\i386\divlarge.c \
+ ..\i386\debug3.c \
+ ..\i386\debug2.asm \
+ ..\i386\ioaccess.asm \
+ ..\i386\largeint.asm \
+ ..\i386\lzntx86.asm \
+ ..\i386\movemem.asm \
+ ..\i386\ntcurteb.asm
+
diff --git a/private/ntos/rtl/boot/makefile b/private/ntos/rtl/boot/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/rtl/boot/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/rtl/boot/mips/sources b/private/ntos/rtl/boot/mips/sources
new file mode 100644
index 000000000..578d1ea35
--- /dev/null
+++ b/private/ntos/rtl/boot/mips/sources
@@ -0,0 +1,4 @@
+MIPS_SOURCES=..\mips\debugstb.s \
+ ..\mips\largeint.s \
+ ..\mips\lzntmips.s \
+ ..\mips\xxmvmem.s
diff --git a/private/ntos/rtl/boot/ppc/sources b/private/ntos/rtl/boot/ppc/sources
new file mode 100644
index 000000000..c81545a04
--- /dev/null
+++ b/private/ntos/rtl/boot/ppc/sources
@@ -0,0 +1,4 @@
+PPC_SOURCES=..\ppc\debugstb.s \
+ ..\ppc\largeint.s \
+ ..\ppc\lzntppc.s \
+ ..\ppc\movemem.s
diff --git a/private/ntos/rtl/boot/sources b/private/ntos/rtl/boot/sources
new file mode 100644
index 000000000..a79762930
--- /dev/null
+++ b/private/ntos/rtl/boot/sources
@@ -0,0 +1,49 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=brtl
+
+TARGETNAME=bldrrtl
+TARGETPATH=..\obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..;..\..\inc;..\..\..\inc
+
+C_DEFINES=$(C_DEFINES) -D_NTSYSTEM_ -DBLDR_KERNEL_RUNTIME=1
+
+ASM_DEFINES=-DBLDR_KERNEL_RUNTIME=1
+
+SOURCES=..\assert.c \
+ ..\bitmap.c \
+ ..\cnvint.c \
+ ..\debug.c \
+ ..\imagedir.c \
+ ..\checksum.c \
+ ..\compress.c \
+ ..\ldrreloc.c \
+ ..\lznt1.c \
+ ..\time.c
+
+UMTYPE=console
diff --git a/private/ntos/rtl/checksum.c b/private/ntos/rtl/checksum.c
new file mode 100644
index 000000000..62cabb857
--- /dev/null
+++ b/private/ntos/rtl/checksum.c
@@ -0,0 +1,178 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imagedir.c
+
+Abstract:
+
+ The module contains the code to translate an image directory type to
+ the address of the data for that entry.
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Environment:
+
+ User Mode or Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Define forward referenced prootypes.
+//
+
+USHORT
+ChkSum(
+ ULONG PartialSum,
+ PUSHORT Source,
+ ULONG Length
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,ChkSum)
+#pragma alloc_text(PAGE,LdrVerifyMappedImageMatchesChecksum)
+#endif
+
+USHORT
+ChkSum(
+ ULONG PartialSum,
+ PUSHORT Source,
+ ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ Compute a partial checksum on a portion of an imagefile.
+
+Arguments:
+
+ PartialSum - Supplies the initial checksum value.
+
+ Sources - Supplies a pointer to the array of words for which the
+ checksum is computed.
+
+ Length - Supplies the length of the array in words.
+
+Return Value:
+
+ The computed checksum value is returned as the function value.
+
+--*/
+
+{
+
+ RTL_PAGED_CODE();
+
+ //
+ // Compute the word wise checksum allowing carries to occur into the
+ // high order half of the checksum longword.
+ //
+
+ while (Length--) {
+ PartialSum += *Source++;
+ PartialSum = (PartialSum >> 16) + (PartialSum & 0xffff);
+ }
+
+ //
+ // Fold final carry into a single word result and return the resultant
+ // value.
+ //
+
+ return (USHORT)(((PartialSum >> 16) + PartialSum) & 0xffff);
+}
+
+BOOLEAN
+LdrVerifyMappedImageMatchesChecksum (
+ IN PVOID BaseAddress,
+ IN ULONG FileLength
+ )
+
+/*++
+
+Routine Description:
+
+ This functions computes the checksum of an image mapped as a data file.
+
+Arguments:
+
+ BaseAddress - Supplies a pointer to the base of the mapped file.
+
+ FileLength - Supplies the length of the file in bytes.
+
+Return Value:
+
+ TRUE - The checksum stored in the image matches the checksum of the data.
+
+ FALSE - The checksum in the image is not correct.
+
+--*/
+
+{
+
+ PUSHORT AdjustSum;
+ PIMAGE_NT_HEADERS NtHeaders;
+ USHORT PartialSum;
+ ULONG HeaderSum;
+ ULONG CheckSum;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Compute the checksum of the file and zero the header checksum value.
+ //
+
+ HeaderSum = 0;
+ PartialSum = ChkSum(0, (PUSHORT)BaseAddress, (FileLength + 1) >> 1);
+
+ //
+ // If the file is an image file, then subtract the two checksum words
+ // in the optional header from the computed checksum before adding
+ // the file length, and set the value of the header checksum.
+ //
+
+ NtHeaders = RtlImageNtHeader(BaseAddress);
+ if (NtHeaders != NULL) {
+ HeaderSum = NtHeaders->OptionalHeader.CheckSum;
+
+#ifndef NTOS_KERNEL_RUNTIME
+ //
+ // On Nt 3.1 and 3.5, we allowed printer drivers with 0 checksums into
+ // csrss unintentionally. This means that we must allow this forever.
+ // I don't want to allow this for kernel mode drivers, so I will only
+ // allow 0 checksums of the high order bit is clear ?
+ //
+
+
+ if ( HeaderSum == 0 ) {
+ return TRUE;
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ AdjustSum = (PUSHORT)(&NtHeaders->OptionalHeader.CheckSum);
+ PartialSum -= (PartialSum < AdjustSum[0]);
+ PartialSum -= AdjustSum[0];
+ PartialSum -= (PartialSum < AdjustSum[1]);
+ PartialSum -= AdjustSum[1];
+ } else {
+ PartialSum = 0;
+ HeaderSum = FileLength;
+ }
+
+ //
+ // Compute the final checksum value as the sum of the paritial checksum
+ // and the file length.
+ //
+
+ CheckSum = (ULONG)PartialSum + FileLength;
+ return (CheckSum == HeaderSum);
+}
diff --git a/private/ntos/rtl/cnvint.c b/private/ntos/rtl/cnvint.c
new file mode 100644
index 000000000..41ade42bb
--- /dev/null
+++ b/private/ntos/rtl/cnvint.c
@@ -0,0 +1,455 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ cnvint.c
+
+Abstract:
+
+ Text to integer and integer to text converion routines.
+
+Author:
+
+ Steve Wood (stevewo) 23-Aug-1990
+
+Revision History:
+
+--*/
+
+#include <ntrtlp.h>
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlIntegerToChar)
+#pragma alloc_text(PAGE,RtlCharToInteger)
+#pragma alloc_text(PAGE,RtlUnicodeStringToInteger)
+#pragma alloc_text(PAGE,RtlIntegerToUnicodeString)
+#pragma alloc_text(PAGE,RtlLargeIntegerToChar)
+#endif
+
+CHAR RtlpIntegerChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+NTSTATUS
+RtlIntegerToChar (
+ IN ULONG Value,
+ IN ULONG Base OPTIONAL,
+ IN LONG OutputLength,
+ OUT PSZ String
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ CHAR Result[ 33 ], *s;
+ ULONG Shift, Mask, Digit, Length;
+
+ Shift = 0;
+ switch( Base ) {
+ case 16: Shift = 4; break;
+ case 8: Shift = 3; break;
+ case 2: Shift = 1; break;
+
+ case 0: Base = 10;
+ case 10: Shift = 0; break;
+ default: return( STATUS_INVALID_PARAMETER );
+ }
+
+ if (Shift != 0) {
+ Mask = 0xF >> 4 - Shift;
+ }
+
+ s = &Result[ 32 ];
+ *s = '\0';
+ do {
+ if (Shift != 0) {
+ Digit = Value & Mask;
+ Value >>= Shift;
+ }
+ else {
+ Digit = Value % Base;
+ Value = Value / Base;
+ }
+
+ *--s = RtlpIntegerChars[ Digit ];
+ } while (Value != 0);
+
+ Length = &Result[ 32 ] - s;
+ if (OutputLength < 0) {
+ OutputLength = -OutputLength;
+ while ((LONG)Length < OutputLength) {
+ *--s = '0';
+ Length++;
+ }
+ }
+
+ if ((LONG)Length > OutputLength) {
+ return( STATUS_BUFFER_OVERFLOW );
+ }
+ else {
+ try {
+ RtlMoveMemory( String, s, Length );
+
+ if ((LONG)Length < OutputLength) {
+ String[ Length ] = '\0';
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_SUCCESS );
+ }
+}
+
+
+NTSTATUS
+RtlCharToInteger (
+ IN PCSZ String,
+ IN ULONG Base OPTIONAL,
+ OUT PULONG Value
+ )
+{
+ CHAR c, Sign;
+ ULONG Result, Digit, Shift;
+
+ while ((Sign = *String++) <= ' ') {
+ if (!*String) {
+ String--;
+ break;
+ }
+ }
+
+ c = Sign;
+ if (c == '-' || c == '+') {
+ c = *String++;
+ }
+
+ if (!ARGUMENT_PRESENT( Base )) {
+ Base = 10;
+ Shift = 0;
+ if (c == '0') {
+ c = *String++;
+ if (c == 'x') {
+ Base = 16;
+ Shift = 4;
+ }
+ else
+ if (c == 'o') {
+ Base = 8;
+ Shift = 3;
+ }
+ else
+ if (c == 'b') {
+ Base = 2;
+ Shift = 1;
+ }
+ else {
+ String--;
+ }
+
+ c = *String++;
+ }
+ }
+ else {
+ switch( Base ) {
+ case 16: Shift = 4; break;
+ case 8: Shift = 3; break;
+ case 2: Shift = 1; break;
+ case 10: Shift = 0; break;
+ default: return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ Result = 0;
+ while (c) {
+ if (c >= '0' && c <= '9') {
+ Digit = c - '0';
+ }
+ else
+ if (c >= 'A' && c <= 'F') {
+ Digit = c - 'A' + 10;
+ }
+ else
+ if (c >= 'a' && c <= 'f') {
+ Digit = c - 'a' + 10;
+ }
+ else {
+ break;
+ }
+
+ if (Digit >= Base) {
+ break;
+ }
+
+ if (Shift == 0) {
+ Result = (Base * Result) + Digit;
+ }
+ else {
+ Result = (Result << Shift) | Digit;
+ }
+
+ c = *String++;
+ }
+
+ if (Sign == '-') {
+ Result = (ULONG)(-(LONG)Result);
+ }
+
+ try {
+ *Value = Result;
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlUnicodeStringToInteger (
+ IN PUNICODE_STRING String,
+ IN ULONG Base OPTIONAL,
+ OUT PULONG Value
+ )
+{
+ PCWSTR s;
+ WCHAR c, Sign;
+ ULONG nChars, Result, Digit, Shift;
+
+ s = String->Buffer;
+ nChars = String->Length / sizeof( WCHAR );
+ while (nChars-- && (Sign = *s++) <= ' ') {
+ if (!nChars) {
+ Sign = UNICODE_NULL;
+ break;
+ }
+ }
+
+ c = Sign;
+ if (c == L'-' || c == L'+') {
+ if (nChars) {
+ nChars--;
+ c = *s++;
+ }
+ else {
+ c = UNICODE_NULL;
+ }
+ }
+
+ if (!ARGUMENT_PRESENT( Base )) {
+ Base = 10;
+ Shift = 0;
+ if (c == L'0') {
+ if (nChars) {
+ nChars--;
+ c = *s++;
+ if (c == L'x') {
+ Base = 16;
+ Shift = 4;
+ }
+ else
+ if (c == L'o') {
+ Base = 8;
+ Shift = 3;
+ }
+ else
+ if (c == L'b') {
+ Base = 2;
+ Shift = 1;
+ }
+ else {
+ nChars++;
+ s--;
+ }
+ }
+
+ if (nChars) {
+ nChars--;
+ c = *s++;
+ }
+ else {
+ c = UNICODE_NULL;
+ }
+ }
+ }
+ else {
+ switch( Base ) {
+ case 16: Shift = 4; break;
+ case 8: Shift = 3; break;
+ case 2: Shift = 1; break;
+ case 10: Shift = 0; break;
+ default: return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ Result = 0;
+ while (c != UNICODE_NULL) {
+ if (c >= L'0' && c <= L'9') {
+ Digit = c - L'0';
+ }
+ else
+ if (c >= L'A' && c <= L'F') {
+ Digit = c - L'A' + 10;
+ }
+ else
+ if (c >= L'a' && c <= L'f') {
+ Digit = c - L'a' + 10;
+ }
+ else {
+ break;
+ }
+
+ if (Digit >= Base) {
+ break;
+ }
+
+ if (Shift == 0) {
+ Result = (Base * Result) + Digit;
+ }
+ else {
+ Result = (Result << Shift) | Digit;
+ }
+
+ if (!nChars) {
+ break;
+ }
+ nChars--;
+ c = *s++;
+ }
+
+ if (Sign == L'-') {
+ Result = (ULONG)(-(LONG)Result);
+ }
+
+ try {
+ *Value = Result;
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlIntegerToUnicodeString (
+ IN ULONG Value,
+ IN ULONG Base OPTIONAL,
+ IN OUT PUNICODE_STRING String
+ )
+{
+ NTSTATUS Status;
+ UCHAR ResultBuffer[ 16 ];
+ ANSI_STRING AnsiString;
+
+ Status = RtlIntegerToChar( Value, Base, sizeof( ResultBuffer ), ResultBuffer );
+ if (NT_SUCCESS( Status )) {
+ AnsiString.Buffer = ResultBuffer;
+ AnsiString.MaximumLength = sizeof( ResultBuffer );
+ AnsiString.Length = (USHORT)strlen( ResultBuffer );
+ Status = RtlAnsiStringToUnicodeString( String, &AnsiString, FALSE );
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlLargeIntegerToChar (
+ IN PLARGE_INTEGER Value,
+ IN ULONG Base OPTIONAL,
+ IN LONG OutputLength,
+ OUT PSZ String
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ CHAR Result[ 100 ], *s;
+ ULONG Shift, Mask, Digit, Length;
+
+ Shift = 0;
+ switch( Base ) {
+ case 16: Shift = 4; break;
+ case 8: Shift = 3; break;
+ case 2: Shift = 1; break;
+
+ case 0:
+ case 10: Shift = 0; break;
+ default: return( STATUS_INVALID_PARAMETER );
+ }
+
+ if (Shift != 0) {
+ Mask = 0xF >> 4 - Shift;
+ }
+
+ s = &Result[ 99 ];
+ *s = '\0';
+ if (Shift != 0) {
+ ULONG LowValue,HighValue,HighShift,HighMask;
+
+ LowValue = Value->LowPart;
+ HighValue = Value->HighPart;
+ HighShift = Shift - (sizeof(ULONG) % Shift);
+ HighMask = 0xF >> 4 - HighShift;
+ do {
+ Digit = LowValue & Mask;
+ LowValue = (LowValue >> Shift) | ((HighValue & HighMask) << (sizeof(ULONG) - HighShift));
+ HighValue = HighValue >> HighShift;
+ *--s = RtlpIntegerChars[ Digit ];
+ } while ((LowValue | HighValue) != 0);
+ } else {
+ LARGE_INTEGER TempValue=*Value;
+ do {
+ TempValue = RtlExtendedLargeIntegerDivide(TempValue,Base,&Digit);
+ *--s = RtlpIntegerChars[ Digit ];
+ } while (TempValue.HighPart != 0 || TempValue.LowPart != 0);
+ }
+
+ Length = &Result[ 99 ] - s;
+ if (OutputLength < 0) {
+ OutputLength = -OutputLength;
+ while ((LONG)Length < OutputLength) {
+ *--s = '0';
+ Length++;
+ }
+ }
+
+ if ((LONG)Length > OutputLength) {
+ return( STATUS_BUFFER_OVERFLOW );
+ }
+ else {
+ try {
+ RtlMoveMemory( String, s, Length );
+
+ if ((LONG)Length < OutputLength) {
+ String[ Length ] = '\0';
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ return( GetExceptionCode() );
+ }
+
+ return( STATUS_SUCCESS );
+ }
+}
diff --git a/private/ntos/rtl/compress.c b/private/ntos/rtl/compress.c
new file mode 100644
index 000000000..59f93cba0
--- /dev/null
+++ b/private/ntos/rtl/compress.c
@@ -0,0 +1,1257 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ compress.c
+
+Abstract:
+
+ This module implements the NT Rtl compression engine.
+
+Author:
+
+ Gary Kimura [GaryKi] 21-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+
+//
+// The following arrays hold procedures that we call to do the various
+// compression functions. Each new compression function will need to
+// be added to this array. For one that are currently not supported
+// we will fill in a not supported routine.
+//
+
+NTSTATUS
+RtlCompressWorkSpaceSizeNS (
+ IN USHORT CompressionEngine,
+ OUT PULONG CompressBufferWorkSpaceSize,
+ OUT PULONG CompressFragmentWorkSpaceSize
+ );
+
+NTSTATUS
+RtlCompressBufferNS (
+ IN USHORT CompressionEngine,
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG UncompressedChunkSize,
+ OUT PULONG FinalCompressedSize,
+ IN PVOID WorkSpace
+ );
+
+NTSTATUS
+RtlDecompressBufferNS (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ );
+
+NTSTATUS
+RtlDecompressFragmentNS (
+ OUT PUCHAR UncompressedFragment,
+ IN ULONG UncompressedFragmentSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG FragmentOffset,
+ OUT PULONG FinalUncompressedSize,
+ IN PVOID WorkSpace
+ );
+
+NTSTATUS
+RtlDescribeChunkNS (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ OUT PULONG ChunkSize
+ );
+
+NTSTATUS
+RtlReserveChunkNS (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ IN ULONG ChunkSize
+ );
+
+//
+// Routines to query the amount of memory needed for each workspace
+//
+
+PRTL_COMPRESS_WORKSPACE_SIZE RtlWorkSpaceProcs[8] = {
+ NULL, // 0
+ NULL, // 1
+ RtlCompressWorkSpaceSizeLZNT1, // 2
+ RtlCompressWorkSpaceSizeNS, // 3
+ RtlCompressWorkSpaceSizeNS, // 4
+ RtlCompressWorkSpaceSizeNS, // 5
+ RtlCompressWorkSpaceSizeNS, // 6
+ RtlCompressWorkSpaceSizeNS // 7
+};
+
+//
+// Routines to compress a buffer
+//
+
+PRTL_COMPRESS_BUFFER RtlCompressBufferProcs[8] = {
+ NULL, // 0
+ NULL, // 1
+ RtlCompressBufferLZNT1, // 2
+ RtlCompressBufferNS, // 3
+ RtlCompressBufferNS, // 4
+ RtlCompressBufferNS, // 5
+ RtlCompressBufferNS, // 6
+ RtlCompressBufferNS // 7
+};
+
+//
+// Routines to decompress a buffer
+//
+
+PRTL_DECOMPRESS_BUFFER RtlDecompressBufferProcs[8] = {
+ NULL, // 0
+ NULL, // 1
+ RtlDecompressBufferLZNT1, // 2
+ RtlDecompressBufferNS, // 3
+ RtlDecompressBufferNS, // 4
+ RtlDecompressBufferNS, // 5
+ RtlDecompressBufferNS, // 6
+ RtlDecompressBufferNS // 7
+};
+
+//
+// Routines to decompress a fragment
+//
+
+PRTL_DECOMPRESS_FRAGMENT RtlDecompressFragmentProcs[8] = {
+ NULL, // 0
+ NULL, // 1
+ RtlDecompressFragmentLZNT1, // 2
+ RtlDecompressFragmentNS, // 3
+ RtlDecompressFragmentNS, // 4
+ RtlDecompressFragmentNS, // 5
+ RtlDecompressFragmentNS, // 6
+ RtlDecompressFragmentNS // 7
+};
+
+//
+// Routines to describe the current chunk
+//
+
+PRTL_DESCRIBE_CHUNK RtlDescribeChunkProcs[8] = {
+ NULL, // 0
+ NULL, // 1
+ RtlDescribeChunkLZNT1, // 2
+ RtlDescribeChunkNS, // 3
+ RtlDescribeChunkNS, // 4
+ RtlDescribeChunkNS, // 5
+ RtlDescribeChunkNS, // 6
+ RtlDescribeChunkNS // 7
+};
+
+//
+// Routines to reserve for a chunk
+//
+
+PRTL_RESERVE_CHUNK RtlReserveChunkProcs[8] = {
+ NULL, // 0
+ NULL, // 1
+ RtlReserveChunkLZNT1, // 2
+ RtlReserveChunkNS, // 3
+ RtlReserveChunkNS, // 4
+ RtlReserveChunkNS, // 5
+ RtlReserveChunkNS, // 6
+ RtlReserveChunkNS // 7
+};
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE, RtlGetCompressionWorkSpaceSize)
+#pragma alloc_text(PAGE, RtlCompressBuffer)
+#pragma alloc_text(PAGE, RtlDecompressBuffer)
+#pragma alloc_text(PAGE, RtlDecompressFragment)
+#pragma alloc_text(PAGE, RtlDescribeChunk)
+#pragma alloc_text(PAGE, RtlReserveChunk)
+#pragma alloc_text(PAGE, RtlCompressWorkSpaceSizeNS)
+#pragma alloc_text(PAGE, RtlCompressBufferNS)
+#pragma alloc_text(PAGE, RtlDecompressBufferNS)
+#pragma alloc_text(PAGE, RtlDecompressFragmentNS)
+#pragma alloc_text(PAGE, RtlDescribeChunkNS)
+#pragma alloc_text(PAGE, RtlReserveChunkNS)
+#endif
+
+
+NTSTATUS
+RtlGetCompressionWorkSpaceSize (
+ IN USHORT CompressionFormatAndEngine,
+ OUT PULONG CompressBufferWorkSpaceSize,
+ OUT PULONG CompressFragmentWorkSpaceSize
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine returns to the caller the size in bytes of the
+ different work space buffers need to perform the compression
+
+Arguments:
+
+ CompressionFormatAndEngine - Supplies the format and engine
+ specification for the compressed data.
+
+ CompressBufferWorkSpaceSize - Receives the size in bytes needed
+ to compress a buffer.
+
+ CompressBufferWorkSpaceSize - Receives the size in bytes needed
+ to decompress a fragment.
+
+Return Value:
+
+ STATUS_SUCCESS - the operation worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+--*/
+
+{
+ //
+ // Declare two variables to hold the format and engine specification
+ //
+
+ USHORT Format = CompressionFormatAndEngine & 0x00ff;
+ USHORT Engine = CompressionFormatAndEngine & 0xff00;
+
+ //
+ // make sure the format is sort of supported
+ //
+
+ if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Format & 0x00f0) {
+
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ //
+ // Call the routine to return the workspace sizes.
+ //
+
+ return RtlWorkSpaceProcs[ Format ]( Engine,
+ CompressBufferWorkSpaceSize,
+ CompressFragmentWorkSpaceSize );
+}
+
+
+NTSTATUS
+RtlCompressBuffer (
+ IN USHORT CompressionFormatAndEngine,
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG UncompressedChunkSize,
+ OUT PULONG FinalCompressedSize,
+ IN PVOID WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input an uncompressed buffer and produces
+ its compressed equivalent provided the compressed data fits within
+ the specified destination buffer.
+
+ An output variable indicates the number of bytes used to store
+ the compressed buffer.
+
+Arguments:
+
+ CompressionFormatAndEngine - Supplies the format and engine
+ specification for the compressed data.
+
+ UncompressedBuffer - Supplies a pointer to the uncompressed data.
+
+ UncompressedBufferSize - Supplies the size, in bytes, of the
+ uncompressed buffer.
+
+ CompressedBuffer - Supplies a pointer to where the compressed data
+ is to be stored.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ UncompressedChunkSize - Supplies the chunk size to use when
+ compressing the input buffer. The only valid values are
+ 512, 1024, 2048, and 4096.
+
+ FinalCompressedSize - Receives the number of bytes needed in
+ the compressed buffer to store the compressed data.
+
+ WorkSpace - Mind your own business, just give it to me.
+
+Return Value:
+
+ STATUS_SUCCESS - the compression worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
+ addition the input buffer was all zeros.
+
+ STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
+ compressed data.
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+--*/
+
+{
+ //
+ // Declare two variables to hold the format and engine specification
+ //
+
+ USHORT Format = CompressionFormatAndEngine & 0x00ff;
+ USHORT Engine = CompressionFormatAndEngine & 0xff00;
+
+ //
+ // make sure the format is sort of supported
+ //
+
+ if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Format & 0x00f0) {
+
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ //
+ // Call the compression routine for the individual format
+ //
+
+ return RtlCompressBufferProcs[ Format ]( Engine,
+ UncompressedBuffer,
+ UncompressedBufferSize,
+ CompressedBuffer,
+ CompressedBufferSize,
+ UncompressedChunkSize,
+ FinalCompressedSize,
+ WorkSpace );
+}
+
+
+NTSTATUS
+RtlDecompressBuffer (
+ IN USHORT CompressionFormat,
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer and produces
+ its uncompressed equivalent provided the uncompressed data fits
+ within the specified destination buffer.
+
+ An output variable indicates the number of bytes used to store the
+ uncompressed data.
+
+Arguments:
+
+ CompressionFormat - Supplies the format of the compressed data.
+
+ UncompressedBuffer - Supplies a pointer to where the uncompressed
+ data is to be stored.
+
+ UncompressedBufferSize - Supplies the size, in bytes, of the
+ uncompressed buffer.
+
+ CompressedBuffer - Supplies a pointer to the compressed data.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ FinalUncompressedSize - Receives the number of bytes needed in
+ the uncompressed buffer to store the uncompressed data.
+
+Return Value:
+
+ STATUS_SUCCESS - the decompression worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+--*/
+
+{
+ //
+ // Declare two variables to hold the format specification
+ //
+
+ USHORT Format = CompressionFormat & 0x00ff;
+
+ //
+ // make sure the format is sort of supported
+ //
+
+ if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Format & 0x00f0) {
+
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ //
+ // Call the compression routine for the individual format
+ //
+
+ return RtlDecompressBufferProcs[ Format ]( UncompressedBuffer,
+ UncompressedBufferSize,
+ CompressedBuffer,
+ CompressedBufferSize,
+ FinalUncompressedSize );
+}
+
+
+NTSTATUS
+RtlDecompressFragment (
+ IN USHORT CompressionFormat,
+ OUT PUCHAR UncompressedFragment,
+ IN ULONG UncompressedFragmentSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG FragmentOffset,
+ OUT PULONG FinalUncompressedSize,
+ IN PVOID WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer and extract an
+ uncompressed fragment.
+
+ Output bytes are copied to the fragment buffer until either the
+ fragment buffer is full or the end of the uncompressed buffer is
+ reached.
+
+ An output variable indicates the number of bytes used to store the
+ uncompressed fragment.
+
+Arguments:
+
+ CompressionFormat - Supplies the format of the compressed data.
+
+ UncompressedFragment - Supplies a pointer to where the uncompressed
+ fragment is to be stored.
+
+ UncompressedFragmentSize - Supplies the size, in bytes, of the
+ uncompressed fragment buffer.
+
+ CompressedBuffer - Supplies a pointer to the compressed data buffer.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ FragmentOffset - Supplies the offset (zero based) where the uncompressed
+ fragment is being extract from. The offset is the position within
+ the original uncompressed buffer.
+
+ FinalUncompressedSize - Receives the number of bytes needed in
+ the Uncompressed fragment buffer to store the data.
+
+Return Value:
+
+ STATUS_SUCCESS - the operation worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+--*/
+
+{
+ //
+ // Declare two variables to hold the format specification
+ //
+
+ USHORT Format = CompressionFormat & 0x00ff;
+
+ //
+ // make sure the format is sort of supported
+ //
+
+ if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Format & 0x00f0) {
+
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ //
+ // Call the compression routine for the individual format
+ //
+
+ return RtlDecompressFragmentProcs[ Format ]( UncompressedFragment,
+ UncompressedFragmentSize,
+ CompressedBuffer,
+ CompressedBufferSize,
+ FragmentOffset,
+ FinalUncompressedSize,
+ WorkSpace );
+}
+
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+RtlDescribeChunk (
+ IN USHORT CompressionFormat,
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ OUT PULONG ChunkSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer, and returns
+ a description of the current chunk in that buffer, updating
+ the CompressedBuffer pointer to point to the next chunk (if
+ there is one).
+
+Arguments:
+
+ CompressionFormat - Supplies the format of the compressed data.
+
+ CompressedBuffer - Supplies a pointer to the current chunk in
+ the compressed data, and returns pointing to the next chunk
+
+ EndOfCompressedBufferPlus1 - Points at first byte beyond
+ compressed buffer
+
+ ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
+ is nonzero, else undefined
+
+ ChunkSize - Receives the size of the current chunk pointed
+ to by CompressedBuffer. Returns 0 if STATUS_NO_MORE_ENTRIES.
+
+Return Value:
+
+ STATUS_SUCCESS - the decompression worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+ STATUS_NO_MORE_ENTRIES - There is no chunk at the current pointer.
+
+--*/
+
+{
+ //
+ // Declare two variables to hold the format specification
+ //
+
+ USHORT Format = CompressionFormat & 0x00ff;
+
+ //
+ // make sure the format is sort of supported
+ //
+
+ if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Format & 0x00f0) {
+
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ //
+ // Call the compression routine for the individual format
+ //
+
+ return RtlDescribeChunkProcs[ Format ]( CompressedBuffer,
+ EndOfCompressedBufferPlus1,
+ ChunkBuffer,
+ ChunkSize );
+}
+
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+RtlReserveChunk (
+ IN USHORT CompressionFormat,
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ IN ULONG ChunkSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer, and reserves
+ space for a chunk of the specified size - filling in any pattern
+ as is necessary for a chunk of that size. On return it has
+ updated the CompressedBuffer pointer to point to the next chunk (if
+ there is one).
+
+Arguments:
+
+ CompressionFormat - Supplies the format of the compressed data.
+
+ CompressedBuffer - Supplies a pointer to the current chunk in
+ the compressed data, and returns pointing to the next chunk
+
+ EndOfCompressedBufferPlus1 - Points at first byte beyond
+ compressed buffer
+
+ ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
+ is nonzero, else undefined
+
+ ChunkSize - Supplies the compressed size of the chunk to be received.
+ Two special values are 0, and whatever the maximum
+ uncompressed chunk size is for the routine. 0 means
+ the chunk should be filled with a pattern that equates
+ to all 0's. The maximum chunk size implies that the
+ compression routine should prepare to receive all of the
+ data in uncompressed form.
+
+Return Value:
+
+ STATUS_SUCCESS - the decompression worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+--*/
+
+{
+ //
+ // Declare two variables to hold the format specification
+ //
+
+ USHORT Format = CompressionFormat & 0x00ff;
+
+ //
+ // make sure the format is sort of supported
+ //
+
+ if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (Format & 0x00f0) {
+
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+
+ //
+ // Call the compression routine for the individual format
+ //
+
+ return RtlReserveChunkProcs[ Format ]( CompressedBuffer,
+ EndOfCompressedBufferPlus1,
+ ChunkBuffer,
+ ChunkSize );
+}
+
+
+NTSTATUS
+RtlDecompressChunks (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN PUCHAR CompressedTail,
+ IN ULONG CompressedTailSize,
+ IN PCOMPRESSED_DATA_INFO CompressedDataInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer which is a stream
+ of chunks and decompresses it into the specified destination buffer.
+ The compressed data may be in two pieces, such that the "tail" of
+ the buffer is top aligned in the buffer at CompressedTail, and the
+ rest of the data is top aligned in the CompressedBuffer. The
+ CompressedBuffer can overlap and be top-aligned in the UncompressedBuffer,
+ to allow something close to in-place decompression. The CompressedTail
+ must be large enough to completely contain the final chunk and it
+ chunk header.
+
+Arguments:
+
+ UncompressedBuffer - Supplies a pointer to where the uncompressed
+ data is to be stored.
+
+ UncompressedBufferSize - Supplies the size, in bytes, of the
+ uncompressed buffer.
+
+ CompressedBuffer - Supplies a pointer to the compressed data, part 1.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ CompressedTail - Supplies a pointer to the compressed data, part 2,
+ which must be the bytes immediately following the CompressedBuffer.
+
+ CompressedTailSize - Supplies the size of the CompressedTail.
+
+ CompressedDataInfo - Supplies a complete description of the
+ compressed data with all the chunk sizes and compression
+ parameters.
+
+Return Value:
+
+ STATUS_SUCCESS - the decompression worked without a hitch.
+
+ STATUS_INVALID_PARAMETER - The specified format is illegal
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+ STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
+ is not support.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PULONG CurrentCompressedChunkSize;
+ ULONG SizeToDecompress, FinalUncompressedSize;
+ ULONG ChunksToGo = CompressedDataInfo->NumberOfChunks;
+ ULONG UncompressedChunkSize = 1 << CompressedDataInfo->ChunkShift;
+
+ CurrentCompressedChunkSize = &CompressedDataInfo->CompressedChunkSizes[0];
+
+ //
+ // Loop to decompress chunks.
+ //
+
+ do {
+
+ //
+ // Calculate uncompressed size of next chunk to decompress.
+ //
+
+ SizeToDecompress = UncompressedBufferSize;
+ if (SizeToDecompress >= UncompressedChunkSize) {
+ SizeToDecompress = UncompressedChunkSize;
+ }
+
+ //
+ // If the next chunk is all zeros, then zero it.
+ //
+
+ if ((ChunksToGo == 0) || (*CurrentCompressedChunkSize == 0)) {
+
+ RtlZeroMemory( UncompressedBuffer, SizeToDecompress );
+
+ //
+ // Test for out of chunks here and set to 1, so we can
+ // unconditionally decrement below.
+ //
+
+ if (ChunksToGo == 0) {
+ ChunksToGo = 1;
+ }
+
+ //
+ // If the next chunk is not compressed, just copy it.
+ //
+
+ } else if (*CurrentCompressedChunkSize == UncompressedChunkSize) {
+
+ //
+ // Does this chunk extend beyond the end of the current
+ // buffer? If so, that probably means we can move the
+ // first part of the chunk, and then switch to the Compressed
+ // tail to get the rest.
+ //
+
+ if (SizeToDecompress >= CompressedBufferSize) {
+
+ //
+ // If we have already switched to the tail, then this must
+ // be badly formatted compressed data.
+ //
+
+ if ((CompressedTailSize == 0) && (SizeToDecompress > CompressedBufferSize)) {
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // Copy the first part, and then the second part from the tail.
+ // Then switch to make the tail the current buffer.
+ //
+
+ RtlCopyMemory( UncompressedBuffer, CompressedBuffer, CompressedBufferSize );
+ RtlCopyMemory( UncompressedBuffer + CompressedBufferSize,
+ CompressedTail,
+ SizeToDecompress - CompressedBufferSize );
+
+ //
+ // If we exhausted the first buffer, move into the tail, knowing
+ // that we adjust these pointers by *CurrentCompressedChunkSize
+ // below.
+ //
+
+ CompressedBuffer = CompressedTail - CompressedBufferSize;
+ CompressedBufferSize = CompressedTailSize + CompressedBufferSize;
+ CompressedTailSize = 0;
+
+ //
+ // Otherwise we can just copy the whole chunk.
+ //
+
+ } else {
+ RtlCopyMemory( UncompressedBuffer, CompressedBuffer, SizeToDecompress );
+ }
+
+ //
+ // Otherwise it is a normal chunk to decompress.
+ //
+
+ } else {
+
+ //
+ // Does this chunk extend beyond the end of the current
+ // buffer? If so, that probably means we can move the
+ // first part of the chunk, and then switch to the Compressed
+ // tail to get the rest. Since the tail must be at least
+ // ChunkSize, the last chunk cannot be the one that is
+ // overlapping into the tail. Therefore, it is safe for
+ // us to copy the chunk to decompress into the last chunk
+ // of the uncompressed buffer, and decompress it from there.
+ //
+
+ if (*CurrentCompressedChunkSize > CompressedBufferSize) {
+
+ //
+ // If we have already switched to the tail, then this must
+ // be badly formatted compressed data.
+ //
+
+ if (CompressedTailSize == 0) {
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // Move the beginning of the chunk to the beginning of the last
+ // chunk in the uncompressed buffer. This move could overlap.
+ //
+
+ RtlMoveMemory( UncompressedBuffer + UncompressedBufferSize - UncompressedChunkSize,
+ CompressedBuffer,
+ CompressedBufferSize );
+
+ //
+ // Move the rest of the chunk from the tail.
+ //
+
+ RtlCopyMemory( UncompressedBuffer + UncompressedBufferSize - UncompressedChunkSize + CompressedBufferSize,
+ CompressedTail,
+ *CurrentCompressedChunkSize - CompressedBufferSize );
+
+ //
+ // We temporarily set CompressedBuffer to describe where we
+ // copied the chunk to make the call in common code, then we
+ // switch it into the tail below.
+ //
+
+ CompressedBuffer = UncompressedBuffer + UncompressedBufferSize - UncompressedChunkSize;
+ }
+
+ //
+ // Attempt the decompress.
+ //
+
+ Status =
+ RtlDecompressBuffer( CompressedDataInfo->CompressionFormatAndEngine,
+ UncompressedBuffer,
+ SizeToDecompress,
+ CompressedBuffer,
+ *CurrentCompressedChunkSize,
+ &FinalUncompressedSize );
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // If we did not get a full chunk, zero the rest.
+ //
+
+ if (SizeToDecompress > FinalUncompressedSize) {
+ RtlZeroMemory( UncompressedBuffer + FinalUncompressedSize,
+ SizeToDecompress - FinalUncompressedSize );
+ }
+
+ //
+ // If we exhausted the first buffer, move into the tail, knowing
+ // that we adjust these pointers by *CurrentCompressedChunkSize
+ // below.
+ //
+
+ if (*CurrentCompressedChunkSize >= CompressedBufferSize) {
+ CompressedBuffer = CompressedTail - CompressedBufferSize;
+ CompressedBufferSize = CompressedTailSize + CompressedBufferSize;
+ CompressedTailSize = 0;
+ }
+ }
+
+ //
+ // Update for next possible pass through the loop.
+ //
+
+ UncompressedBuffer += SizeToDecompress;
+ UncompressedBufferSize -= SizeToDecompress;
+ CompressedBuffer += *CurrentCompressedChunkSize;
+ CompressedBufferSize -= *CurrentCompressedChunkSize;
+ CurrentCompressedChunkSize += 1;
+ ChunksToGo -= 1;
+
+ } while (UncompressedBufferSize != 0);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlCompressChunks(
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN OUT PCOMPRESSED_DATA_INFO CompressedDataInfo,
+ IN ULONG CompressedDataInfoLength,
+ IN PVOID WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input an uncompressed buffer and produces
+ its compressed equivalent provided the compressed data fits within
+ the specified destination buffer.
+
+ The desired compression parameters must be supplied via the
+ CompressedDataInfo structure, and this structure then returns all
+ of the compressed chunk sizes.
+
+ Note that since any given chunk (or all chunks) can simply be
+ transmitted uncompressed, all error possibilities are actually
+ stopped in this routine, except for STATUS_BUFFER_TOO_SMALL.
+ This code will be returned when the data is not compressing
+ sufficiently to warrant sending the data compressed. The caller
+ must field this error, and send the data uncompressed.
+
+Arguments:
+
+ UncompressedBuffer - Supplies a pointer to the uncompressed data.
+
+ UncompressedBufferSize - Supplies the size, in bytes, of the
+ uncompressed buffer.
+
+ CompressedBuffer - Supplies a pointer to where the compressed data
+ is to be stored.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ CompressedDataInfo - Supplies the compression parameters, such as
+ CompressionFormat, CompressionUnitSize, ChunkSize and ClusterSize,
+ returns all of the compressed chunk sizes.
+
+ CompressedDataInfoLength - Size of the supplied CompressedDataInfo
+ in bytes.
+
+ WorkSpace - A workspace area of the correct size as returned from
+ RtlGetCompressionWorkSpaceSize.
+
+Return Value:
+
+ STATUS_SUCCESS - the compression worked without a hitch.
+
+ STATUS_BUFFER_TOO_SMALL - the data is not compressing sufficiently to
+ warrant sending the data compressed.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PULONG CurrentCompressedChunkSize;
+ ULONG SizeToCompress, FinalCompressedSize;
+ ULONG UncompressedChunkSize = 1 << CompressedDataInfo->ChunkShift;
+
+ //
+ // Make sure CompressedDataInfo is long enough.
+ //
+
+ ASSERT(CompressedDataInfoLength >=
+ (sizeof(COMPRESSED_DATA_INFO) +
+ ((UncompressedBufferSize - 1) >> (CompressedDataInfo->ChunkShift - 2))));
+
+ //
+ // For the worst case, the compressed buffer actually has to be
+ // the same size as the uncompressed buffer, minus 1/16th. We then
+ // will actually use that size. If the data is not compressing very
+ // well, it is cheaper for us to actually send the data to the
+ // server uncompressed, than poorly compressed, because if the
+ // data is poorly compressed, the server will end up doing an
+ // extra copy before trying to compress the data again anyway.
+ //
+
+ ASSERT(CompressedBufferSize >= (UncompressedBufferSize - (UncompressedBufferSize / 16)));
+ CompressedBufferSize = (UncompressedBufferSize - (UncompressedBufferSize / 16));
+
+ //
+ // Initialize NumberOfChunks returned and the pointer to the first chunk size.
+ //
+
+ CompressedDataInfo->NumberOfChunks = 0;
+ CurrentCompressedChunkSize = &CompressedDataInfo->CompressedChunkSizes[0];
+
+ //
+ // Loop to decompress chunks.
+ //
+
+ do {
+
+ //
+ // Calculate uncompressed size of next chunk to decompress.
+ //
+
+ SizeToCompress = UncompressedBufferSize;
+ if (SizeToCompress >= UncompressedChunkSize) {
+ SizeToCompress = UncompressedChunkSize;
+ }
+
+ //
+ // Now compress the next chunk.
+ //
+
+ Status = RtlCompressBuffer( CompressedDataInfo->CompressionFormatAndEngine,
+ UncompressedBuffer,
+ SizeToCompress,
+ CompressedBuffer,
+ CompressedBufferSize,
+ UncompressedChunkSize,
+ &FinalCompressedSize,
+ WorkSpace );
+
+ //
+ // If the Buffer was all zeros, then we will not send anything.
+ //
+
+ if (Status == STATUS_BUFFER_ALL_ZEROS) {
+
+ FinalCompressedSize = 0;
+
+ //
+ // Otherwise, if there was any kind of error (we only expect the
+ // case where the data did not compress), then just copy the
+ // data and return UncompressedChunkSize for this one.
+ //
+
+ } else if (!NT_SUCCESS(Status)) {
+
+ //
+ // The most likely error is STATUS_BUFFER_TOO_SMALL.
+ // But in any case, our only recourse would be to send
+ // the data uncompressed. To be completely safe, we
+ // see if there is enough space for an uncompressed chunk
+ // in the CompressedBuffer, and if not we return
+ // buffer too small (which is probably what we had anyway!).
+ //
+
+ if (CompressedBufferSize < UncompressedChunkSize) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ //
+ // Copy the uncompressed chunk.
+ //
+
+ RtlCopyMemory( CompressedBuffer, UncompressedBuffer, SizeToCompress );
+ if (UncompressedChunkSize > SizeToCompress) {
+ RtlZeroMemory( (PCHAR)CompressedBuffer + SizeToCompress,
+ UncompressedChunkSize - SizeToCompress );
+ }
+
+ FinalCompressedSize = UncompressedChunkSize;
+ }
+
+ ASSERT(FinalCompressedSize <= CompressedBufferSize);
+
+ //
+ // At this point, we have handled any error status.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Store the final chunk size.
+ //
+
+ *CurrentCompressedChunkSize = FinalCompressedSize;
+ CurrentCompressedChunkSize += 1;
+ CompressedDataInfo->NumberOfChunks += 1;
+
+ //
+ // Prepare for the next trip through the loop.
+ //
+
+ UncompressedBuffer += SizeToCompress;
+ UncompressedBufferSize -= SizeToCompress;
+ CompressedBuffer += FinalCompressedSize;
+ CompressedBufferSize -= FinalCompressedSize;
+
+ } while (UncompressedBufferSize != 0);
+
+ return Status;
+}
+
+
+NTSTATUS
+RtlCompressWorkSpaceSizeNS (
+ IN USHORT CompressionEngine,
+ OUT PULONG CompressBufferWorkSpaceSize,
+ OUT PULONG CompressFragmentWorkSpaceSize
+ )
+{
+ return STATUS_UNSUPPORTED_COMPRESSION;
+}
+
+NTSTATUS
+RtlCompressBufferNS (
+ IN USHORT CompressionEngine,
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG UncompressedChunkSize,
+ OUT PULONG FinalCompressedSize,
+ IN PVOID WorkSpace
+ )
+{
+ return STATUS_UNSUPPORTED_COMPRESSION;
+}
+
+NTSTATUS
+RtlDecompressBufferNS (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ )
+{
+ return STATUS_UNSUPPORTED_COMPRESSION;
+}
+
+NTSTATUS
+RtlDecompressFragmentNS (
+ OUT PUCHAR UncompressedFragment,
+ IN ULONG UncompressedFragmentSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG FragmentOffset,
+ OUT PULONG FinalUncompressedSize,
+ IN PVOID WorkSpace
+ )
+{
+ return STATUS_UNSUPPORTED_COMPRESSION;
+}
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+RtlDescribeChunkNS (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ OUT PULONG ChunkSize
+ )
+
+{
+ return STATUS_UNSUPPORTED_COMPRESSION;
+}
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+RtlReserveChunkNS (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ IN ULONG ChunkSize
+ )
+
+{
+ return STATUS_UNSUPPORTED_COMPRESSION;
+}
+
diff --git a/private/ntos/rtl/debug.c b/private/ntos/rtl/debug.c
new file mode 100644
index 000000000..752536ca8
--- /dev/null
+++ b/private/ntos/rtl/debug.c
@@ -0,0 +1,215 @@
+// TITLE("Debug Support Functions")
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+//
+// Module Name:
+//
+// debug.c
+//
+// Abstract:
+//
+// This module implements functions to support debugging NT. They call
+// architecture specific routines to do the actual work.
+//
+// Author:
+//
+// Steven R. Wood (stevewo) 8-Nov-1994
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "stdarg.h"
+#include "stdio.h"
+#include "ntrtlp.h"
+#include <ntdbg.h>
+
+ULONG
+DbgPrint(
+ PCHAR Format,
+ ...
+ )
+{
+ va_list arglist;
+ UCHAR Buffer[512];
+ int cb;
+ STRING Output;
+
+ //
+ // Format the output into a buffer and then print it.
+ //
+
+ va_start(arglist, Format);
+ cb = _vsnprintf(Buffer, sizeof(Buffer), Format, arglist);
+ if (cb == -1) { // detect buffer overflow
+ cb = sizeof(Buffer);
+ Buffer[sizeof(Buffer) - 1] = '\n';
+ }
+ Output.Buffer = Buffer;
+ Output.Length = (USHORT) cb;
+
+ //
+ // If APP is being debugged, raise an exception and the debugger
+ // will catch and handle this. Otherwise, kernel debugger service
+ // is called.
+ //
+
+#if !defined(BLDR_KERNEL_RUNTIME) && !defined(NTOS_KERNEL_RUNTIME)
+#if !i386
+ //
+ // For non-Intel architectures, can't raise exceptions until the PebLock
+ // is initialized, since the Function Table lookup code uses the PebLock
+ // to serialize access to the loaded module database. What a crock
+ //
+ if (NtCurrentPeb()->FastPebLockRoutine != NULL)
+#endif //!i386
+ if (NtCurrentPeb()->BeingDebugged) {
+ EXCEPTION_RECORD ExceptionRecord;
+
+ //
+ // Construct an exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = DBG_PRINTEXCEPTION_C;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 2;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.ExceptionInformation[ 0 ] = Output.Length + 1;
+ ExceptionRecord.ExceptionInformation[ 1 ] = (ULONG)(Output.Buffer);
+ RtlRaiseException( &ExceptionRecord );
+ return STATUS_SUCCESS;
+ }
+#endif
+ return DebugPrint( &Output );
+}
+
+
+ULONG
+DbgPrompt(
+ IN PCHAR Prompt,
+ OUT PCHAR Response,
+ IN ULONG MaximumResponseLength
+ )
+
+//++
+//
+// Routine Description:
+//
+// This function displays the prompt string on the debugging console and
+// then reads a line of text from the debugging console. The line read
+// is returned in the memory pointed to by the second parameter. The
+// third parameter specifies the maximum number of characters that can
+// be stored in the response area.
+//
+// Arguments:
+//
+// Prompt - specifies the text to display as the prompt.
+//
+// Response - specifies where to store the response read from the
+// debugging console.
+//
+// Prompt - specifies the maximum number of characters that can be
+// stored in the Response buffer.
+//
+// Return Value:
+//
+// Number of characters stored in the Response buffer. Includes the
+// terminating newline character, but not the null character after
+// that.
+//
+//--
+
+{
+
+ STRING Input;
+ STRING Output;
+
+ //
+ // Output the prompt string and read input.
+ //
+
+ Input.MaximumLength = (USHORT)MaximumResponseLength;
+ Input.Buffer = Response;
+ Output.Length = (USHORT)strlen( Prompt );
+ Output.Buffer = Prompt;
+ return DebugPrompt( &Output, &Input );
+}
+
+
+#if defined(NTOS_KERNEL_RUNTIME)
+VOID
+DbgLoadImageSymbols(
+ IN PSTRING FileName,
+ IN PVOID ImageBase,
+ IN ULONG ProcessId
+ )
+
+//++
+//
+// Routine Description:
+//
+// Tells the debugger about newly loaded symbols.
+//
+// Arguments:
+//
+// Return Value:
+//
+//--
+
+{
+ PIMAGE_NT_HEADERS NtHeaders;
+ KD_SYMBOLS_INFO SymbolInfo;
+
+ SymbolInfo.BaseOfDll = ImageBase;
+ SymbolInfo.ProcessId = ProcessId;
+ NtHeaders = RtlImageNtHeader( ImageBase );
+ if (NtHeaders) {
+ SymbolInfo.CheckSum = (ULONG)NtHeaders->OptionalHeader.CheckSum;
+ SymbolInfo.SizeOfImage = (ULONG)NtHeaders->OptionalHeader.SizeOfImage;
+ }
+ else {
+ SymbolInfo.CheckSum = 0;
+ SymbolInfo.SizeOfImage = 0;
+ }
+
+ DebugLoadImageSymbols( FileName, &SymbolInfo);
+ return;
+}
+
+
+VOID
+DbgUnLoadImageSymbols (
+ IN PSTRING FileName,
+ IN PVOID ImageBase,
+ IN ULONG ProcessId
+ )
+
+//++
+//
+// Routine Description:
+//
+// Tells the debugger about newly unloaded symbols.
+//
+// Arguments:
+//
+// Return Value:
+//
+//--
+
+{
+ KD_SYMBOLS_INFO SymbolInfo;
+
+ SymbolInfo.BaseOfDll = ImageBase;
+ SymbolInfo.ProcessId = ProcessId;
+ SymbolInfo.CheckSum = 0;
+ SymbolInfo.SizeOfImage = 0;
+
+ DebugUnLoadImageSymbols( FileName, &SymbolInfo );
+ return;
+}
+#endif // defined(NTOS_KERNEL_RUNTIME)
diff --git a/private/ntos/rtl/dirs b/private/ntos/rtl/dirs
new file mode 100644
index 000000000..dbed03387
--- /dev/null
+++ b/private/ntos/rtl/dirs
@@ -0,0 +1,27 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=user \
+ up \
+ boot
+
+OPTIONAL_DIRS=mp
diff --git a/private/ntos/rtl/eballoc.c b/private/ntos/rtl/eballoc.c
new file mode 100644
index 000000000..204a680b1
--- /dev/null
+++ b/private/ntos/rtl/eballoc.c
@@ -0,0 +1,59 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ eballoc.c
+
+Abstract:
+
+ Process/Thread Environment Block allocation functions
+
+Author:
+
+ Steve Wood (stevewo) 10-May-1990
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include <nturtl.h>
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlAcquirePebLock)
+#pragma alloc_text(PAGE,RtlReleasePebLock)
+#endif
+
+typedef VOID (*PEB_LOCK_ROUTINE)(PVOID FastLock);
+
+VOID
+RtlAcquirePebLock( VOID )
+{
+ PEB_LOCK_ROUTINE LockRoutine;
+ PPEB Peb;
+
+ RTL_PAGED_CODE();
+
+ Peb = NtCurrentPeb();
+
+ LockRoutine = (PEB_LOCK_ROUTINE)Peb->FastPebLockRoutine;
+ ASSERT(LockRoutine);
+ (LockRoutine)(Peb->FastPebLock);
+}
+
+VOID
+RtlReleasePebLock( VOID )
+{
+ PEB_LOCK_ROUTINE LockRoutine;
+ PPEB Peb;
+
+ RTL_PAGED_CODE();
+
+ Peb = NtCurrentPeb();
+
+ LockRoutine = (PEB_LOCK_ROUTINE)Peb->FastPebUnlockRoutine;
+ ASSERT(LockRoutine);
+ (LockRoutine)(Peb->FastPebLock);
+}
diff --git a/private/ntos/rtl/environ.c b/private/ntos/rtl/environ.c
new file mode 100644
index 000000000..ed06022cc
--- /dev/null
+++ b/private/ntos/rtl/environ.c
@@ -0,0 +1,839 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ environ.c
+
+Abstract:
+
+ Environment Variable support
+
+Author:
+
+ Steven R. Wood (stevewo) 30-Jan-1991
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "zwapi.h"
+#include "nturtl.h"
+#include "string.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(INIT,RtlCreateEnvironment )
+#pragma alloc_text(INIT,RtlDestroyEnvironment )
+#pragma alloc_text(INIT,RtlSetCurrentEnvironment )
+#pragma alloc_text(INIT,RtlQueryEnvironmentVariable_U )
+#pragma alloc_text(INIT,RtlSetEnvironmentVariable )
+#endif
+
+NTSTATUS
+RtlCreateEnvironment(
+ IN BOOLEAN CloneCurrentEnvironment OPTIONAL,
+ OUT PVOID *Environment
+ )
+{
+ NTSTATUS Status;
+ MEMORY_BASIC_INFORMATION MemoryInformation;
+ PVOID pNew, pOld;
+
+ //
+ // If not cloning a copy of the current process's environment variable
+ // block, just allocate a block of committed memory and return its
+ // address.
+ //
+
+ pNew = NULL;
+ if (!CloneCurrentEnvironment) {
+createEmptyEnvironment:
+ MemoryInformation.RegionSize = 1;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &pNew,
+ 0,
+ &MemoryInformation.RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (NT_SUCCESS( Status )) {
+ *Environment = pNew;
+ }
+
+ return( Status );
+ }
+
+ //
+ // Acquire the Peb Lock for the duration while we munge the environment
+ // variable storage block.
+ //
+
+ RtlAcquirePebLock();
+
+ //
+ // Capture the pointer to the current process's environment variable
+ // block and initialize the new pointer to null for our finally clause.
+ //
+
+ pOld = NtCurrentPeb()->ProcessParameters->Environment;
+ if (pOld == NULL) {
+ RtlReleasePebLock();
+ goto createEmptyEnvironment;
+ }
+
+ try {
+ //
+ // Query the current size of the current process's environment
+ // variable block. Return status if failure.
+ //
+
+ Status = ZwQueryVirtualMemory( NtCurrentProcess(),
+ pOld,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Allocate memory to contain a copy of the current process's
+ // environment variable block. Return status if failure.
+ //
+
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &pNew,
+ 0,
+ &MemoryInformation.RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Copy the current process's environment to the allocated memory
+ // and return a pointer to the copy.
+ //
+
+ RtlMoveMemory( pNew, pOld, MemoryInformation.RegionSize );
+ *Environment = pNew;
+ }
+ finally {
+ if (AbnormalTermination()) {
+ Status = STATUS_ACCESS_VIOLATION;
+ if (pNew != NULL) {
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ &pNew,
+ &MemoryInformation.RegionSize,
+ MEM_RELEASE
+ );
+ }
+ }
+
+ RtlReleasePebLock();
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlDestroyEnvironment(
+ IN PVOID Environment
+ )
+{
+ NTSTATUS Status;
+ ULONG RegionSize;
+
+ //
+ // Free the specified environment variable block.
+ //
+
+ RegionSize = 0;
+ Status = ZwFreeVirtualMemory( NtCurrentProcess(),
+ &Environment,
+ &RegionSize,
+ MEM_RELEASE
+ );
+ //
+ // Return status.
+ //
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlSetCurrentEnvironment(
+ IN PVOID Environment,
+ OUT PVOID *PreviousEnvironment OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PVOID pOld;
+
+ //
+ // Acquire the Peb Lock for the duration while we munge the environment
+ // variable storage block.
+ //
+
+ RtlAcquirePebLock();
+
+ try {
+ //
+ // Capture current process's environment variable block pointer to
+ // return to caller or destroy.
+ //
+
+ pOld = NtCurrentPeb()->ProcessParameters->Environment;
+
+ //
+ // Change current process's environment variable block pointer to
+ // point to the passed block.
+ //
+
+
+ NtCurrentPeb()->ProcessParameters->Environment = Environment;
+
+ //
+ // If caller requested it, return the pointer to the previous
+ // process environment variable block and set the local variable
+ // to NULL so we dont destroy it below.
+ //
+
+ if (ARGUMENT_PRESENT( PreviousEnvironment )) {
+ *PreviousEnvironment = pOld;
+ pOld = NULL;
+ }
+ }
+ finally {
+ if (AbnormalTermination()) {
+ Status = STATUS_ACCESS_VIOLATION;
+ pOld = NULL;
+ }
+ }
+
+ //
+ // Release the Peb Lock
+ //
+
+ RtlReleasePebLock();
+
+
+ //
+ // If old environment not returned to caller, destroy it.
+ //
+
+ if (pOld != NULL) {
+ RtlDestroyEnvironment( pOld );
+ }
+
+ //
+ // Return status
+ //
+
+ return( Status );
+}
+
+BOOLEAN RtlpEnvironCacheValid;
+UNICODE_STRING RtlpEnvironCacheName;
+UNICODE_STRING RtlpEnvironCacheValue;
+
+NTSTATUS
+RtlQueryEnvironmentVariable_U(
+ IN PVOID Environment OPTIONAL,
+ IN PUNICODE_STRING Name,
+ IN OUT PUNICODE_STRING Value
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING CurrentName;
+ UNICODE_STRING CurrentValue;
+ PWSTR p;
+ PPEB Peb;
+
+ Status = STATUS_VARIABLE_NOT_FOUND;
+ Peb = NtCurrentPeb();
+
+ if (ARGUMENT_PRESENT( Environment )) {
+ p = Environment;
+ }
+ else {
+ //
+ // Acquire the Peb Lock for the duration while we munge the
+ // environment variable storage block.
+ //
+
+ RtlAcquirePebLock();
+
+ //
+ // Capture the pointer to the current process's environment variable
+ // block.
+ //
+
+ p = Peb->ProcessParameters->Environment;
+
+ }
+#if DBG
+ if (*p == UNICODE_NULL)
+ DbgPrint( "RTL: QEV - Empty Environment being searched: %08x\n", p);
+ else if ((UCHAR)((*p) >> 8) != '\0')
+ DbgPrint( "RTL: QEV - Possible ANSI Environment being searched: %08x\n", p);
+#endif
+
+ try {
+
+
+ if ( RtlpEnvironCacheValid && p == Peb->ProcessParameters->Environment ) {
+ if (RtlEqualUnicodeString( Name, &RtlpEnvironCacheName, TRUE )) {
+
+ //
+ // Names are equal. Always return the length of the
+ // value string, excluding the terminating null. If
+ // there is room in the caller's buffer, return a copy
+ // of the value string and success status. Otherwise
+ // return an error status. In the latter case, the caller
+ // can examine the length field of their value string
+ // so they can determine much memory is needed.
+ //
+
+ Value->Length = RtlpEnvironCacheValue.Length;
+ if (Value->MaximumLength >= RtlpEnvironCacheValue.Length) {
+ RtlMoveMemory( Value->Buffer,
+ RtlpEnvironCacheValue.Buffer,
+ RtlpEnvironCacheValue.Length
+ );
+ //
+ // Null terminate returned string if there is room.
+ //
+
+ if (Value->MaximumLength > RtlpEnvironCacheValue.Length) {
+ Value->Buffer[ RtlpEnvironCacheValue.Length/sizeof(WCHAR) ] = L'\0';
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ goto environcachehit;
+ }
+ }
+
+ //
+ // The environment variable block consists of zero or more null
+ // terminated UNICODE strings. Each string is of the form:
+ //
+ // name=value
+ //
+ // where the null termination is after the value.
+ //
+
+ if (p != NULL) while (*p) {
+ //
+ // Determine the size of the name and value portions of
+ // the current string of the environment variable block.
+ //
+
+ CurrentName.Buffer = p;
+ CurrentName.Length = 0;
+ CurrentName.MaximumLength = 0;
+ while (*p) {
+ //
+ // If we see an equal sign, then compute the size of
+ // the name portion and scan for the end of the value.
+ //
+
+ if (*p == L'=' && p != CurrentName.Buffer) {
+ CurrentName.Length = (USHORT)(p - CurrentName.Buffer)*sizeof(WCHAR);
+ CurrentName.MaximumLength = (USHORT)(CurrentName.Length+sizeof(WCHAR));
+ CurrentValue.Buffer = ++p;
+
+ while(*p) {
+ p++;
+ }
+ CurrentValue.Length = (USHORT)(p - CurrentValue.Buffer)*sizeof(WCHAR);
+ CurrentValue.MaximumLength = (USHORT)(CurrentValue.Length+sizeof(WCHAR));
+
+ //
+ // At this point we have the length of both the name
+ // and value portions, so exit the loop so we can
+ // do the compare.
+ //
+ break;
+ }
+ else {
+ p++;
+ }
+ }
+
+ //
+ // Skip over the terminating null character for this name=value
+ // pair in preparation for the next iteration of the loop.
+ //
+
+ p++;
+
+ //
+ // Compare the current name with the one requested, ignore
+ // case.
+ //
+
+ if (RtlEqualUnicodeString( Name, &CurrentName, TRUE )) {
+ //
+ // Names are equal. Always return the length of the
+ // value string, excluding the terminating null. If
+ // there is room in the caller's buffer, return a copy
+ // of the value string and success status. Otherwise
+ // return an error status. In the latter case, the caller
+ // can examine the length field of their value string
+ // so they can determine much memory is needed.
+ //
+
+ Value->Length = CurrentValue.Length;
+ if (Value->MaximumLength >= CurrentValue.Length) {
+ RtlMoveMemory( Value->Buffer,
+ CurrentValue.Buffer,
+ CurrentValue.Length
+ );
+ //
+ // Null terminate returned string if there is room.
+ //
+
+ if (Value->MaximumLength > CurrentValue.Length) {
+ Value->Buffer[ CurrentValue.Length/sizeof(WCHAR) ] = L'\0';
+ }
+
+ if ( !Environment || Environment == Peb->ProcessParameters->Environment) {
+ RtlpEnvironCacheValid = TRUE;
+ RtlpEnvironCacheName = CurrentName;
+ RtlpEnvironCacheValue = CurrentValue;
+ }
+
+ Status = STATUS_SUCCESS;
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ break;
+ }
+ }
+environcachehit:;
+ }
+ finally {
+ //
+ // If abnormally terminating, assume access violation.
+ //
+
+ if (AbnormalTermination()) {
+ Status = STATUS_ACCESS_VIOLATION;
+ }
+
+ //
+ // Release the Peb lock.
+ //
+
+ if (!ARGUMENT_PRESENT( Environment )) {
+ RtlReleasePebLock();
+ }
+ }
+
+ //
+ // Return status.
+ //
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlSetEnvironmentVariable(
+ IN OUT PVOID *Environment OPTIONAL,
+ IN PUNICODE_STRING Name,
+ IN PUNICODE_STRING Value OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ MEMORY_BASIC_INFORMATION MemoryInformation;
+ UNICODE_STRING CurrentName;
+ UNICODE_STRING CurrentValue;
+ PVOID pOld, pNew;
+ ULONG Size, NewSize;
+ LONG CompareResult;
+ PWSTR p, pStart, pEnd;
+
+ RtlpEnvironCacheValid = FALSE;
+ Status = STATUS_VARIABLE_NOT_FOUND;
+ if (ARGUMENT_PRESENT( Environment )) {
+ pOld = *Environment;
+ }
+ else {
+ //
+ // Acquire the Peb Lock for the duration while we munge the
+ // environment variable storage block.
+ //
+
+ RtlAcquirePebLock();
+
+ //
+ // Capture the pointer to the current process's environment variable
+ // block.
+ //
+
+ pOld = NtCurrentPeb()->ProcessParameters->Environment;
+ }
+ pNew = NULL;
+
+ try {
+ //
+ // The environment variable block consists of zero or more null
+ // terminated UNICODE strings. Each string is of the form:
+ //
+ // name=value
+ //
+ // where the null termination is after the value.
+ //
+
+ p = pOld;
+ pEnd = NULL;
+ if (p != NULL) while (*p) {
+ //
+ // Determine the size of the name and value portions of
+ // the current string of the environment variable block.
+ //
+
+ CurrentName.Buffer = p;
+ CurrentName.Length = 0;
+ CurrentName.MaximumLength = 0;
+ while (*p) {
+ //
+ // If we see an equal sign, then compute the size of
+ // the name portion and scan for the end of the value.
+ //
+
+ if (*p == L'=' && p != CurrentName.Buffer) {
+ CurrentName.Length = (USHORT)(p - CurrentName.Buffer) * sizeof(WCHAR);
+ CurrentName.MaximumLength = (USHORT)(CurrentName.Length+sizeof(WCHAR));
+ CurrentValue.Buffer = ++p;
+
+ while(*p) {
+ p++;
+ }
+ CurrentValue.Length = (USHORT)(p - CurrentValue.Buffer) * sizeof(WCHAR);
+ CurrentValue.MaximumLength = (USHORT)(CurrentValue.Length+sizeof(WCHAR));
+
+ //
+ // At this point we have the length of both the name
+ // and value portions, so exit the loop so we can
+ // do the compare.
+ //
+ break;
+ }
+ else {
+ p++;
+ }
+ }
+
+ //
+ // Skip over the terminating null character for this name=value
+ // pair in preparation for the next iteration of the loop.
+ //
+
+ p++;
+
+ //
+ // Compare the current name with the one requested, ignore
+ // case.
+ //
+
+ if (!(CompareResult = RtlCompareUnicodeString( Name, &CurrentName, TRUE ))) {
+ //
+ // Names are equal. Now find the end of the current
+ // environment variable block.
+ //
+
+ pEnd = p;
+ while (*pEnd) {
+ while (*pEnd++) {
+ }
+ }
+ pEnd++;
+
+ if (!ARGUMENT_PRESENT( Value )) {
+ //
+ // If the caller did not specify a new value, then delete
+ // the entire name=value pair by copying up the remainder
+ // of the environment variable block.
+ //
+
+ RtlMoveMemory( CurrentName.Buffer,
+ p,
+ (pEnd - p)*sizeof(WCHAR)
+ );
+ Status = STATUS_SUCCESS;
+ }
+ else
+ if (Value->Length <= CurrentValue.Length) {
+ //
+ // New value is smaller, so copy new value, then null
+ // terminate it, and then move up the remainder of the
+ // variable block so it is immediately after the new
+ // null terminated value.
+ //
+
+ pStart = CurrentValue.Buffer;
+ RtlMoveMemory( pStart, Value->Buffer, Value->Length );
+ pStart += Value->Length/sizeof(WCHAR);
+ *pStart++ = L'\0';
+
+ RtlMoveMemory( pStart, p,(pEnd - p)*sizeof(WCHAR) );
+ Status = STATUS_SUCCESS;
+ }
+ else {
+ //
+ // New value is larger, so query the current size of the
+ // environment variable block. Return status if failure.
+ //
+
+ Status = ZwQueryVirtualMemory( NtCurrentProcess(),
+ pOld,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // See if there is room for new, larger value. If not
+ // allocate a new copy of the environment variable
+ // block.
+ //
+
+ NewSize = (pEnd - (PWSTR)pOld)*sizeof(WCHAR) +
+ Value->Length - CurrentValue.Length;
+ if (NewSize >= MemoryInformation.RegionSize) {
+ //
+ // Allocate memory to contain a copy of the current
+ // process's environment variable block. Return
+ // status if failure.
+ //
+
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &pNew,
+ 0,
+ &NewSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Copy the current process's environment to the allocated memory
+ // inserting the new value as we do the copy.
+ //
+
+ Size = CurrentValue.Buffer - (PWSTR)pOld;
+ RtlMoveMemory( pNew, pOld, Size*sizeof(WCHAR) );
+ pStart = (PWSTR)pNew + Size;
+ RtlMoveMemory( pStart, Value->Buffer, Value->Length );
+ pStart += Value->Length/sizeof(WCHAR);
+ *pStart++ = L'\0';
+ RtlMoveMemory( pStart, p,(pEnd - p)*sizeof(WCHAR));
+ if (ARGUMENT_PRESENT( Environment ))
+ *Environment = pNew;
+ else {
+ NtCurrentPeb()->ProcessParameters->Environment = pNew;
+ NtCurrentPeb()->EnvironmentUpdateCount += 1;
+ }
+
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ &pOld,
+ &MemoryInformation.RegionSize,
+ MEM_RELEASE
+ );
+ pNew = pOld;
+ }
+ else {
+ pStart = CurrentValue.Buffer + Value->Length/sizeof(WCHAR) + 1;
+ RtlMoveMemory( pStart, p,(pEnd - p)*sizeof(WCHAR));
+ *--pStart = L'\0';
+
+ RtlMoveMemory( pStart - Value->Length/sizeof(WCHAR),
+ Value->Buffer,
+ Value->Length
+ );
+ }
+ }
+
+ break;
+ }
+ else
+ if (CompareResult < 0) {
+ //
+ // Request name is less than current name, then look no
+ // further as we will not find it in our sorted list.
+ // The insertion point for the new variable is before the
+ // variable just examined.
+ //
+
+ p = CurrentName.Buffer;
+ break;
+ }
+ }
+
+ //
+ // If variable name not found and a new value parameter was specified
+ // then insert the new variable name and its value at the appropriate
+ // place in the environment variable block (i.e. where p points to).
+ //
+
+ if (pEnd == NULL && ARGUMENT_PRESENT( Value )) {
+ if (p != NULL) {
+ //
+ // Name not found. Now find the end of the current
+ // environment variable block.
+ //
+
+ pEnd = p;
+ while (*pEnd) {
+ while (*pEnd++) {
+ }
+ }
+ pEnd++;
+
+ //
+ // New value is present, so query the current size of the
+ // environment variable block. Return status if failure.
+ //
+
+ Status = ZwQueryVirtualMemory( NtCurrentProcess(),
+ pOld,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // See if there is room for new, larger value. If not
+ // allocate a new copy of the environment variable
+ // block.
+ //
+
+ NewSize = (pEnd - (PWSTR)pOld) * sizeof(WCHAR) +
+ Name->Length +
+ sizeof(WCHAR) +
+ Value->Length +
+ sizeof(WCHAR);
+ }
+ else {
+ NewSize = Name->Length +
+ sizeof(WCHAR) +
+ Value->Length +
+ sizeof(WCHAR);
+ MemoryInformation.RegionSize = 0;
+ }
+
+ if (NewSize >= MemoryInformation.RegionSize) {
+ //
+ // Allocate memory to contain a copy of the current
+ // process's environment variable block. Return
+ // status if failure.
+ //
+
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &pNew,
+ 0,
+ &NewSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Copy the current process's environment to the allocated memory
+ // inserting the new value as we do the copy.
+ //
+
+ if (p != NULL) {
+ Size = p - (PWSTR)pOld;
+ RtlMoveMemory( pNew, pOld, Size*sizeof(WCHAR) );
+ }
+ else {
+ Size = 0;
+ }
+ pStart = (PWSTR)pNew + Size;
+ RtlMoveMemory( pStart, Name->Buffer, Name->Length );
+ pStart += Name->Length/sizeof(WCHAR);
+ *pStart++ = L'=';
+ RtlMoveMemory( pStart, Value->Buffer, Value->Length );
+ pStart += Value->Length/sizeof(WCHAR);
+ *pStart++ = L'\0';
+ if (p != NULL) {
+ RtlMoveMemory( pStart, p,(pEnd - p)*sizeof(WCHAR) );
+ }
+
+ if (ARGUMENT_PRESENT( Environment ))
+ *Environment = pNew;
+ else {
+ NtCurrentPeb()->ProcessParameters->Environment = pNew;
+ NtCurrentPeb()->EnvironmentUpdateCount += 1;
+ }
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ &pOld,
+ &MemoryInformation.RegionSize,
+ MEM_RELEASE
+ );
+ }
+ else {
+ pStart = p + Name->Length/sizeof(WCHAR) + 1 + Value->Length/sizeof(WCHAR) + 1;
+ RtlMoveMemory( pStart, p,(pEnd - p)*sizeof(WCHAR) );
+ RtlMoveMemory( p, Name->Buffer, Name->Length );
+ p += Name->Length/sizeof(WCHAR);
+ *p++ = L'=';
+ RtlMoveMemory( p, Value->Buffer, Value->Length );
+ p += Value->Length/sizeof(WCHAR);
+ *p++ = L'\0';
+ }
+ }
+ }
+ finally {
+ //
+ // Release the Peb lock.
+ //
+
+ if (!ARGUMENT_PRESENT( Environment )) {
+ RtlReleasePebLock();
+ }
+
+ //
+ // If abnormally terminating, assume access violation.
+ //
+
+ if (AbnormalTermination()) {
+ return (STATUS_ACCESS_VIOLATION);
+ }
+ }
+
+ //
+ // Return status.
+ //
+
+ return( Status );
+}
diff --git a/private/ntos/rtl/error.c b/private/ntos/rtl/error.c
new file mode 100644
index 000000000..fc727fbd0
--- /dev/null
+++ b/private/ntos/rtl/error.c
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ error.c
+
+Abstract:
+
+ This module contains a routine for converting NT status codes
+ to DOS/OS|2 error codes.
+
+Author:
+
+ David Treadwell (davidtr) 04-Apr-1991
+
+Revision History:
+
+--*/
+
+#include <ntrtlp.h>
+#include "winerror.h"
+#include "error.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE, RtlNtStatusToDosError)
+#endif
+
+//
+// Ensure that the Registry ERROR_SUCCESS error code and the
+// NO_ERROR error code remain equal and zero.
+//
+
+#if ERROR_SUCCESS != 0 || NO_ERROR != 0
+#error Invalid value for ERROR_SUCCESS.
+#endif
+
+ULONG
+RtlNtStatusToDosError (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an NT status code to its DOS/OS|2 equivalent.
+ Remembers the Status code value in the TEB.
+
+Arguments:
+
+ Status - Supplies the status value to convert.
+
+Return Value:
+
+ The matching DOS/OS|2 error code.
+
+--*/
+
+{
+ PTEB Teb;
+
+ Teb = NtCurrentTeb();
+ if ( Teb ) {
+ Teb->LastStatusValue = Status;
+ }
+
+ return RtlNtStatusToDosErrorNoTeb( Status );
+}
+
+ULONG
+RtlNtStatusToDosErrorNoTeb (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an NT status code to its DOS/OS 2 equivalent
+ and returns the translated value.
+
+Arguments:
+
+ Status - Supplies the status value to convert.
+
+Return Value:
+
+ The matching DOS/OS 2 error code.
+
+--*/
+
+{
+
+ ULONG Offset;
+ ULONG Entry;
+ ULONG Index;
+
+ //
+ // Convert any HRESULTs to their original form of a NTSTATUS or a
+ // WIN32 error
+ //
+
+
+ if (Status & 0x20000000) {
+
+ //
+ // The customer bit is set so lets just pass the
+ // error code on thru
+ //
+
+ return Status;
+
+ }
+ else if ((Status & 0xffff0000) == 0x80070000) {
+
+ //
+ // The status code was a win32 error already.
+ //
+
+ return(Status & 0x0000ffff);
+ }
+ else if ((Status & 0xf0000000) == 0xd0000000) {
+
+ //
+ // The status code is a HRESULT from NTSTATUS
+ //
+
+ Status &= 0xcfffffff;
+ }
+
+ //
+ // Scan the run length table and compute the entry in the translation
+ // table that maps the specified status code to a DOS error code.
+ //
+
+ Entry = 0;
+ Index = 0;
+ do {
+ if ((ULONG)Status >= RtlpRunTable[Entry + 1].BaseCode) {
+ Index += (RtlpRunTable[Entry].RunLength * RtlpRunTable[Entry].CodeSize);
+
+ } else {
+ Offset = (ULONG)Status - RtlpRunTable[Entry].BaseCode;
+ if (Offset >= RtlpRunTable[Entry].RunLength) {
+ break;
+
+ } else {
+ Index += (Offset * (ULONG)RtlpRunTable[Entry].CodeSize);
+ if (RtlpRunTable[Entry].CodeSize == 1) {
+ return (ULONG)RtlpStatusTable[Index];
+
+ } else {
+ return (((ULONG)RtlpStatusTable[Index + 1] << 16) |
+ (ULONG)RtlpStatusTable[Index]);
+ }
+ }
+ }
+
+ Entry += 1;
+ } while (Entry < (sizeof(RtlpRunTable) / sizeof(RUN_ENTRY)));
+
+ //
+ // The translation to a DOS error code failed.
+ //
+ // The redirector maps unknown OS/2 error codes by ORing 0xC001 into
+ // the high 16 bits. Detect this and return the low 16 bits if true.
+ //
+
+ if (((ULONG)Status >> 16) == 0xC001) {
+ return ((ULONG)Status & 0xFFFF);
+ }
+
+#ifndef NTOS_KERNEL_RUNTIME
+ DbgPrint("RTL: RtlNtStatusToDosError(0x%lx): No Valid Win32 Error Mapping\n",Status);
+ DbgPrint("RTL: Edit ntos\\rtl\\generr.c to correct the problem\n");
+ DbgPrint("RTL: ERROR_MR_MID_NOT_FOUND is being returned\n");
+
+#if DBG
+ if (NtCurrentPeb()->BeingDebugged) {
+ DbgBreakPoint();
+ }
+
+#endif // DBG
+#endif // NTOS_KERNEL_RUNTIME
+
+ return ERROR_MR_MID_NOT_FOUND;
+}
diff --git a/private/ntos/rtl/eventlog.c b/private/ntos/rtl/eventlog.c
new file mode 100644
index 000000000..e83dfb1de
--- /dev/null
+++ b/private/ntos/rtl/eventlog.c
@@ -0,0 +1,914 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ stktrace.c
+
+Abstract:
+
+ This module implements routines to do dyanmic logging of system events.
+
+Author:
+
+ Steve Wood (stevewo) 09-Sep-1993
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if DBG
+
+PRTL_EVENT
+RtlpAllocEventRecord(
+ IN PRTL_EVENT_LOG EventLog,
+ IN ULONG Size
+ );
+
+PRTL_EVENT_LOG
+RtlpServerAcquireEventLog( VOID );
+
+VOID
+RtlpServerReleaseEventLog(
+ IN PRTL_EVENT_LOG EventLog
+ );
+
+VOID
+RtlCloseEventLog( VOID );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlpAllocEventRecord)
+#pragma alloc_text(PAGE,RtlpServerAcquireEventLog)
+#pragma alloc_text(PAGE,RtlpServerReleaseEventLog)
+#pragma alloc_text(PAGE,RtlCreateEventId)
+#pragma alloc_text(PAGE,RtlAreLogging)
+#pragma alloc_text(PAGE,RtlLogEvent)
+#pragma alloc_text(PAGE,RtlCloseEventLog)
+#endif
+
+PRTL_EVENT
+RtlpAllocEventRecord(
+ IN PRTL_EVENT_LOG EventLog,
+ IN ULONG Size
+ )
+{
+ PRTL_EVENT Event;
+
+ RTL_PAGED_CODE();
+
+ if (EventLog->CurrentWriteOffset < EventLog->CurrentReadOffset &&
+ (EventLog->CurrentWriteOffset + Size) > EventLog->CurrentReadOffset
+ ) {
+ return NULL;
+ }
+
+ if ((EventLog->CurrentWriteOffset + Size) >= EventLog->MaximumOffset) {
+ Event = (PRTL_EVENT)((PCHAR)EventLog + EventLog->CurrentWriteOffset);
+ if ((EventLog->MinimumOffset + Size) > EventLog->CurrentReadOffset) {
+ return NULL;
+ }
+
+ Event->Length = 0xFFFF;
+ EventLog->CurrentWriteOffset = EventLog->MinimumOffset;
+ }
+
+ Event = (PRTL_EVENT)((PCHAR)EventLog + EventLog->CurrentWriteOffset);
+ EventLog->CurrentWriteOffset += Size;
+ return Event;
+}
+
+
+
+PRTL_EVENT_LOG
+RtlpServerAcquireEventLog( VOID )
+{
+ NTSTATUS Status;
+ PRTL_EVENT_LOG EventLog;
+
+ RTL_PAGED_CODE();
+
+ if (NtCurrentPeb() == NULL) {
+ return NULL;
+ }
+
+ EventLog = NtCurrentPeb()->EventLog;
+ if (EventLog == NULL) {
+ return NULL;
+ }
+
+ Status = NtWaitForSingleObject( EventLog->ServerMutant, TRUE, NULL );
+ if (NT_SUCCESS( Status )) {
+ return NtCurrentPeb()->EventLog;
+ }
+ else {
+ return NULL;
+ }
+}
+
+
+VOID
+RtlpServerReleaseEventLog(
+ IN PRTL_EVENT_LOG EventLog
+ )
+{
+ NTSTATUS Status;
+
+ RTL_PAGED_CODE();
+
+ Status = NtReleaseMutant( EventLog->ServerMutant, NULL );
+ return;
+}
+
+USHORT RtlpNextEventId;
+
+PRTL_EVENT_ID_INFO
+RtlCreateEventId(
+ IN OUT PVOID *Buffer OPTIONAL,
+ IN PULONG Size OPTIONAL,
+ IN PCHAR Name,
+ IN ULONG NumberOfParameters OPTIONAL,
+ ...
+ )
+{
+ va_list arglist;
+ NTSTATUS Status;
+ ULONG i, j, k, CurrentOffset;
+ ULONG BufferSize;
+ ULONG ParameterType[ RTL_EVENT_MAXIMUM_PARAMETERS ];
+ PCHAR ParameterName[ RTL_EVENT_MAXIMUM_PARAMETERS ];
+ ULONG ParameterNumberOfValues[ RTL_EVENT_MAXIMUM_PARAMETERS ];
+ ULONG ParameterValues[ RTL_EVENT_MAXIMUM_VALUE_PAIRS ];
+ PCHAR ParameterValueNames[ RTL_EVENT_MAXIMUM_VALUE_PAIRS ];
+ ULONG TotalNumberOfParameterValues;
+ PRTL_EVENT_ID_INFO EventId, OldEventId;
+ PRTL_EVENT_PARAMETER_INFO ParameterInfo;
+ PRTL_EVENT_PARAMETER_VALUE_INFO ValueInfo;
+
+ RTL_PAGED_CODE();
+
+ if (NumberOfParameters > RTL_EVENT_MAXIMUM_PARAMETERS) {
+ return NULL;
+ }
+
+ EventId = NULL;
+ //
+ // Capture variable length argument list into stack array.
+ //
+
+ BufferSize = sizeof( RTL_EVENT_ID_INFO );
+ BufferSize += strlen( Name );
+ BufferSize = ALIGN_UP( BufferSize, ULONG );
+
+ TotalNumberOfParameterValues = 0;
+
+ va_start( arglist, NumberOfParameters );
+ for (i=0; i<NumberOfParameters; i++) {
+ ParameterType[ i ] = va_arg( arglist, ULONG );
+ ParameterName[ i ] = va_arg( arglist, PCHAR );
+ ParameterNumberOfValues[ i ] = va_arg( arglist, ULONG );
+ BufferSize += sizeof( RTL_EVENT_PARAMETER_INFO );
+ BufferSize += strlen( ParameterName[ i ] );
+ BufferSize = ALIGN_UP( BufferSize, ULONG );
+
+ for (j=0; j<ParameterNumberOfValues[ i ]; j++) {
+ TotalNumberOfParameterValues += 1;
+ if (TotalNumberOfParameterValues > RTL_EVENT_MAXIMUM_VALUE_PAIRS) {
+ return NULL;
+ }
+
+ ParameterValues[ TotalNumberOfParameterValues - 1 ] = va_arg( arglist, ULONG );
+ ParameterValueNames[ TotalNumberOfParameterValues - 1 ] = va_arg( arglist, PCHAR );
+ BufferSize += sizeof( RTL_EVENT_PARAMETER_VALUE_INFO );
+ BufferSize += strlen( ParameterValueNames[ TotalNumberOfParameterValues - 1 ] );
+ BufferSize = ALIGN_UP( BufferSize, ULONG );
+ }
+ }
+ va_end( arglist );
+
+ //
+ // Allocate space for the RTL_EVENT_ID_INFO structure.
+ //
+
+ if (ARGUMENT_PRESENT( Buffer )) {
+ if (BufferSize > *Size) {
+ DbgPrint( "RTL: CreateEventId - static buffer size %x < %x\n", *Size, BufferSize );
+ return NULL;
+ }
+ else {
+ EventId = (PRTL_EVENT_ID_INFO)*Buffer;
+ *Size -= BufferSize;
+ *Buffer = (PCHAR)*Buffer + BufferSize;
+ }
+ }
+ else {
+#ifdef NTOS_KERNEL_RUNTIME
+ EventId = (PRTL_EVENT_ID_INFO)ExAllocatePoolWithTag( PagedPool, BufferSize, 'divE' );
+#else
+ EventId = (PRTL_EVENT_ID_INFO)RtlAllocateHeap( RtlProcessHeap(), 0, BufferSize );
+#endif // NTOS_KERNEL_RUNTIME
+ if (EventId == NULL) {
+ return NULL;
+ }
+ }
+
+ EventId->Length = (USHORT)BufferSize;
+ EventId->EventId = (USHORT)0;
+ EventId->NumberOfParameters = (USHORT)NumberOfParameters;
+ strcpy( EventId->Name, Name );
+
+ CurrentOffset = sizeof( *EventId );
+ CurrentOffset += strlen( Name );
+ CurrentOffset = ALIGN_UP( CurrentOffset, ULONG );
+ k = 0;
+ EventId->OffsetToParameterInfo = (USHORT)CurrentOffset;
+ for (i=0; i<NumberOfParameters; i++) {
+ ParameterInfo = (PRTL_EVENT_PARAMETER_INFO)((PCHAR)EventId + CurrentOffset);
+ ParameterInfo->Length = sizeof( *ParameterInfo );
+ ParameterInfo->Length += strlen( ParameterName[ i ] );
+ ParameterInfo->Length = (USHORT)(ALIGN_UP( ParameterInfo->Length, ULONG ));
+ ParameterInfo->Type = (USHORT)ParameterType[ i ];
+ strcpy( ParameterInfo->Label, ParameterName[ i ] );
+ ParameterInfo->NumberOfValueNames = (USHORT)ParameterNumberOfValues[ i ];
+ ParameterInfo->OffsetToValueNames = ParameterInfo->Length;
+ for (j=0; j<ParameterInfo->NumberOfValueNames; j++) {
+ ValueInfo = (PRTL_EVENT_PARAMETER_VALUE_INFO)((PCHAR)ParameterInfo + ParameterInfo->Length);
+ ValueInfo->Value = ParameterValues[ k + j ];
+ strcpy( ValueInfo->ValueName, ParameterValueNames[ k + j ] );
+ ValueInfo->Length = sizeof( *ValueInfo ) + strlen( ValueInfo->ValueName );
+ ValueInfo->Length = ALIGN_UP( ValueInfo->Length, ULONG );
+ ParameterInfo->Length = (USHORT)(ParameterInfo->Length + ValueInfo->Length);
+ }
+
+ CurrentOffset += ParameterInfo->Length;
+ k += ParameterInfo->NumberOfValueNames;
+ }
+
+#ifdef NTOS_KERNEL_RUNTIME
+ OldEventId = ExDefineEventId( EventId );
+ if (OldEventId != EventId) {
+ ExFreePool( EventId );
+ EventId = OldEventId;
+ }
+#else
+ Status = NtQuerySystemInformation( SystemNextEventIdInformation,
+ (PVOID)EventId,
+ EventId->Length,
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (!ARGUMENT_PRESENT( Buffer )) {
+ RtlFreeHeap( RtlProcessHeap(), 0, EventId );
+ }
+
+ EventId = NULL;
+ }
+#endif // NTOS_KERNEL_RUNTIME
+ return EventId;
+}
+
+
+BOOLEAN
+RtlAreLogging(
+ IN ULONG EventClass
+ )
+{
+#ifdef NTOS_KERNEL_RUNTIME
+ PPEB Peb;
+ PRTL_EVENT_LOG EventLog;
+
+ RTL_PAGED_CODE();
+
+ if (!PsGetCurrentProcess()->ExitProcessCalled &&
+ (Peb = PsGetCurrentProcess()->Peb) != NULL &&
+ (EventLog = Peb->EventLog) != NULL &&
+ (EventLog->EventClassMask & EventClass) != 0
+ ) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+#else
+ if ((NtCurrentTeb() != NULL) && \
+ (NtCurrentPeb()->EventLog != NULL) && \
+ (((PRTL_EVENT_LOG)NtCurrentPeb()->EventLog)->EventClassMask & EventClass) \
+ ) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+#endif // NTOS_KERNEL_RUNTIME
+}
+
+NTSTATUS
+RtlLogEvent(
+ IN PRTL_EVENT_ID_INFO EventId,
+ IN ULONG EventClassMask,
+ ...
+ )
+{
+ va_list arglist;
+ NTSTATUS Status;
+ PRTL_EVENT_LOG EventLog;
+ ULONG i, BufferSize;
+ PRTL_EVENT_PARAMETER_INFO ParameterInfo;
+ PRTL_EVENT Event;
+ ULONG Parameters[ RTL_EVENT_MAXIMUM_PARAMETERS ];
+ USHORT StackBackTraceLength;
+ ULONG Hash;
+ PVOID StackBackTrace[ MAX_STACK_DEPTH ];
+ PULONG ParameterData;
+ PWSTR Src, Dst;
+ LPSTR AnsiSrc, AnsiDst;
+
+ RTL_PAGED_CODE();
+
+ EventLog = RtlpServerAcquireEventLog();
+ if (EventId == NULL || EventLog == NULL) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (EventClassMask != 0 && !(EventClassMask & EventLog->EventClassMask)) {
+ RtlpServerReleaseEventLog( EventLog );
+ return STATUS_SUCCESS;
+ }
+
+#if i386
+
+ try {
+ Hash = 0;
+ StackBackTraceLength = RtlCaptureStackBackTrace( 1,
+ MAX_STACK_DEPTH,
+ StackBackTrace,
+ &Hash
+ );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ StackBackTraceLength = 0;
+ }
+#else
+ StackBackTraceLength = 0;
+#endif
+
+ va_start( arglist, EventClassMask );
+
+ while (StackBackTraceLength != 0) {
+ if (StackBackTrace[ StackBackTraceLength - 1 ] != 0) {
+ break;
+ }
+ else {
+ StackBackTraceLength -= 1;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+ try {
+ if (NtCurrentTeb()->RealClientId.UniqueProcess == EventLog->DisplayClientId.UniqueProcess) {
+ leave;
+ }
+
+ ParameterInfo = (PRTL_EVENT_PARAMETER_INFO)
+ ((PCHAR)EventId + EventId->OffsetToParameterInfo);
+
+ BufferSize = sizeof( RTL_EVENT ) +
+ (StackBackTraceLength * sizeof( ULONG )) +
+ (EventId->NumberOfParameters * sizeof( ULONG ));
+
+ for (i=0; i<EventId->NumberOfParameters; i++) {
+ Parameters[ i ] = va_arg( arglist, ULONG );
+ switch( ParameterInfo->Type ) {
+ //
+ // No additional data for these parameter types;
+ //
+
+ case RTL_EVENT_STATUS_PARAM:
+ case RTL_EVENT_ULONG_PARAM:
+ case RTL_EVENT_ENUM_PARAM:
+ case RTL_EVENT_FLAGS_PARAM:
+ case RTL_EVENT_ADDRESS_PARAM:
+ break;
+
+ case RTL_EVENT_PWSTR_PARAM:
+ try {
+ BufferSize += wcslen( (PWSTR)Parameters[ i ] ) * sizeof( WCHAR );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+ BufferSize += sizeof( UNICODE_NULL );
+ BufferSize = ALIGN_UP( BufferSize, ULONG );
+ break;
+
+ case RTL_EVENT_PUNICODE_STRING_PARAM:
+ try {
+ BufferSize += ((PUNICODE_STRING)Parameters[ i ])->Length;
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+ BufferSize += sizeof( UNICODE_NULL );
+ BufferSize = ALIGN_UP( BufferSize, ULONG );
+ break;
+
+ case RTL_EVENT_PANSI_STRING_PARAM:
+ try {
+ BufferSize += ((PANSI_STRING)Parameters[ i ])->Length;
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+ BufferSize += sizeof( '\0' );
+ BufferSize = ALIGN_UP( BufferSize, ULONG );
+ break;
+
+ case RTL_EVENT_STRUCTURE_PARAM:
+ default:
+ break;
+ }
+
+ ParameterInfo = (PRTL_EVENT_PARAMETER_INFO)
+ ((PCHAR)ParameterInfo + ParameterInfo->Length);
+ }
+
+ Event = RtlpAllocEventRecord( EventLog, BufferSize );
+ if (Event == NULL) {
+ leave;
+ }
+
+ Event->Length = (USHORT)BufferSize;
+ Event->EventId = EventId->EventId;
+ Event->ClientId = NtCurrentTeb()->ClientId;
+
+ ParameterData = (PULONG)(Event + 1);
+ if (Event->StackBackTraceLength = (USHORT)StackBackTraceLength) {
+ RtlMoveMemory( ParameterData, StackBackTrace, StackBackTraceLength * sizeof( ULONG ));
+ ParameterData += StackBackTraceLength;
+ }
+ Event->OffsetToParameterData = (PCHAR)ParameterData - (PCHAR)Event;
+
+ ParameterInfo = (PRTL_EVENT_PARAMETER_INFO)
+ ((PCHAR)EventId + EventId->OffsetToParameterInfo);
+
+ for (i=0; i<EventId->NumberOfParameters; i++) {
+ switch( ParameterInfo->Type ) {
+ //
+ // No additional data for these parameter types;
+ //
+
+ case RTL_EVENT_STATUS_PARAM:
+ case RTL_EVENT_ULONG_PARAM:
+ case RTL_EVENT_ENUM_PARAM:
+ case RTL_EVENT_FLAGS_PARAM:
+ case RTL_EVENT_ADDRESS_PARAM:
+ *ParameterData++ = Parameters[ i ];
+ break;
+
+ case RTL_EVENT_PWSTR_PARAM:
+ Src = (PWSTR)Parameters[ i ];
+ Dst = (PWSTR)ParameterData;
+ try {
+ while (*Dst = *Src++) {
+ Dst += 1;
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+ *Dst = UNICODE_NULL;
+
+ ParameterData = (PULONG)ALIGN_UP( Dst, ULONG );
+ break;
+
+ case RTL_EVENT_PUNICODE_STRING_PARAM:
+ try {
+ Src = ((PUNICODE_STRING)Parameters[ i ])->Buffer;
+ Dst = (PWSTR)ParameterData;
+ RtlMoveMemory( Dst, Src, ((PUNICODE_STRING)Parameters[ i ])->Length );
+ Dst += ((PUNICODE_STRING)Parameters[ i ])->Length / sizeof( WCHAR );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+ *Dst++ = UNICODE_NULL;
+
+ ParameterData = (PULONG)ALIGN_UP( Dst, ULONG );
+ break;
+
+ case RTL_EVENT_PANSI_STRING_PARAM:
+ try {
+ AnsiSrc = ((PANSI_STRING)Parameters[ i ])->Buffer;
+ AnsiDst = (LPSTR)ParameterData;
+ RtlMoveMemory( AnsiDst, AnsiSrc, ((PANSI_STRING)Parameters[ i ])->Length );
+ AnsiDst += ((PANSI_STRING)Parameters[ i ])->Length;
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+ *AnsiDst++ = '\0';
+
+ ParameterData = (PULONG)ALIGN_UP( AnsiDst, ULONG );
+ break;
+
+ case RTL_EVENT_STRUCTURE_PARAM:
+ default:
+ break;
+ }
+
+ ParameterInfo = (PRTL_EVENT_PARAMETER_INFO)
+ ((PCHAR)ParameterInfo + ParameterInfo->Length);
+ }
+
+ if (EventLog->ClientSemaphore != NULL) {
+ NtReleaseSemaphore( EventLog->ServerSemaphore, 1, NULL );
+ }
+ else {
+ NtClose( NtCurrentPeb()->EventLogSection );
+ NtCurrentPeb()->EventLogSection = NULL;
+ NtCurrentPeb()->EventLog = NULL;
+ NtClose( EventLog->ServerMutant );
+ NtClose( EventLog->ServerSemaphore );
+ NtUnmapViewOfSection( NtCurrentProcess(), EventLog );
+ EventLog = NULL;
+ }
+ }
+ finally {
+ if (EventLog != NULL) {
+ RtlpServerReleaseEventLog( EventLog );
+ }
+ }
+ va_end( arglist );
+
+ return Status;
+}
+
+
+VOID
+RtlCloseEventLog( VOID )
+{
+ PRTL_EVENT_LOG EventLog;
+
+ EventLog = RtlpServerAcquireEventLog();
+ if (EventLog == NULL) {
+ return;
+ }
+
+ try {
+ NtCurrentPeb()->EventLogSection = NULL;
+ NtCurrentPeb()->EventLog = NULL;
+ NtClose( EventLog->ServerMutant );
+ NtClose( EventLog->ServerSemaphore );
+ NtUnmapViewOfSection( NtCurrentProcess(), EventLog );
+ EventLog = NULL;
+ }
+ finally {
+ if (EventLog != NULL) {
+ RtlpServerReleaseEventLog( EventLog );
+ }
+ }
+
+ return;
+}
+
+#ifndef NTOS_KERNEL_RUNTIME
+
+NTSTATUS
+RtlCreateEventLog(
+ IN HANDLE TargetProcess,
+ IN ULONG Flags,
+ IN ULONG EventClassMask,
+ OUT PRTL_EVENT_LOG* ReturnedEventLog
+ )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LARGE_INTEGER MaximumSize;
+ ULONG CommitSize;
+ HANDLE Section, ServerSection;
+ PRTL_EVENT_LOG EventLog, ServerEventLog;
+ BOOLEAN Inherited;
+ PROCESS_BASIC_INFORMATION ProcessInformation;
+
+ if (Flags & RTL_EVENT_LOG_INHERIT) {
+ Inherited = TRUE;
+ }
+ else {
+ Inherited = FALSE;
+ }
+ InitializeObjectAttributes( &ObjectAttributes,
+ NULL,
+ Inherited ? OBJ_INHERIT : 0,
+ NULL,
+ NULL
+ );
+
+ MaximumSize.LowPart = 1024 * 1024;
+ MaximumSize.HighPart = 0;
+ Status = NtCreateSection( &Section,
+ SECTION_ALL_ACCESS,
+ &ObjectAttributes,
+ &MaximumSize,
+ PAGE_READWRITE,
+ SEC_RESERVE,
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ EventLog = NULL;
+ Status = NtMapViewOfSection( Section,
+ NtCurrentProcess(),
+ &EventLog,
+ 0,
+ 0,
+ NULL,
+ &MaximumSize.LowPart,
+ ViewUnmap,
+ 0,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ NtClose( Section );
+ return Status;
+ }
+
+ ServerEventLog = NULL;
+ Status = NtMapViewOfSection( Section,
+ TargetProcess,
+ &ServerEventLog,
+ 0,
+ 0,
+ NULL,
+ &MaximumSize.LowPart,
+ ViewUnmap,
+ 0,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ NtUnmapViewOfSection( NtCurrentProcess(), EventLog );
+ NtClose( Section );
+ return Status;
+ }
+
+
+ CommitSize = 1024 * 16;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &EventLog,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ NtUnmapViewOfSection( NtCurrentProcess(), ServerEventLog );
+ NtUnmapViewOfSection( NtCurrentProcess(), EventLog );
+ NtClose( Section );
+ return Status;
+ }
+
+ EventLog->EventClassMask = EventClassMask;
+ EventLog->DisplayClientId = NtCurrentTeb()->ClientId;
+ EventLog->ClientMutant = NULL;
+ EventLog->ClientSemaphore = NULL;
+ EventLog->ServerMutant = NULL;
+ EventLog->ServerSemaphore = NULL;
+ EventLog->MinimumOffset = sizeof( *EventLog );
+ EventLog->MaximumOffset = CommitSize;
+ EventLog->CurrentReadOffset = EventLog->MinimumOffset;
+ EventLog->CurrentWriteOffset = EventLog->MinimumOffset;
+ EventLog->CommitLimitOffset = MaximumSize.LowPart;
+
+ Status = NtCreateSemaphore( &EventLog->ClientSemaphore,
+ SEMAPHORE_ALL_ACCESS,
+ &ObjectAttributes,
+ 0,
+ 0x7FFFFFFF
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtCreateMutant( &EventLog->ClientMutant,
+ MUTANT_ALL_ACCESS,
+ &ObjectAttributes,
+ FALSE
+ );
+
+ Status = NtDuplicateObject( NtCurrentProcess(),
+ EventLog->ClientSemaphore,
+ TargetProcess,
+ &EventLog->ServerSemaphore,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtDuplicateObject( NtCurrentProcess(),
+ EventLog->ClientMutant,
+ TargetProcess,
+ &EventLog->ServerMutant,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtDuplicateObject( NtCurrentProcess(),
+ Section,
+ TargetProcess,
+ &ServerSection,
+ 0,
+ 0,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtQueryInformationProcess( TargetProcess,
+ ProcessBasicInformation,
+ &ProcessInformation,
+ sizeof( ProcessInformation ),
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtWriteVirtualMemory( TargetProcess,
+ &ProcessInformation.PebBaseAddress->EventLogSection,
+ &ServerSection,
+ sizeof( ServerSection ),
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtWriteVirtualMemory( TargetProcess,
+ &ProcessInformation.PebBaseAddress->EventLog,
+ &ServerEventLog,
+ sizeof( ServerEventLog ),
+ NULL
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ NtClose( Section );
+ if (!NT_SUCCESS( Status )) {
+ NtClose( EventLog->ClientSemaphore );
+ NtClose( EventLog->ClientMutant );
+ NtClose( EventLog->ServerSemaphore );
+ NtClose( EventLog->ServerMutant );
+ NtUnmapViewOfSection( NtCurrentProcess(), EventLog );
+ return Status;
+ }
+
+ EventLog->CountOfClients = 1;
+ *ReturnedEventLog = EventLog;
+ return Status;
+}
+
+
+PRTL_EVENT_ID_INFO
+RtlpFindEventIdForEvent(
+ PRTL_EVENT Event
+ );
+
+NTSTATUS
+RtlWaitForEvent(
+ IN PRTL_EVENT_LOG EventLog,
+ IN ULONG EventBufferSize,
+ OUT PRTL_EVENT EventBuffer,
+ OUT PRTL_EVENT_ID_INFO *ReturnedEventId
+ )
+{
+ NTSTATUS Status;
+ HANDLE WaitObjects[ 2 ];
+ PRTL_EVENT Event;
+
+ WaitObjects[ 0 ] = EventLog->ClientSemaphore;
+ WaitObjects[ 1 ] = EventLog->ClientMutant;
+ Status = NtWaitForMultipleObjects( 2,
+ WaitObjects,
+ WaitAll,
+ TRUE,
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ try {
+ Event = (PRTL_EVENT)((PCHAR)EventLog + EventLog->CurrentReadOffset);
+ if (Event->Length == 0xFFFF) {
+ Event = (PRTL_EVENT)((PCHAR)EventLog + EventLog->MinimumOffset);
+ EventLog->CurrentReadOffset = EventLog->MinimumOffset;
+ }
+
+ if (Event->Length <= EventBufferSize) {
+ RtlMoveMemory( EventBuffer, Event, Event->Length );
+ EventLog->CurrentReadOffset += Event->Length;
+
+ *ReturnedEventId = RtlpFindEventIdForEvent( Event );
+ }
+ else {
+ RtlMoveMemory( EventBuffer, Event, EventBufferSize );
+ EventLog->CurrentReadOffset += Event->Length;
+
+ *ReturnedEventId = RtlpFindEventIdForEvent( Event );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ finally {
+ NtReleaseMutant( EventLog->ClientMutant, NULL );
+ }
+
+ return Status;
+}
+
+
+PRTL_EVENT_ID_INFO RtlpEventIds;
+
+PRTL_EVENT_ID_INFO
+RtlpFindEventIdForEvent(
+ PRTL_EVENT Event
+ )
+{
+ PRTL_EVENT_ID_INFO EventId;
+ BOOLEAN EventIdsFetched;
+ NTSTATUS Status;
+ ULONG Size;
+
+ EventIdsFetched = FALSE;
+ while (TRUE) {
+ EventId = RtlpEventIds;
+ if (EventId != NULL) {
+ while (EventId->Length != 0) {
+ if (Event->EventId == EventId->EventId) {
+ return EventId;
+ }
+
+ EventId = (PRTL_EVENT_ID_INFO)((PCHAR)EventId + EventId->Length);
+ }
+ }
+
+ if (EventIdsFetched) {
+ return NULL;
+ }
+
+ if (RtlpEventIds != NULL) {
+ Size = 0;
+ NtFreeVirtualMemory( NtCurrentProcess(),
+ &RtlpEventIds,
+ &Size,
+ MEM_RELEASE
+ );
+ }
+retryEventIds:
+ Status = NtQuerySystemInformation( SystemEventIdsInformation,
+ NULL,
+ 0,
+ &Size
+ );
+ if (Status != STATUS_INFO_LENGTH_MISMATCH) {
+ return NULL;
+ }
+
+ RtlpEventIds = NULL;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &RtlpEventIds,
+ 0,
+ &Size,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ Status = NtQuerySystemInformation( SystemEventIdsInformation,
+ RtlpEventIds,
+ Size,
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (Status != STATUS_INFO_LENGTH_MISMATCH) {
+ return NULL;
+ }
+ else {
+ goto retryEventIds;
+ }
+ }
+
+ EventIdsFetched = TRUE;
+ }
+}
+
+
+
+NTSTATUS
+RtlDestroyEventLog(
+ IN PRTL_EVENT_LOG EventLog
+ )
+{
+ NtClose( EventLog->ClientMutant );
+ NtClose( EventLog->ClientSemaphore );
+ EventLog->ClientMutant = NULL;
+ EventLog->ClientSemaphore = NULL;
+ EventLog->CurrentReadOffset = 0;
+ return NtUnmapViewOfSection( NtCurrentProcess(), EventLog );
+}
+
+#endif // ndef NTOS_KERNEL_RUNTIME
+
+#endif // DBG
diff --git a/private/ntos/rtl/excptdbg.c b/private/ntos/rtl/excptdbg.c
new file mode 100644
index 000000000..d4e303f63
--- /dev/null
+++ b/private/ntos/rtl/excptdbg.c
@@ -0,0 +1,171 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ excpdbg.c
+
+Abstract:
+
+ This module implements an exception dispatcher logging facility
+
+Author:
+
+ Kent Forschmiedt (kentf) 05-Oct-1995
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+PLAST_EXCEPTION_LOG RtlpExceptionLog;
+ULONG RtlpExceptionLogCount;
+ULONG RtlpExceptionLogSize;
+
+
+VOID
+RtlInitializeExceptionLog(
+ IN ULONG Entries
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates space for the exception dispatcher logging
+ facility, and records the address and size of the log area in globals
+ where they can be found by the debugger.
+
+ If memory is not available, the table pointer will remain NULL
+ and the logging functions will do nothing.
+
+Arguments:
+
+ Entries - Supplies the number of entries to allocate for
+
+Return Value:
+
+ None
+
+--*/
+{
+#if defined(NTOS_KERNEL_RUNTIME)
+ RtlpExceptionLog = (PLAST_EXCEPTION_LOG)ExAllocatePoolWithTag( NonPagedPool, sizeof(LAST_EXCEPTION_LOG) * Entries, 'gbdE' );
+#else
+ //RtlpExceptionLog = (PLAST_EXCEPTION_LOG)RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(LAST_EXCEPTION_LOG) * Entries );
+#endif
+ if (RtlpExceptionLog) {
+ RtlpExceptionLogSize = Entries;
+ }
+}
+
+
+ULONG
+RtlpLogExceptionHandler(
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord,
+ IN ULONG ControlPc,
+ IN PVOID HandlerData,
+ IN ULONG Size
+ )
+/*++
+
+Routine Description:
+
+ Records the dispatching of exceptions to frame-based handlers.
+ The debugger may inspect the table later and interpret the data
+ to discover the address of the filters and handlers.
+
+Arguments:
+
+ ExceptionRecord - Supplies an exception record
+
+ ContextRecord - Supplies the context at the exception
+
+ ControlPc - Supplies the PC where control left the frame being
+ dispatched to.
+
+ HandlerData - Supplies a pointer to the host-dependent exception
+ data. On the RISC machines this is a RUNTIME_FUNCTION record;
+ on x86 it is the registration record from the stack frame.
+
+ Size - Supplies the size of HandlerData
+
+Returns:
+
+ The index to the log entry used, so that if the handler returns
+ a disposition it may be recorded.
+
+--*/
+{
+#if !defined(NTOS_KERNEL_RUNTIME)
+
+ return 0;
+
+#else
+
+ ULONG LogIndex;
+
+ if (!RtlpExceptionLog) {
+ return 0;
+ }
+
+ ASSERT(Size <= MAX_EXCEPTION_LOG_DATA_SIZE * sizeof(ULONG));
+
+ do {
+ LogIndex = RtlpExceptionLogCount;
+ } while ((PVOID)LogIndex != InterlockedCompareExchange(
+ (PVOID*)&RtlpExceptionLogCount,
+ (PVOID)((LogIndex + 1) % MAX_EXCEPTION_LOG),
+ (PVOID)LogIndex));
+
+ //
+ // the debugger will have to interpret the exception handler
+ // data, because it cannot be done safely here.
+ //
+
+ RtlCopyMemory(RtlpExceptionLog[LogIndex].HandlerData,
+ HandlerData,
+ Size);
+ RtlpExceptionLog[LogIndex].ExceptionRecord = *ExceptionRecord;
+ RtlpExceptionLog[LogIndex].ContextRecord = *ContextRecord;
+ RtlpExceptionLog[LogIndex].Disposition = -1;
+
+ return LogIndex;
+#endif // !NTOS_KERNEL_RUNTIME
+}
+
+
+VOID
+RtlpLogLastExceptionDisposition(
+ ULONG LogIndex,
+ EXCEPTION_DISPOSITION Disposition
+ )
+/*++
+
+Routine Description:
+
+ Records the disposition from an exception handler.
+
+Arguments:
+
+ LogIndex - Supplies the entry number of the exception log record.
+
+ Disposition - Supplies the disposition code
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ // If MAX_EXCEPTION_LOG or more exceptions were dispatched while
+ // this one was being handled, this disposition will get written
+ // on the wrong record. Oh well.
+ if (RtlpExceptionLog) {
+ RtlpExceptionLog[LogIndex].Disposition = Disposition;
+ }
+}
+
diff --git a/private/ntos/rtl/gen8dot3.c b/private/ntos/rtl/gen8dot3.c
new file mode 100644
index 000000000..b2dc99764
--- /dev/null
+++ b/private/ntos/rtl/gen8dot3.c
@@ -0,0 +1,911 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ Gen8dot3.c
+
+Abstract:
+
+ This module implements a routine to generate 8.3 names from long names.
+
+Author:
+
+ Gary Kimura [GaryKi] 26-Mar-1992
+
+Environment:
+
+ Pure Utility Routines
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include <stdio.h>
+
+extern PUSHORT NlsUnicodeToMbOemData;
+extern PUSHORT NlsOemToUnicodeData;
+extern PCH NlsUnicodeToOemData;
+extern PUSHORT NlsMbOemCodePageTables;
+extern BOOLEAN NlsMbOemCodePageTag;
+extern PUSHORT NlsOemLeadByteInfo;
+extern USHORT OemDefaultChar;
+
+//
+// A condensed table of legal fat character values
+//
+
+ULONG RtlFatIllegalTable[] = { 0xffffffff,
+ 0xfc009c04,
+ 0x38000000,
+ 0x10000000 };
+
+WCHAR
+GetNextWchar (
+ IN PUNICODE_STRING Name,
+ IN PULONG CurrentIndex,
+ IN BOOLEAN SkipDots,
+ IN BOOLEAN AllowExtendedCharacters
+ );
+
+BOOLEAN
+IsValidOemCharacter (
+ IN WCHAR *Wc
+ );
+
+USHORT
+RtlComputeLfnChecksum (
+ PUNICODE_STRING Name
+ );
+
+//
+// BOOLEAN
+// IsDbcsCharacter (
+// IN WCHAR Wc
+// );
+//
+
+#define IsDbcsCharacter(WC) ( \
+ ((WC) > 127) && \
+ (HIBYTE(NlsUnicodeToMbOemData[(WC)])) \
+)
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlGenerate8dot3Name)
+#pragma alloc_text(PAGE,GetNextWchar)
+#pragma alloc_text(PAGE,RtlComputeLfnChecksum)
+#pragma alloc_text(PAGE,RtlIsNameLegalDOS8Dot3)
+#endif
+
+
+VOID
+RtlGenerate8dot3Name (
+ IN PUNICODE_STRING Name,
+ IN BOOLEAN AllowExtendedCharacters,
+ IN OUT PGENERATE_NAME_CONTEXT Context,
+ OUT PUNICODE_STRING Name8dot3
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to generate an 8.3 name from a long name. It can
+ be called repeatedly to generate different 8.3 name variations for the
+ same long name. This is necessary if the gernerated 8.3 name conflicts
+ with an existing 8.3 name.
+
+Arguments:
+
+ Name - Supplies the original long name that is being translated from.
+
+ AllowExtendedCharacters - If TRUE, then extended characters, including
+ DBCS characters, are allowed in the basis of the short name if they
+ map to an upcased Oem character.
+
+ Context - Supplies a context for the translation. This is a private structure
+ needed by this routine to help enumerate the different long name
+ possibilities. The caller is responsible with providing a "zeroed out"
+ context structure on the first call for each given input name.
+
+ Name8dot3 - Receives the new 8.3 name. Pool for the buffer must be allocated
+ by the caller and should be 12 characters wide (i.e., 24 bytes).
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOLEAN DbcsAware;
+ BOOLEAN IndexAll9s = TRUE;
+ ULONG OemLength;
+ ULONG IndexLength;
+ WCHAR IndexBuffer[8];
+ ULONG i;
+
+#ifdef NTOS_KERNEL_RUNTIME
+ extern BOOLEAN FsRtlSafeExtensions;
+#else
+ BOOLEAN FsRtlSafeExtensions = TRUE;
+#endif
+
+ DbcsAware = AllowExtendedCharacters && NlsMbOemCodePageTag;
+
+ //
+ // Check if this is the first time we are being called, and if so then
+ // initialize the context fields.
+ //
+
+ if (Context->NameLength == 0) {
+
+ ULONG LastDotIndex;
+
+ ULONG CurrentIndex;
+ BOOLEAN SkipDots;
+ WCHAR wc;
+
+ //
+ // Skip down the name remembering the index of the last dot we
+ // will skip over the first dot provided the name starts with
+ // a dot.
+ //
+
+ LastDotIndex = MAXULONG;
+
+ CurrentIndex = 0;
+ SkipDots = ((Name->Length > 0) && (Name->Buffer[0] == L'.'));
+
+ while ((wc = GetNextWchar( Name,
+ &CurrentIndex,
+ SkipDots,
+ AllowExtendedCharacters )) != 0) {
+
+ SkipDots = FALSE;
+ if (wc == L'.') { LastDotIndex = CurrentIndex; }
+ }
+
+ //
+ // If the LastDotIndex is the last character in the name,
+ // then there really isn't an extension, so reset LastDotIndex.
+ //
+
+ if (LastDotIndex == Name->Length/sizeof(WCHAR)) {
+
+ LastDotIndex = MAXULONG;
+ }
+
+ //
+ // Build up the name part. This can be at most 6 characters
+ // (because of the ~# appeneded on the end) and we skip over
+ // dots, except the last dot, which terminates the loop.
+ //
+ // We exit the loop if:
+ //
+ // - The input Name has been exhausted
+ // - We have consumed the input name up to the last dot
+ // - We have filled 6 characters of short name basis
+ //
+
+ CurrentIndex = 0;
+ OemLength = 0;
+ Context->NameLength = 0;
+
+ while ((wc = GetNextWchar( Name, &CurrentIndex, TRUE, AllowExtendedCharacters)) &&
+ (CurrentIndex < LastDotIndex) &&
+ (Context->NameLength < 6)) {
+
+ //
+ // If we are on a multi-byte code page we have to be careful
+ // here because the short name (when converted to Oem) must
+ // be 8.3 compliant. Note that if AllowExtendedCharacters
+ // is FALSE, then GetNextWchar will never return a DBCS
+ // character, so we don't care what kind of code page we
+ // are on.
+ //
+
+ if (DbcsAware) {
+
+ OemLength += IsDbcsCharacter(wc) ? 2 : 1;
+
+ if (OemLength > 6) { break; }
+ }
+
+ //
+ // Copy the UNICODE character into the name buffer
+ //
+
+ Context->NameBuffer[Context->NameLength++] = wc;
+ }
+
+ //
+ // Now if the name part of the basis is 2 or less bytes (when
+ // represented in Oem) then append a four character checksum
+ // to make the short name space less sparse.
+ //
+
+ if ((DbcsAware ? OemLength : Context->NameLength) <= 2) {
+
+ USHORT Checksum;
+ WCHAR Nibble;
+
+ Checksum =
+ Context->Checksum = RtlComputeLfnChecksum( Name );
+
+ for (i = 0; i < 4; i++, Checksum >>= 4) {
+
+ Nibble = Checksum & 0xf;
+ Nibble += Nibble <= 9 ? '0' : 'A' - 10;
+
+ Context->NameBuffer[ Context->NameLength + i ] = Nibble;
+ }
+
+ Context->NameLength += 4;
+ Context->ChecksumInserted = TRUE;
+ }
+
+ //
+ // Now process the last extension (if there is one).
+ // If the last dot index is not MAXULONG then we
+ // have located the last dot in the name
+ //
+
+ if (LastDotIndex != MAXULONG) {
+
+ //
+ // Put in the "."
+ //
+
+ Context->ExtensionBuffer[0] = L'.';
+
+ //
+ // Process the extension similar to how we processed the name
+ //
+ // We exit the loop if:
+ //
+ // - The input Name has been exhausted
+ // - We have filled . + 3 characters of extension
+ //
+
+ OemLength = 1;
+ Context->ExtensionLength = 1;
+
+ while ((wc = GetNextWchar( Name, &LastDotIndex, TRUE, AllowExtendedCharacters)) &&
+ (Context->ExtensionLength < 4)) {
+
+ if (DbcsAware) {
+
+ OemLength += IsDbcsCharacter(wc) ? 2 : 1;
+
+ if (OemLength > 4) { break; }
+ }
+
+ Context->ExtensionBuffer[Context->ExtensionLength++] = wc;
+ }
+
+ //
+ // If we had to truncate the extension (i.e. input name was not
+ // exhausted), change the last char of the truncated extension
+ // to a ~ is user has selected safe extensions.
+ //
+
+ if (wc && FsRtlSafeExtensions) {
+
+ Context->ExtensionBuffer[Context->ExtensionLength - 1] = L'~';
+ }
+
+ } else {
+
+ Context->ExtensionLength = 0;
+ }
+ }
+
+ //
+ // In all cases we add one to the index value and this is the value
+ // of the index we are going to generate this time around
+ //
+
+ Context->LastIndexValue += 1;
+
+ //
+ // Now if the new index value is greater than 4 then we've had too
+ // many collisions and we should alter our basis if possible
+ //
+
+ if ((Context->LastIndexValue > 4) && !Context->ChecksumInserted) {
+
+ USHORT Checksum;
+ WCHAR Nibble;
+
+ //
+ // 'XX' is represented A DBCS character.
+ //
+ // LongName -> ShortName | DbcsBias Oem Unicode
+ // -----------------------------+------------------------
+ // XXXXThisisapen -> XX1234 | 1 6 5
+ // XXThisisapen -> XX1234 | 1 6 5
+ // aXXThisisapen -> a1234 | 1 5 5
+ // aaThisisapen -> aa1234 | 0 6 6
+ //
+
+ ULONG DbcsBias;
+
+ if (DbcsAware) {
+
+ DbcsBias = ((IsDbcsCharacter(Context->NameBuffer[0]) ? 1 : 0) |
+ (IsDbcsCharacter(Context->NameBuffer[1]) ? 1 : 0));
+
+ } else {
+
+ DbcsBias = 0;
+ }
+
+ Checksum =
+ Context->Checksum = RtlComputeLfnChecksum( Name );
+
+ for (i = (2-DbcsBias); i < (6-DbcsBias); i++, Checksum >>= 4) {
+
+ Nibble = Checksum & 0xf;
+ Nibble += Nibble <= 9 ? '0' : 'A' - 10;
+
+ Context->NameBuffer[ i ] = Nibble;
+ }
+
+ Context->NameLength = (UCHAR)(6-DbcsBias);
+ Context->LastIndexValue = 1;
+ Context->ChecksumInserted = TRUE;
+ }
+
+ //
+ // Now build the index buffer from high index to low index because we
+ // use a mod & div operation to build the string from the index value.
+ //
+ // We also want to remember is we are about to rollover in base 10.
+ //
+
+ for (IndexLength = 1, i = Context->LastIndexValue;
+ (IndexLength <= 7) && (i > 0);
+ IndexLength += 1, i /= 10) {
+
+ if ((IndexBuffer[ 8 - IndexLength] = (WCHAR)(L'0' + (i % 10))) != L'9') {
+
+ IndexAll9s = FALSE;
+ }
+ }
+
+ //
+ // And tack on the preceding dash
+ //
+
+ IndexBuffer[ 8 - IndexLength ] = L'~';
+
+ //
+ // At this point everything is set up to copy to the output buffer. First
+ // copy over the name and then only copy the index and extension if they exist
+ //
+
+ if (Context->NameLength != 0) {
+
+ RtlCopyMemory( &Name8dot3->Buffer[0],
+ &Context->NameBuffer[0],
+ Context->NameLength * 2 );
+
+ Name8dot3->Length = (USHORT)(Context->NameLength * 2);
+
+ } else {
+
+ Name8dot3->Length = 0;
+ }
+
+ //
+ // Now do the index.
+ //
+
+ RtlCopyMemory( &Name8dot3->Buffer[ Name8dot3->Length/2 ],
+ &IndexBuffer[ 8 - IndexLength ],
+ IndexLength * 2 );
+
+ Name8dot3->Length += (USHORT) (IndexLength * 2);
+
+ //
+ // Now conditionally do the extension
+ //
+
+ if (Context->ExtensionLength != 0) {
+
+ RtlCopyMemory( &Name8dot3->Buffer[ Name8dot3->Length/2 ],
+ &Context->ExtensionBuffer[0],
+ Context->ExtensionLength * 2 );
+
+ Name8dot3->Length += (USHORT) (Context->ExtensionLength * 2);
+ }
+
+ //
+ // If current index value is all 9s, then the next value will cause the
+ // index string to grow from it's current size. In this case recompute
+ // Context->NameLength so that is will be correct for next time.
+ //
+
+ if (IndexAll9s) {
+
+ if (DbcsAware) {
+
+ for (i = 0, OemLength = 0; i < Context->NameLength; i++) {
+
+ OemLength += IsDbcsCharacter(Context->NameBuffer[i]) ? 2 : 1;
+
+ if (OemLength >= 8 - (IndexLength + 1)) {
+ break;
+ }
+ }
+
+ Context->NameLength = (UCHAR)i;
+
+ } else {
+
+ Context->NameLength -= 1;
+ }
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+
+//
+// Local support routine
+//
+
+WCHAR
+GetNextWchar (
+ IN PUNICODE_STRING Name,
+ IN PULONG CurrentIndex,
+ IN BOOLEAN SkipDots,
+ IN BOOLEAN AllowExtendedCharacters
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scans the input name starting at the current index and
+ returns the next valid character for the long name to 8.3 generation
+ algorithm. It also updates the current index to point to the
+ next character to examine.
+
+ The user can specify if dots are skipped over or passed back. The
+ filtering done by the procedure is:
+
+ 1. Skip characters less then blanks, and larger than 127 if
+ AllowExtendedCharacters is FALSE
+ 2. Optionally skip over dots
+ 3. translate the special 7 characters : + , ; = [ ] into underscores
+
+Arguments:
+
+ Name - Supplies the name being examined
+
+ CurrentIndex - Supplies the index to start our examination and also
+ receives the index of one beyond the character we return.
+
+ SkipDots - Indicates whether this routine will also skip over periods
+
+ AllowExtendedCharacters - Tell whether charaacters >= 127 are valid.
+
+Return Value:
+
+ WCHAR - returns the next wchar in the name string
+
+--*/
+
+{
+ WCHAR wc;
+
+ //
+ // Until we find out otherwise the character we are going to return
+ // is 0
+ //
+
+ wc = 0;
+
+ //
+ // Now loop through updating the current index until we either have a character to
+ // return or until we exhaust the name buffer
+ //
+
+ while (*CurrentIndex < (ULONG)(Name->Length/2)) {
+
+ //
+ // Get the next character in the buffer
+ //
+
+ wc = Name->Buffer[*CurrentIndex];
+ *CurrentIndex += 1;
+
+ //
+ // If the character is to be skipped over then reset wc to 0
+ //
+
+ if ((wc <= L' ') ||
+ ((wc >= 127) && (!AllowExtendedCharacters || !IsValidOemCharacter(&wc))) ||
+ ((wc == L'.') && SkipDots)) {
+
+ wc = 0;
+
+ } else {
+
+ //
+ // We have a character to return, but first translate the character is necessary
+ //
+
+ if ((wc < 0x80) && (RtlFatIllegalTable[wc/32] & (1 << (wc%32)))) {
+
+ wc = L'_';
+ }
+
+ //
+ // Do an a-z upcase.
+ //
+
+ if ((wc >= L'a') && (wc <= L'z')) {
+
+ wc -= L'a' - L'A';
+ }
+
+ //
+ // And break out of the loop to return to our caller
+ //
+
+ break;
+ }
+ }
+
+ //DebugTrace( 0, Dbg, "GetNextWchar -> %08x\n", wc);
+
+ return wc;
+}
+
+BOOLEAN
+IsValidOemCharacter (
+ IN WCHAR *Char
+)
+
+/*++
+
+Routine Description:
+
+ This routine determines if the best-fitted and upcased version of the
+ input unicode char is a valid Oem character.
+
+Arguments:
+
+ Char - Supplies the Unicode char and receives the best-fitted and
+ upcased version if it was indeed valid.
+
+Return Value:
+
+ TRUE if the character was valid.
+
+--*/
+
+{
+ WCHAR UniTmp;
+ WCHAR OemChar;
+
+ //
+ // First try to make a round trip from Unicode->Oem->Unicode.
+ //
+
+ if (!NlsMbOemCodePageTag) {
+
+ UniTmp = (WCHAR)NLS_UPCASE(NlsOemToUnicodeData[(UCHAR)NlsUnicodeToOemData[*Char]]);
+ OemChar = NlsUnicodeToOemData[UniTmp];
+
+ } else {
+
+ //
+ // Convert to OEM and back to Unicode before upper casing
+ // to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+
+ OemChar = NlsUnicodeToMbOemData[ *Char ];
+
+ if (NlsOemLeadByteInfo[HIBYTE(OemChar)]) {
+
+ USHORT Entry;
+
+ //
+ // Lead byte - translate the trail byte using the table
+ // that corresponds to this lead byte.
+ //
+
+ Entry = NlsOemLeadByteInfo[HIBYTE(OemChar)];
+ UniTmp = (WCHAR)NlsMbOemCodePageTables[ Entry + LOBYTE(OemChar) ];
+
+ } else {
+
+ //
+ // Single byte character.
+ //
+
+ UniTmp = NlsOemToUnicodeData[LOBYTE(OemChar)];
+ }
+
+ //
+ // Now upcase this UNICODE character, and convert it to Oem.
+ //
+
+ UniTmp = (WCHAR)NLS_UPCASE(UniTmp);
+ OemChar = NlsUnicodeToMbOemData[UniTmp];
+ }
+
+ //
+ // Now if the final OemChar is the default one, then there was no
+ // mapping for this UNICODE character.
+ //
+
+ if (OemChar == OemDefaultChar) {
+
+ return FALSE;
+
+ } else {
+
+ *Char = UniTmp;
+ return TRUE;
+ }
+}
+
+//
+// Internal support routine
+//
+
+USHORT
+RtlComputeLfnChecksum (
+ PUNICODE_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This routine computes the Chicago long file name checksum.
+
+Arguments:
+
+ Name - Supplies the name to compute the checksum on. Note that one
+ character names don't have interesting checksums.
+
+Return Value:
+
+ The checksum.
+
+--*/
+
+{
+ ULONG i;
+ USHORT Checksum;
+
+ RTL_PAGED_CODE();
+
+ if (Name->Length == sizeof(WCHAR)) {
+
+ return Name->Buffer[0];
+ }
+
+ Checksum = (Name->Buffer[0] << 8 + Name->Buffer[1]) & 0xffff;
+
+ //
+ // This checksum is kinda strange because we want to still have
+ // a good range even if all the characters are < 0x00ff.
+ //
+
+ for (i=2; i < Name->Length / sizeof(WCHAR); i+=2) {
+
+ Checksum = (Checksum & 1 ? 0x8000 : 0) +
+ (Checksum >> 1) +
+ (Name->Buffer[i] << 8);
+
+ //
+ // Be carefull to not walk off the end of the string.
+ //
+
+ if (i+1 < Name->Length / sizeof(WCHAR)) {
+
+ Checksum += Name->Buffer[i+1] & 0xffff;
+ }
+ }
+
+ return Checksum;
+}
+
+
+BOOLEAN
+RtlIsNameLegalDOS8Dot3 (
+ IN PUNICODE_STRING Name,
+ IN OUT POEM_STRING OemName OPTIONAL,
+ OUT PBOOLEAN NameContainsSpaces OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine takes an input string and gives a definitive answer
+ on whether this name can successfully be used to create a file
+ on the FAT file system.
+
+ This routine can therefore also be used to determine if a name is
+ appropriate to be passed back to a Win31 or DOS app, i.e. whether
+ the downlevel APP will understand the name.
+
+ Note: an important part of this test is the mapping from UNICODE
+ to Oem, which is why it is important that the input parameter be
+ received in UNICODE.
+
+Arguments:
+
+ Name - The UNICODE name to test for conformance to 8.3 symantics.
+
+ OemName - If specified, will receive the Oem name corresponding
+ to the passed in Name. Storage must be provided by the caller.
+ The name is undefined if the routine returns FALSE.
+
+ NameContainsSpaces - If the function returns TRUE, then this
+ parameter will indicate if the names contains spaces. If
+ the function returns FALSE, this parameter is undefined. In
+ many instances, the alternate name is more appropriate to
+ use if spaces are present in the principle name, even if
+ it is 8.3 compliant.
+
+Return Value:
+
+ BOOLEAN - TRUE if the passed in UNICODE name forms a valid 8.3
+ FAT name when upcased to the current Oem code page.
+
+--*/
+
+{
+ ULONG Index;
+ BOOLEAN ExtensionPresent = FALSE;
+ BOOLEAN SpacesPresent = FALSE;
+ OEM_STRING LocalOemName;
+ UCHAR Char;
+ UCHAR OemBuffer[12];
+
+ //
+ // If the name is more than 12 chars, bail.
+ //
+
+ if (Name->Length > 12*sizeof(WCHAR)) {
+ return FALSE;
+ }
+
+ //
+ // Now upcase this name to Oem. If anything goes wrong,
+ // return FALSE.
+ //
+
+ if (!ARGUMENT_PRESENT(OemName)) {
+
+ OemName = &LocalOemName;
+
+ OemName->Buffer = &OemBuffer[0];
+ OemName->Length = 0;
+ OemName->MaximumLength = 12;
+ }
+
+ if (!NT_SUCCESS(RtlUpcaseUnicodeStringToCountedOemString(OemName, Name, FALSE))) {
+ return FALSE;
+ }
+
+ //
+ // Special case . and ..
+ //
+
+ if (((OemName->Length == 1) && (OemName->Buffer[0] == '.')) ||
+ ((OemName->Length == 2) && (OemName->Buffer[0] == '.') && (OemName->Buffer[1] == '.'))) {
+
+ if (ARGUMENT_PRESENT(NameContainsSpaces)) {
+ *NameContainsSpaces = FALSE;
+ }
+ return TRUE;
+ }
+
+ //
+ // Now we are going to walk through the string looking for
+ // illegal characters and/or incorrect syntax.
+ //
+
+ for ( Index = 0; Index < OemName->Length; Index += 1 ) {
+
+ Char = OemName->Buffer[ Index ];
+
+ //
+ // Skip over and Dbcs chacters
+ //
+
+ if (NlsMbOemCodePageTag && NlsOemLeadByteInfo[Char]) {
+
+ //
+ // 1) if we're looking at base part ( !ExtensionPresent ) and the 8th byte
+ // is in the dbcs leading byte range, it's error ( Index == 7 ). If the
+ // length of base part is more than 8 ( Index > 7 ), it's definitely error.
+ //
+ // 2) if the last byte ( Index == DbcsName.Length - 1 ) is in the dbcs leading
+ // byte range, it's error
+ //
+
+ if ((!ExtensionPresent && (Index >= 7)) ||
+ (Index == (ULONG)(OemName->Length - 1))) {
+ return FALSE;
+ }
+
+ Index += 1;
+
+ continue;
+ }
+
+ //
+ // Make sure this character is legal.
+ //
+
+ if ((Char < 0x80) &&
+ (RtlFatIllegalTable[Char/32] & (1 << (Char%32)))) {
+ return FALSE;
+ }
+
+ //
+ // Remember if there was a space.
+ //
+
+ if (Char == ' ') {
+ SpacesPresent = TRUE;
+ }
+
+ if (Char == '.') {
+
+ //
+ // We stepped onto a period. We require the following things:
+ //
+ // - There can only be one
+ // - It can't be the first character
+ // - The previous character can't be a space.
+ // - There can't be more than 3 bytes following
+ //
+
+ if (ExtensionPresent ||
+ (Index == 0) ||
+ (OemName->Buffer[Index - 1] == ' ') ||
+ (OemName->Length - (Index + 1) > 3)) {
+
+ return FALSE;
+ }
+
+ ExtensionPresent = TRUE;
+ }
+
+ //
+ // The base part of the name can't be more than 8 characters long.
+ //
+
+ if ((Index >= 8) && !ExtensionPresent) { return FALSE; }
+ }
+
+ //
+ // The name cannot end in a space or a period.
+ //
+
+ if ((Char == ' ') || (Char == '.')) { return FALSE; }
+
+ if (ARGUMENT_PRESENT(NameContainsSpaces)) {
+ *NameContainsSpaces = SpacesPresent;
+ }
+ return TRUE;
+}
+
diff --git a/private/ntos/rtl/generr.c b/private/ntos/rtl/generr.c
new file mode 100644
index 000000000..47e02466d
--- /dev/null
+++ b/private/ntos/rtl/generr.c
@@ -0,0 +1,1260 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ generr.c
+
+Abstract:
+
+ This module contains code to generate the NT status code to DOS
+ error code table that is used by the runtime to translate status
+ codes.
+
+Author:
+
+ David N. Cutler (davec) 2-Dec-1992
+
+Revision History:
+
+--*/
+
+#include <ntos.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+#include <wtypes.h>
+#include <issperr.h>
+#include "stdio.h"
+#include "stdarg.h"
+#include "stdlib.h"
+
+//
+// Ensure that the Registry ERROR_SUCCESS error code and the
+// NO_ERROR error code remain equal and zero.
+//
+
+#if ERROR_SUCCESS != 0 || NO_ERROR != 0
+#error Invalid value for ERROR_SUCCESS.
+#endif
+
+//
+// The following error code table contains paired entries in a singly
+// dimensioned array. The first member of a paired entry is an NT status
+// code and the second member is the DOS error code that it translates to.
+//
+// To add a value to this table simply insert the NT status/DOS error code
+// pair anywhere is the table. If multiple NT status codes map to a single
+// DOS error code, then insert a paired entry for each of the code pairs.
+//
+#ifdef i386
+#pragma warning (4:4018) // lower to -W4
+#endif
+LONG CodePairs[] = {
+
+ //
+ // Exception codes defined in WINNT.H can map to themselves.
+ //
+
+ STATUS_BREAKPOINT, STATUS_BREAKPOINT,
+ STATUS_SINGLE_STEP, STATUS_SINGLE_STEP,
+ STATUS_ILLEGAL_INSTRUCTION, STATUS_ILLEGAL_INSTRUCTION,
+ STATUS_NONCONTINUABLE_EXCEPTION, STATUS_NONCONTINUABLE_EXCEPTION,
+ STATUS_INVALID_DISPOSITION, STATUS_INVALID_DISPOSITION,
+ STATUS_PARITY_ERROR, STATUS_PARITY_ERROR,
+ STATUS_ARRAY_BOUNDS_EXCEEDED, STATUS_ARRAY_BOUNDS_EXCEEDED,
+ STATUS_FLOAT_DENORMAL_OPERAND, STATUS_FLOAT_DENORMAL_OPERAND,
+ STATUS_FLOAT_DIVIDE_BY_ZERO, STATUS_FLOAT_DIVIDE_BY_ZERO,
+ STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INEXACT_RESULT,
+ STATUS_FLOAT_INVALID_OPERATION, STATUS_FLOAT_INVALID_OPERATION,
+ STATUS_FLOAT_OVERFLOW, STATUS_FLOAT_OVERFLOW,
+ STATUS_FLOAT_STACK_CHECK, STATUS_FLOAT_STACK_CHECK,
+ STATUS_FLOAT_UNDERFLOW, STATUS_FLOAT_UNDERFLOW,
+ STATUS_INTEGER_DIVIDE_BY_ZERO, STATUS_INTEGER_DIVIDE_BY_ZERO,
+ STATUS_PRIVILEGED_INSTRUCTION, STATUS_PRIVILEGED_INSTRUCTION,
+ STATUS_GUARD_PAGE_VIOLATION, STATUS_GUARD_PAGE_VIOLATION,
+
+ //
+ // These are also defined in WINNT.H, but we are stuck with these
+ // mappings.
+ //
+
+ STATUS_DATATYPE_MISALIGNMENT, ERROR_NOACCESS,
+ STATUS_ACCESS_VIOLATION, ERROR_NOACCESS,
+
+ STATUS_CTL_FILE_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
+ STATUS_PORT_ALREADY_SET, ERROR_INVALID_PARAMETER,
+ STATUS_SECTION_NOT_IMAGE, ERROR_INVALID_PARAMETER,
+ STATUS_BAD_WORKING_SET_LIMIT, ERROR_INVALID_PARAMETER,
+ STATUS_WORKING_SET_LIMIT_RANGE, ERROR_INVALID_PARAMETER,
+ STATUS_INCOMPATIBLE_FILE_MAP, ERROR_INVALID_PARAMETER,
+ STATUS_PORT_DISCONNECTED, ERROR_INVALID_HANDLE,
+ STATUS_NOT_LOCKED, ERROR_NOT_LOCKED,
+ STATUS_NOT_MAPPED_VIEW, ERROR_INVALID_ADDRESS,
+ STATUS_UNABLE_TO_FREE_VM, ERROR_INVALID_PARAMETER,
+ STATUS_UNABLE_TO_DELETE_SECTION, ERROR_INVALID_PARAMETER,
+ STATUS_MORE_PROCESSING_REQUIRED, ERROR_MORE_DATA,
+ STATUS_INVALID_CID, ERROR_INVALID_PARAMETER,
+ STATUS_STACK_OVERFLOW, ERROR_STACK_OVERFLOW,
+ STATUS_BAD_INITIAL_STACK, ERROR_STACK_OVERFLOW,
+ STATUS_INVALID_VOLUME_LABEL, ERROR_LABEL_TOO_LONG,
+ STATUS_SECTION_NOT_EXTENDED, ERROR_OUTOFMEMORY,
+ STATUS_NOT_MAPPED_DATA, ERROR_INVALID_ADDRESS,
+
+ STATUS_INFO_LENGTH_MISMATCH, ERROR_BAD_LENGTH,
+ STATUS_INVALID_INFO_CLASS, ERROR_INVALID_PARAMETER,
+
+ STATUS_SUSPEND_COUNT_EXCEEDED, ERROR_SIGNAL_REFUSED,
+
+ STATUS_NOTIFY_ENUM_DIR, ERROR_NOTIFY_ENUM_DIR,
+
+ STATUS_REGISTRY_RECOVERED, ERROR_REGISTRY_RECOVERED,
+
+ STATUS_REGISTRY_IO_FAILED, ERROR_REGISTRY_IO_FAILED,
+
+ STATUS_NOT_REGISTRY_FILE, ERROR_NOT_REGISTRY_FILE,
+
+ STATUS_KEY_DELETED, ERROR_KEY_DELETED,
+
+ STATUS_NO_LOG_SPACE, ERROR_NO_LOG_SPACE,
+
+ STATUS_KEY_HAS_CHILDREN, ERROR_KEY_HAS_CHILDREN,
+
+ STATUS_CHILD_MUST_BE_VOLATILE, ERROR_CHILD_MUST_BE_VOLATILE,
+
+ STATUS_REGISTRY_CORRUPT, ERROR_BADDB,
+
+ STATUS_DLL_NOT_FOUND, ERROR_MOD_NOT_FOUND,
+
+ STATUS_DLL_INIT_FAILED, ERROR_DLL_INIT_FAILED,
+
+ STATUS_ORDINAL_NOT_FOUND, ERROR_INVALID_ORDINAL,
+ STATUS_DRIVER_ORDINAL_NOT_FOUND, ERROR_INVALID_ORDINAL,
+
+ STATUS_ENTRYPOINT_NOT_FOUND, ERROR_PROC_NOT_FOUND,
+ STATUS_DRIVER_ENTRYPOINT_NOT_FOUND, ERROR_PROC_NOT_FOUND,
+
+ STATUS_PENDING, ERROR_IO_PENDING,
+
+ STATUS_MORE_ENTRIES, ERROR_MORE_DATA,
+
+ STATUS_INTEGER_OVERFLOW, ERROR_ARITHMETIC_OVERFLOW,
+
+ STATUS_BUFFER_OVERFLOW, ERROR_MORE_DATA,
+
+ STATUS_NO_MORE_FILES, ERROR_NO_MORE_FILES,
+
+ STATUS_NO_INHERITANCE, ERROR_NO_INHERITANCE,
+
+ STATUS_NO_MORE_EAS, ERROR_NO_MORE_ITEMS,
+ STATUS_NO_MORE_ENTRIES, ERROR_NO_MORE_ITEMS,
+ STATUS_GUIDS_EXHAUSTED, ERROR_NO_MORE_ITEMS,
+ STATUS_AGENTS_EXHAUSTED, ERROR_NO_MORE_ITEMS,
+
+ STATUS_UNSUCCESSFUL, ERROR_GEN_FAILURE,
+
+ STATUS_TOO_MANY_LINKS, ERROR_TOO_MANY_LINKS,
+
+ STATUS_NOT_IMPLEMENTED, ERROR_INVALID_FUNCTION,
+ STATUS_ILLEGAL_FUNCTION, ERROR_INVALID_FUNCTION,
+
+ STATUS_IN_PAGE_ERROR, ERROR_SWAPERROR,
+
+ STATUS_PAGEFILE_QUOTA, ERROR_PAGEFILE_QUOTA,
+ STATUS_COMMITMENT_LIMIT, ERROR_COMMITMENT_LIMIT,
+ STATUS_SECTION_TOO_BIG, ERROR_NOT_ENOUGH_MEMORY,
+
+ RPC_NT_SS_IN_NULL_CONTEXT, ERROR_INVALID_HANDLE,
+ RPC_NT_INVALID_BINDING, ERROR_INVALID_HANDLE,
+ STATUS_INVALID_HANDLE, ERROR_INVALID_HANDLE,
+ STATUS_OBJECT_TYPE_MISMATCH, ERROR_INVALID_HANDLE,
+ STATUS_FILE_CLOSED, ERROR_INVALID_HANDLE,
+ STATUS_INVALID_PORT_HANDLE, ERROR_INVALID_HANDLE,
+ STATUS_HANDLE_NOT_CLOSABLE, ERROR_INVALID_HANDLE,
+
+ STATUS_NOT_COMMITTED, ERROR_INVALID_ADDRESS,
+ STATUS_PARTIAL_COPY, ERROR_PARTIAL_COPY,
+
+ STATUS_LPC_REPLY_LOST, ERROR_INTERNAL_ERROR,
+ STATUS_INVALID_PARAMETER, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_1, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_2, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_3, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_4, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_5, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_6, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_7, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_8, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_9, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_10, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_11, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_12, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PARAMETER_MIX, ERROR_INVALID_PARAMETER,
+ STATUS_INVALID_PAGE_PROTECTION, ERROR_INVALID_PARAMETER,
+ STATUS_SECTION_PROTECTION, ERROR_INVALID_PARAMETER,
+
+ STATUS_RESOURCE_DATA_NOT_FOUND, ERROR_RESOURCE_DATA_NOT_FOUND,
+ STATUS_RESOURCE_TYPE_NOT_FOUND, ERROR_RESOURCE_TYPE_NOT_FOUND,
+ STATUS_RESOURCE_NAME_NOT_FOUND, ERROR_RESOURCE_NAME_NOT_FOUND,
+ STATUS_RESOURCE_LANG_NOT_FOUND, ERROR_RESOURCE_LANG_NOT_FOUND,
+
+ STATUS_NO_SUCH_DEVICE, ERROR_FILE_NOT_FOUND,
+ STATUS_NO_SUCH_FILE, ERROR_FILE_NOT_FOUND,
+
+ STATUS_INVALID_DEVICE_REQUEST, ERROR_INVALID_FUNCTION,
+
+ STATUS_END_OF_FILE, ERROR_HANDLE_EOF,
+ STATUS_FILE_FORCED_CLOSED, ERROR_HANDLE_EOF,
+
+ STATUS_WRONG_VOLUME, ERROR_WRONG_DISK,
+
+ STATUS_NO_MEDIA, ERROR_NO_MEDIA_IN_DRIVE,
+
+ STATUS_NO_MEDIA_IN_DEVICE, ERROR_NOT_READY,
+
+ STATUS_NONEXISTENT_SECTOR, ERROR_SECTOR_NOT_FOUND,
+
+ STATUS_WORKING_SET_QUOTA, ERROR_WORKING_SET_QUOTA,
+ STATUS_NO_MEMORY, ERROR_NOT_ENOUGH_MEMORY,
+ STATUS_CONFLICTING_ADDRESSES, ERROR_INVALID_ADDRESS,
+
+ STATUS_INVALID_SYSTEM_SERVICE, ERROR_INVALID_FUNCTION,
+
+ STATUS_THREAD_IS_TERMINATING, ERROR_ACCESS_DENIED,
+ STATUS_PROCESS_IS_TERMINATING, ERROR_ACCESS_DENIED,
+ STATUS_INVALID_LOCK_SEQUENCE, ERROR_ACCESS_DENIED,
+ STATUS_INVALID_VIEW_SIZE, ERROR_ACCESS_DENIED,
+ STATUS_ALREADY_COMMITTED, ERROR_ACCESS_DENIED,
+ STATUS_ACCESS_DENIED, ERROR_ACCESS_DENIED,
+ STATUS_FILE_IS_A_DIRECTORY, ERROR_ACCESS_DENIED,
+ STATUS_CANNOT_DELETE, ERROR_ACCESS_DENIED,
+ STATUS_INVALID_COMPUTER_NAME, ERROR_INVALID_COMPUTERNAME,
+ STATUS_FILE_DELETED, ERROR_ACCESS_DENIED,
+ STATUS_DELETE_PENDING, ERROR_ACCESS_DENIED,
+ STATUS_PORT_CONNECTION_REFUSED, ERROR_ACCESS_DENIED,
+
+ STATUS_NO_SUCH_PRIVILEGE, ERROR_NO_SUCH_PRIVILEGE,
+
+ STATUS_PRIVILEGE_NOT_HELD, ERROR_PRIVILEGE_NOT_HELD,
+
+ STATUS_CANNOT_IMPERSONATE, ERROR_CANNOT_IMPERSONATE,
+
+ STATUS_LOGON_FAILURE, ERROR_LOGON_FAILURE,
+
+ STATUS_ACCOUNT_RESTRICTION, ERROR_ACCOUNT_RESTRICTION,
+
+ STATUS_INVALID_LOGON_HOURS, ERROR_INVALID_LOGON_HOURS,
+
+ STATUS_INVALID_WORKSTATION, ERROR_INVALID_WORKSTATION,
+
+ STATUS_BUFFER_TOO_SMALL, ERROR_INSUFFICIENT_BUFFER,
+
+ STATUS_UNABLE_TO_DECOMMIT_VM, ERROR_INVALID_ADDRESS,
+
+ STATUS_DISK_CORRUPT_ERROR, ERROR_DISK_CORRUPT,
+ STATUS_FT_MISSING_MEMBER, ERROR_IO_DEVICE,
+ STATUS_FT_ORPHANING, ERROR_IO_DEVICE,
+
+ STATUS_VARIABLE_NOT_FOUND, ERROR_ENVVAR_NOT_FOUND,
+
+ STATUS_OBJECT_NAME_INVALID, ERROR_INVALID_NAME,
+
+ STATUS_OBJECT_NAME_NOT_FOUND, ERROR_FILE_NOT_FOUND,
+
+ STATUS_OBJECT_NAME_COLLISION, ERROR_ALREADY_EXISTS,
+
+ STATUS_OBJECT_PATH_INVALID, ERROR_BAD_PATHNAME,
+
+ STATUS_OBJECT_PATH_NOT_FOUND, ERROR_PATH_NOT_FOUND,
+ STATUS_DFS_EXIT_PATH_FOUND, ERROR_PATH_NOT_FOUND,
+
+ STATUS_OBJECT_PATH_SYNTAX_BAD, ERROR_BAD_PATHNAME,
+
+ STATUS_NAME_TOO_LONG, ERROR_FILENAME_EXCED_RANGE,
+
+ STATUS_DATA_OVERRUN, ERROR_IO_DEVICE,
+ STATUS_DATA_LATE_ERROR, ERROR_IO_DEVICE,
+ STATUS_DATA_ERROR, ERROR_CRC,
+
+ STATUS_CRC_ERROR, ERROR_CRC,
+
+ STATUS_SHARING_VIOLATION, ERROR_SHARING_VIOLATION,
+
+ STATUS_QUOTA_EXCEEDED, ERROR_NOT_ENOUGH_QUOTA,
+
+ STATUS_MUTANT_NOT_OWNED, ERROR_NOT_OWNER,
+
+ STATUS_SEMAPHORE_LIMIT_EXCEEDED, ERROR_TOO_MANY_POSTS,
+
+ STATUS_DISK_FULL, ERROR_DISK_FULL,
+
+ STATUS_LOCK_NOT_GRANTED, ERROR_LOCK_VIOLATION,
+
+ STATUS_FILE_LOCK_CONFLICT, ERROR_LOCK_VIOLATION, // FIX, FIX - is this right?
+
+ STATUS_NOT_A_DIRECTORY, ERROR_DIRECTORY, // FIX, FIX - is this right?
+
+ STATUS_UNKNOWN_REVISION, ERROR_UNKNOWN_REVISION,
+
+ STATUS_REVISION_MISMATCH, ERROR_REVISION_MISMATCH,
+
+ STATUS_INVALID_OWNER, ERROR_INVALID_OWNER,
+
+ STATUS_INVALID_PRIMARY_GROUP, ERROR_INVALID_PRIMARY_GROUP,
+
+ STATUS_NO_IMPERSONATION_TOKEN, ERROR_NO_IMPERSONATION_TOKEN,
+
+ STATUS_CANT_DISABLE_MANDATORY, ERROR_CANT_DISABLE_MANDATORY,
+
+ STATUS_NO_LOGON_SERVERS, ERROR_NO_LOGON_SERVERS,
+ STATUS_DOMAIN_CONTROLLER_NOT_FOUND, ERROR_DOMAIN_CONTROLLER_NOT_FOUND,
+
+ STATUS_NO_SUCH_LOGON_SESSION, ERROR_NO_SUCH_LOGON_SESSION,
+
+ STATUS_INVALID_ACCOUNT_NAME, ERROR_INVALID_ACCOUNT_NAME,
+
+ STATUS_USER_EXISTS, ERROR_USER_EXISTS,
+
+ STATUS_NO_SUCH_USER, ERROR_NO_SUCH_USER,
+
+ STATUS_GROUP_EXISTS, ERROR_GROUP_EXISTS,
+
+ STATUS_NO_SUCH_GROUP, ERROR_NO_SUCH_GROUP,
+
+ STATUS_SPECIAL_GROUP, ERROR_SPECIAL_GROUP,
+
+ STATUS_MEMBER_IN_GROUP, ERROR_MEMBER_IN_GROUP,
+
+ STATUS_MEMBER_NOT_IN_GROUP, ERROR_MEMBER_NOT_IN_GROUP,
+
+ STATUS_LAST_ADMIN, ERROR_LAST_ADMIN,
+
+ STATUS_WRONG_PASSWORD, ERROR_INVALID_PASSWORD,
+ STATUS_WRONG_PASSWORD_CORE, ERROR_INVALID_PASSWORD,
+
+ STATUS_ILL_FORMED_PASSWORD, ERROR_ILL_FORMED_PASSWORD,
+
+ STATUS_PASSWORD_RESTRICTION, ERROR_PASSWORD_RESTRICTION,
+
+ STATUS_PASSWORD_EXPIRED, ERROR_PASSWORD_EXPIRED,
+ STATUS_PASSWORD_MUST_CHANGE, ERROR_PASSWORD_MUST_CHANGE,
+
+ STATUS_ACCOUNT_DISABLED, ERROR_ACCOUNT_DISABLED,
+ STATUS_ACCOUNT_LOCKED_OUT, ERROR_ACCOUNT_LOCKED_OUT,
+
+ STATUS_NONE_MAPPED, ERROR_NONE_MAPPED,
+
+ STATUS_TOO_MANY_LUIDS_REQUESTED, ERROR_TOO_MANY_LUIDS_REQUESTED,
+
+ STATUS_LUIDS_EXHAUSTED, ERROR_LUIDS_EXHAUSTED,
+
+ STATUS_INVALID_SUB_AUTHORITY, ERROR_INVALID_SUB_AUTHORITY,
+
+ STATUS_INVALID_ACL, ERROR_INVALID_ACL,
+
+ STATUS_INVALID_SID, ERROR_INVALID_SID,
+
+ STATUS_INVALID_SECURITY_DESCR, ERROR_INVALID_SECURITY_DESCR,
+
+ STATUS_PROCEDURE_NOT_FOUND, ERROR_PROC_NOT_FOUND,
+
+ STATUS_BAD_INITIAL_PC, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_FILE_FOR_SECTION, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_IMAGE_FORMAT, ERROR_BAD_EXE_FORMAT,
+ STATUS_IMAGE_MP_UP_MISMATCH, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_IMAGE_NOT_MZ, ERROR_BAD_EXE_FORMAT,
+ STATUS_IMAGE_CHECKSUM_MISMATCH, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_IMAGE_PROTECT, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_IMAGE_LE_FORMAT, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_IMAGE_NE_FORMAT, ERROR_BAD_EXE_FORMAT,
+ STATUS_INVALID_IMAGE_WIN_16, ERROR_BAD_EXE_FORMAT,
+
+ STATUS_NO_TOKEN, ERROR_NO_TOKEN,
+
+ STATUS_RANGE_NOT_LOCKED, ERROR_NOT_LOCKED,
+
+ STATUS_SERVER_DISABLED, ERROR_SERVER_DISABLED,
+
+ STATUS_SERVER_NOT_DISABLED, ERROR_SERVER_NOT_DISABLED,
+
+ STATUS_INVALID_ID_AUTHORITY, ERROR_INVALID_ID_AUTHORITY,
+
+ STATUS_ALLOTTED_SPACE_EXCEEDED, ERROR_ALLOTTED_SPACE_EXCEEDED,
+
+ STATUS_TOO_MANY_PAGING_FILES, ERROR_NOT_ENOUGH_MEMORY,
+ STATUS_INSUFFICIENT_RESOURCES, ERROR_NO_SYSTEM_RESOURCES,
+
+ STATUS_INSUFF_SERVER_RESOURCES, ERROR_NOT_ENOUGH_SERVER_MEMORY,
+
+ STATUS_FILE_INVALID, ERROR_FILE_INVALID,
+ STATUS_MAPPED_FILE_SIZE_ZERO, ERROR_FILE_INVALID,
+
+ STATUS_DEVICE_PAPER_EMPTY, ERROR_OUT_OF_PAPER,
+
+ STATUS_DEVICE_POWERED_OFF, ERROR_NOT_READY,
+ STATUS_DEVICE_OFF_LINE, ERROR_NOT_READY,
+
+ STATUS_DEVICE_DATA_ERROR, ERROR_CRC,
+
+ STATUS_DEVICE_NOT_READY, ERROR_NOT_READY,
+ STATUS_DEVICE_NOT_CONNECTED, ERROR_NOT_READY,
+ STATUS_DEVICE_POWER_FAILURE, ERROR_NOT_READY,
+
+ STATUS_DEVICE_BUSY, ERROR_BUSY, // FIX, FIX - is there a better choice?
+
+ STATUS_FREE_VM_NOT_AT_BASE, ERROR_INVALID_ADDRESS,
+ STATUS_MEMORY_NOT_ALLOCATED, ERROR_INVALID_ADDRESS,
+
+ STATUS_NOT_SAME_DEVICE, ERROR_NOT_SAME_DEVICE,
+
+ STATUS_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
+
+ STATUS_REMOTE_NOT_LISTENING, ERROR_REM_NOT_LIST,
+
+ STATUS_DUPLICATE_NAME, ERROR_DUP_NAME,
+
+ STATUS_BAD_NETWORK_PATH, ERROR_BAD_NETPATH,
+
+ STATUS_NETWORK_BUSY, ERROR_NETWORK_BUSY,
+
+ STATUS_DEVICE_DOES_NOT_EXIST, ERROR_DEV_NOT_EXIST,
+
+ STATUS_TOO_MANY_COMMANDS, ERROR_TOO_MANY_CMDS,
+
+ STATUS_ADAPTER_HARDWARE_ERROR, ERROR_ADAP_HDW_ERR,
+
+ STATUS_REDIRECTOR_NOT_STARTED, ERROR_PATH_NOT_FOUND,
+
+ STATUS_INVALID_EA_NAME, ERROR_INVALID_EA_NAME,
+
+ STATUS_EA_LIST_INCONSISTENT, ERROR_EA_LIST_INCONSISTENT,
+ STATUS_EA_TOO_LARGE, ERROR_EA_LIST_INCONSISTENT,
+ STATUS_INVALID_EA_FLAG, ERROR_EA_LIST_INCONSISTENT,
+
+ STATUS_FILE_CORRUPT_ERROR, ERROR_FILE_CORRUPT,
+ STATUS_EA_CORRUPT_ERROR, ERROR_FILE_CORRUPT,
+ STATUS_NONEXISTENT_EA_ENTRY, ERROR_FILE_CORRUPT,
+ STATUS_NO_EAS_ON_FILE, ERROR_FILE_CORRUPT,
+
+ STATUS_INVALID_NETWORK_RESPONSE, ERROR_BAD_NET_RESP,
+
+ STATUS_USER_SESSION_DELETED, ERROR_UNEXP_NET_ERR,
+ STATUS_UNEXPECTED_NETWORK_ERROR, ERROR_UNEXP_NET_ERR,
+
+ STATUS_USER_SESSION_DELETED, ERROR_UNEXP_NET_ERR,
+
+ STATUS_BAD_REMOTE_ADAPTER, ERROR_BAD_REM_ADAP,
+
+ STATUS_PRINT_QUEUE_FULL, ERROR_PRINTQ_FULL,
+
+ STATUS_NO_SPOOL_SPACE, ERROR_NO_SPOOL_SPACE,
+
+ STATUS_PRINT_CANCELLED, ERROR_PRINT_CANCELLED,
+
+ STATUS_NETWORK_NAME_DELETED, ERROR_NETNAME_DELETED,
+
+ STATUS_NETWORK_ACCESS_DENIED, ERROR_NETWORK_ACCESS_DENIED,
+
+ STATUS_BAD_DEVICE_TYPE, ERROR_BAD_DEV_TYPE,
+
+ STATUS_BAD_NETWORK_NAME, ERROR_BAD_NET_NAME,
+
+ STATUS_TOO_MANY_NAMES, ERROR_TOO_MANY_NAMES,
+ STATUS_TOO_MANY_GUIDS_REQUESTED, ERROR_TOO_MANY_NAMES,
+ STATUS_TOO_MANY_ADDRESSES, ERROR_TOO_MANY_NAMES,
+ STATUS_TOO_MANY_NODES, ERROR_TOO_MANY_NAMES,
+
+ STATUS_TOO_MANY_SESSIONS, ERROR_TOO_MANY_SESS,
+
+ STATUS_SHARING_PAUSED, ERROR_SHARING_PAUSED,
+
+ STATUS_REQUEST_NOT_ACCEPTED, ERROR_REQ_NOT_ACCEP,
+
+ STATUS_REDIRECTOR_PAUSED, ERROR_REDIR_PAUSED,
+
+ STATUS_NET_WRITE_FAULT, ERROR_NET_WRITE_FAULT,
+
+ STATUS_VIRTUAL_CIRCUIT_CLOSED, ERROR_VC_DISCONNECTED,
+
+ STATUS_INVALID_PIPE_STATE, ERROR_BAD_PIPE,
+ STATUS_INVALID_READ_MODE, ERROR_BAD_PIPE,
+
+ STATUS_PIPE_CLOSING, ERROR_NO_DATA,
+ STATUS_PIPE_EMPTY, ERROR_NO_DATA,
+
+ STATUS_PIPE_CONNECTED, ERROR_PIPE_CONNECTED,
+
+ STATUS_PIPE_DISCONNECTED, ERROR_PIPE_NOT_CONNECTED,
+
+ STATUS_PIPE_LISTENING, ERROR_PIPE_LISTENING,
+
+ STATUS_PIPE_NOT_AVAILABLE, ERROR_PIPE_BUSY,
+ STATUS_INSTANCE_NOT_AVAILABLE, ERROR_PIPE_BUSY,
+ STATUS_PIPE_BUSY, ERROR_PIPE_BUSY,
+
+ STATUS_PIPE_BROKEN, ERROR_BROKEN_PIPE,
+
+ STATUS_DIRECTORY_NOT_EMPTY, ERROR_DIR_NOT_EMPTY,
+
+ STATUS_TOO_MANY_OPENED_FILES, ERROR_TOO_MANY_OPEN_FILES,
+
+ STATUS_IO_TIMEOUT, ERROR_SEM_TIMEOUT,
+
+ STATUS_CANCELLED, ERROR_OPERATION_ABORTED,
+
+ STATUS_UNRECOGNIZED_MEDIA, ERROR_UNRECOGNIZED_MEDIA,
+
+ STATUS_INVALID_LEVEL, ERROR_INVALID_LEVEL,
+
+ STATUS_UNRECOGNIZED_VOLUME, ERROR_UNRECOGNIZED_VOLUME,
+
+ STATUS_MEDIA_WRITE_PROTECTED, ERROR_WRITE_PROTECT,
+ STATUS_TOO_LATE, ERROR_WRITE_PROTECT,
+
+ STATUS_SUCCESS, NO_ERROR,
+
+ STATUS_FULLSCREEN_MODE, ERROR_FULLSCREEN_MODE,
+
+ STATUS_END_OF_MEDIA, ERROR_END_OF_MEDIA,
+
+ STATUS_EOM_OVERFLOW, ERROR_EOM_OVERFLOW,
+
+ STATUS_BEGINNING_OF_MEDIA, ERROR_BEGINNING_OF_MEDIA,
+
+ STATUS_MEDIA_CHANGED, ERROR_MEDIA_CHANGED,
+
+ STATUS_BUS_RESET, ERROR_BUS_RESET,
+
+ STATUS_FILEMARK_DETECTED, ERROR_FILEMARK_DETECTED,
+
+ STATUS_SETMARK_DETECTED, ERROR_SETMARK_DETECTED,
+
+ STATUS_NO_DATA_DETECTED, ERROR_NO_DATA_DETECTED,
+
+ STATUS_PARTITION_FAILURE, ERROR_PARTITION_FAILURE,
+
+ STATUS_INVALID_BLOCK_LENGTH, ERROR_INVALID_BLOCK_LENGTH,
+
+ STATUS_DEVICE_NOT_PARTITIONED, ERROR_DEVICE_NOT_PARTITIONED,
+
+ STATUS_UNABLE_TO_LOCK_MEDIA, ERROR_UNABLE_TO_LOCK_MEDIA,
+
+ STATUS_UNABLE_TO_UNLOAD_MEDIA, ERROR_UNABLE_TO_UNLOAD_MEDIA,
+
+ STATUS_UNMAPPABLE_CHARACTER, ERROR_NO_UNICODE_TRANSLATION,
+
+ STATUS_NOT_ALL_ASSIGNED, ERROR_NOT_ALL_ASSIGNED,
+
+ STATUS_SOME_NOT_MAPPED, ERROR_SOME_NOT_MAPPED,
+
+ STATUS_NO_QUOTAS_FOR_ACCOUNT, ERROR_NO_QUOTAS_FOR_ACCOUNT,
+
+ STATUS_LOCAL_USER_SESSION_KEY, ERROR_LOCAL_USER_SESSION_KEY,
+
+ STATUS_NULL_LM_PASSWORD, ERROR_NULL_LM_PASSWORD,
+
+ STATUS_BAD_INHERITANCE_ACL, ERROR_BAD_INHERITANCE_ACL,
+
+ STATUS_INVALID_GROUP_ATTRIBUTES, ERROR_INVALID_GROUP_ATTRIBUTES,
+
+ STATUS_BAD_IMPERSONATION_LEVEL, ERROR_BAD_IMPERSONATION_LEVEL,
+
+ STATUS_CANT_OPEN_ANONYMOUS, ERROR_CANT_OPEN_ANONYMOUS,
+
+ STATUS_BAD_VALIDATION_CLASS, ERROR_BAD_VALIDATION_CLASS,
+
+ STATUS_BAD_TOKEN_TYPE, ERROR_BAD_TOKEN_TYPE,
+
+ STATUS_NO_SECURITY_ON_OBJECT, ERROR_NO_SECURITY_ON_OBJECT,
+
+ STATUS_CANT_ACCESS_DOMAIN_INFO, ERROR_CANT_ACCESS_DOMAIN_INFO,
+
+ STATUS_INVALID_SERVER_STATE, ERROR_INVALID_SERVER_STATE,
+
+ STATUS_INVALID_DOMAIN_STATE, ERROR_INVALID_DOMAIN_STATE,
+
+ STATUS_INVALID_DOMAIN_ROLE, ERROR_INVALID_DOMAIN_ROLE,
+
+ STATUS_NO_SUCH_DOMAIN, ERROR_NO_SUCH_DOMAIN,
+
+ STATUS_DOMAIN_EXISTS, ERROR_DOMAIN_EXISTS,
+
+ STATUS_DOMAIN_LIMIT_EXCEEDED, ERROR_DOMAIN_LIMIT_EXCEEDED,
+
+ STATUS_INTERNAL_DB_CORRUPTION, ERROR_INTERNAL_DB_CORRUPTION,
+
+ STATUS_INTERNAL_ERROR, ERROR_INTERNAL_ERROR,
+
+ STATUS_GENERIC_NOT_MAPPED, ERROR_GENERIC_NOT_MAPPED,
+
+ STATUS_BAD_DESCRIPTOR_FORMAT, ERROR_BAD_DESCRIPTOR_FORMAT,
+
+ STATUS_NOT_LOGON_PROCESS, ERROR_NOT_LOGON_PROCESS,
+
+ STATUS_LOGON_SESSION_EXISTS, ERROR_LOGON_SESSION_EXISTS,
+
+ STATUS_NO_SUCH_PACKAGE, ERROR_NO_SUCH_PACKAGE,
+
+ STATUS_BAD_LOGON_SESSION_STATE, ERROR_BAD_LOGON_SESSION_STATE,
+
+ STATUS_LOGON_SESSION_COLLISION, ERROR_LOGON_SESSION_COLLISION,
+
+ STATUS_INVALID_LOGON_TYPE, ERROR_INVALID_LOGON_TYPE,
+
+ STATUS_RXACT_INVALID_STATE, ERROR_RXACT_INVALID_STATE,
+
+ STATUS_RXACT_COMMIT_FAILURE, ERROR_RXACT_COMMIT_FAILURE,
+
+ STATUS_SPECIAL_ACCOUNT, ERROR_SPECIAL_ACCOUNT,
+
+ STATUS_SPECIAL_USER, ERROR_SPECIAL_USER,
+
+ STATUS_MEMBERS_PRIMARY_GROUP, ERROR_MEMBERS_PRIMARY_GROUP,
+
+ STATUS_TOKEN_ALREADY_IN_USE, ERROR_TOKEN_ALREADY_IN_USE,
+
+ STATUS_NO_SUCH_ALIAS, ERROR_NO_SUCH_ALIAS,
+
+ STATUS_MEMBER_NOT_IN_ALIAS, ERROR_MEMBER_NOT_IN_ALIAS,
+
+ STATUS_MEMBER_IN_ALIAS, ERROR_MEMBER_IN_ALIAS,
+
+ STATUS_ALIAS_EXISTS, ERROR_ALIAS_EXISTS,
+
+ STATUS_LOGON_NOT_GRANTED, ERROR_LOGON_NOT_GRANTED,
+
+ STATUS_TOO_MANY_SECRETS, ERROR_TOO_MANY_SECRETS,
+
+ STATUS_SECRET_TOO_LONG, ERROR_SECRET_TOO_LONG,
+
+ STATUS_INTERNAL_DB_ERROR, ERROR_INTERNAL_DB_ERROR,
+
+ STATUS_TOO_MANY_CONTEXT_IDS, ERROR_TOO_MANY_CONTEXT_IDS,
+
+ STATUS_LOGON_TYPE_NOT_GRANTED, ERROR_LOGON_TYPE_NOT_GRANTED,
+
+ STATUS_NT_CROSS_ENCRYPTION_REQUIRED, ERROR_NT_CROSS_ENCRYPTION_REQUIRED,
+
+ STATUS_NO_SUCH_MEMBER, ERROR_NO_SUCH_MEMBER,
+
+ STATUS_INVALID_MEMBER, ERROR_INVALID_MEMBER,
+
+ STATUS_TOO_MANY_SIDS, ERROR_TOO_MANY_SIDS,
+
+ STATUS_LM_CROSS_ENCRYPTION_REQUIRED, ERROR_LM_CROSS_ENCRYPTION_REQUIRED,
+
+ STATUS_MESSAGE_NOT_FOUND, ERROR_MR_MID_NOT_FOUND,
+
+ STATUS_LOCAL_DISCONNECT, ERROR_NETNAME_DELETED,
+ STATUS_REMOTE_DISCONNECT, ERROR_NETNAME_DELETED,
+
+ STATUS_REMOTE_RESOURCES, ERROR_REM_NOT_LIST,
+
+ STATUS_LINK_FAILED, ERROR_UNEXP_NET_ERR,
+ STATUS_LINK_TIMEOUT, ERROR_UNEXP_NET_ERR,
+
+ STATUS_INVALID_CONNECTION, ERROR_UNEXP_NET_ERR,
+ STATUS_INVALID_ADDRESS, ERROR_UNEXP_NET_ERR,
+
+ STATUS_IO_DEVICE_ERROR, ERROR_IO_DEVICE,
+ STATUS_DEVICE_PROTOCOL_ERROR, ERROR_IO_DEVICE,
+ STATUS_DRIVER_INTERNAL_ERROR, ERROR_IO_DEVICE,
+
+ STATUS_INVALID_DEVICE_STATE, ERROR_BAD_COMMAND,
+
+ STATUS_DEVICE_CONFIGURATION_ERROR, ERROR_INVALID_PARAMETER,
+
+ STATUS_INVALID_USER_BUFFER, ERROR_INVALID_USER_BUFFER,
+
+ STATUS_SERIAL_NO_DEVICE_INITED, ERROR_SERIAL_NO_DEVICE,
+
+ STATUS_SHARED_IRQ_BUSY, ERROR_IRQ_BUSY,
+
+ STATUS_SERIAL_MORE_WRITES, ERROR_MORE_WRITES,
+
+ STATUS_SERIAL_COUNTER_TIMEOUT, ERROR_COUNTER_TIMEOUT,
+
+ STATUS_FLOPPY_ID_MARK_NOT_FOUND, ERROR_FLOPPY_ID_MARK_NOT_FOUND,
+
+ STATUS_FLOPPY_WRONG_CYLINDER, ERROR_FLOPPY_WRONG_CYLINDER,
+
+ STATUS_FLOPPY_UNKNOWN_ERROR, ERROR_FLOPPY_UNKNOWN_ERROR,
+
+ STATUS_FLOPPY_BAD_REGISTERS, ERROR_FLOPPY_BAD_REGISTERS,
+
+ STATUS_DISK_RECALIBRATE_FAILED, ERROR_DISK_RECALIBRATE_FAILED,
+
+ STATUS_DISK_OPERATION_FAILED, ERROR_DISK_OPERATION_FAILED,
+
+ STATUS_DISK_RESET_FAILED, ERROR_DISK_RESET_FAILED,
+
+ STATUS_EVENTLOG_FILE_CORRUPT, ERROR_EVENTLOG_FILE_CORRUPT,
+
+ STATUS_EVENTLOG_CANT_START, ERROR_EVENTLOG_CANT_START,
+
+ STATUS_NETLOGON_NOT_STARTED, ERROR_NETLOGON_NOT_STARTED,
+
+ STATUS_ACCOUNT_EXPIRED, ERROR_ACCOUNT_EXPIRED,
+
+ STATUS_NETWORK_CREDENTIAL_CONFLICT, ERROR_SESSION_CREDENTIAL_CONFLICT,
+
+ STATUS_REMOTE_SESSION_LIMIT, ERROR_REMOTE_SESSION_LIMIT_EXCEEDED,
+
+ STATUS_INVALID_BUFFER_SIZE, ERROR_INVALID_USER_BUFFER,
+
+ STATUS_INVALID_ADDRESS_COMPONENT, ERROR_INVALID_NETNAME,
+ STATUS_INVALID_ADDRESS_WILDCARD, ERROR_INVALID_NETNAME,
+
+ STATUS_ADDRESS_ALREADY_EXISTS, ERROR_DUP_NAME,
+
+ STATUS_ADDRESS_CLOSED, ERROR_NETNAME_DELETED,
+ STATUS_CONNECTION_DISCONNECTED, ERROR_NETNAME_DELETED,
+
+ STATUS_CONNECTION_RESET, ERROR_NETNAME_DELETED,
+
+ STATUS_TRANSACTION_ABORTED, ERROR_UNEXP_NET_ERR,
+ STATUS_TRANSACTION_TIMED_OUT, ERROR_UNEXP_NET_ERR,
+ STATUS_TRANSACTION_NO_RELEASE, ERROR_UNEXP_NET_ERR,
+ STATUS_TRANSACTION_NO_MATCH, ERROR_UNEXP_NET_ERR,
+ STATUS_TRANSACTION_RESPONDED, ERROR_UNEXP_NET_ERR,
+ STATUS_TRANSACTION_INVALID_ID, ERROR_UNEXP_NET_ERR,
+ STATUS_TRANSACTION_INVALID_TYPE, ERROR_UNEXP_NET_ERR,
+
+ STATUS_NOT_SERVER_SESSION, ERROR_NOT_SUPPORTED,
+ STATUS_NOT_CLIENT_SESSION, ERROR_NOT_SUPPORTED,
+
+ STATUS_USER_MAPPED_FILE, ERROR_USER_MAPPED_FILE,
+
+ STATUS_PLUGPLAY_NO_DEVICE, ERROR_SERVICE_DISABLED,
+
+ RPC_NT_SERVER_UNAVAILABLE, RPC_S_SERVER_UNAVAILABLE,
+
+ RPC_NT_INVALID_STRING_BINDING, RPC_S_INVALID_STRING_BINDING,
+
+ RPC_NT_WRONG_KIND_OF_BINDING, RPC_S_WRONG_KIND_OF_BINDING,
+
+ RPC_NT_PROTSEQ_NOT_SUPPORTED, RPC_S_PROTSEQ_NOT_SUPPORTED,
+
+ RPC_NT_INVALID_RPC_PROTSEQ, RPC_S_INVALID_RPC_PROTSEQ,
+
+ RPC_NT_INVALID_STRING_UUID, RPC_S_INVALID_STRING_UUID,
+
+ RPC_NT_INVALID_ENDPOINT_FORMAT, RPC_S_INVALID_ENDPOINT_FORMAT,
+
+ RPC_NT_INVALID_NET_ADDR, RPC_S_INVALID_NET_ADDR,
+
+ RPC_NT_NO_ENDPOINT_FOUND, RPC_S_NO_ENDPOINT_FOUND,
+
+ RPC_NT_INVALID_TIMEOUT, RPC_S_INVALID_TIMEOUT,
+
+ RPC_NT_OBJECT_NOT_FOUND, RPC_S_OBJECT_NOT_FOUND,
+
+ RPC_NT_ALREADY_REGISTERED, RPC_S_ALREADY_REGISTERED,
+
+ RPC_NT_TYPE_ALREADY_REGISTERED, RPC_S_TYPE_ALREADY_REGISTERED,
+
+ RPC_NT_ALREADY_LISTENING, RPC_S_ALREADY_LISTENING,
+
+ RPC_NT_NO_PROTSEQS_REGISTERED, RPC_S_NO_PROTSEQS_REGISTERED,
+
+ RPC_NT_NOT_LISTENING, RPC_S_NOT_LISTENING,
+
+ RPC_NT_UNKNOWN_MGR_TYPE, RPC_S_UNKNOWN_MGR_TYPE,
+
+ RPC_NT_UNKNOWN_IF, RPC_S_UNKNOWN_IF,
+
+ RPC_NT_NO_BINDINGS, RPC_S_NO_BINDINGS,
+
+ RPC_NT_NO_MORE_BINDINGS, RPC_S_NO_MORE_BINDINGS,
+
+ RPC_NT_NO_PROTSEQS, RPC_S_NO_PROTSEQS,
+
+ RPC_NT_CANT_CREATE_ENDPOINT, RPC_S_CANT_CREATE_ENDPOINT,
+
+ RPC_NT_OUT_OF_RESOURCES, RPC_S_OUT_OF_RESOURCES,
+
+ RPC_NT_SERVER_TOO_BUSY, RPC_S_SERVER_TOO_BUSY,
+
+ RPC_NT_INVALID_NETWORK_OPTIONS, RPC_S_INVALID_NETWORK_OPTIONS,
+
+ RPC_NT_NO_CALL_ACTIVE, RPC_S_NO_CALL_ACTIVE,
+
+ RPC_NT_CALL_FAILED, RPC_S_CALL_FAILED,
+
+ RPC_NT_CALL_FAILED_DNE, RPC_S_CALL_FAILED_DNE,
+
+ RPC_NT_PROTOCOL_ERROR, RPC_S_PROTOCOL_ERROR,
+
+ RPC_NT_UNSUPPORTED_TRANS_SYN, RPC_S_UNSUPPORTED_TRANS_SYN,
+
+ RPC_NT_UNSUPPORTED_TYPE, RPC_S_UNSUPPORTED_TYPE,
+
+ RPC_NT_INVALID_TAG, RPC_S_INVALID_TAG,
+
+ RPC_NT_INVALID_BOUND, RPC_S_INVALID_BOUND,
+
+ RPC_NT_NO_ENTRY_NAME, RPC_S_NO_ENTRY_NAME,
+
+ RPC_NT_INVALID_NAME_SYNTAX, RPC_S_INVALID_NAME_SYNTAX,
+
+ RPC_NT_UNSUPPORTED_NAME_SYNTAX, RPC_S_UNSUPPORTED_NAME_SYNTAX,
+
+ RPC_NT_UUID_NO_ADDRESS, RPC_S_UUID_NO_ADDRESS,
+
+ RPC_NT_DUPLICATE_ENDPOINT, RPC_S_DUPLICATE_ENDPOINT,
+
+ RPC_NT_UNKNOWN_AUTHN_TYPE, RPC_S_UNKNOWN_AUTHN_TYPE,
+
+ RPC_NT_MAX_CALLS_TOO_SMALL, RPC_S_MAX_CALLS_TOO_SMALL,
+
+ RPC_NT_STRING_TOO_LONG, RPC_S_STRING_TOO_LONG,
+
+ RPC_NT_PROTSEQ_NOT_FOUND, RPC_S_PROTSEQ_NOT_FOUND,
+
+ RPC_NT_PROCNUM_OUT_OF_RANGE, RPC_S_PROCNUM_OUT_OF_RANGE,
+
+ RPC_NT_BINDING_HAS_NO_AUTH, RPC_S_BINDING_HAS_NO_AUTH,
+
+ RPC_NT_UNKNOWN_AUTHN_SERVICE, RPC_S_UNKNOWN_AUTHN_SERVICE,
+
+ RPC_NT_UNKNOWN_AUTHN_LEVEL, RPC_S_UNKNOWN_AUTHN_LEVEL,
+
+ RPC_NT_INVALID_AUTH_IDENTITY, RPC_S_INVALID_AUTH_IDENTITY,
+
+ RPC_NT_UNKNOWN_AUTHZ_SERVICE, RPC_S_UNKNOWN_AUTHZ_SERVICE,
+
+ EPT_NT_INVALID_ENTRY, EPT_S_INVALID_ENTRY,
+
+ EPT_NT_CANT_PERFORM_OP, EPT_S_CANT_PERFORM_OP,
+
+ EPT_NT_NOT_REGISTERED, EPT_S_NOT_REGISTERED,
+
+ RPC_NT_NOTHING_TO_EXPORT, RPC_S_NOTHING_TO_EXPORT,
+
+ RPC_NT_INCOMPLETE_NAME, RPC_S_INCOMPLETE_NAME,
+
+ RPC_NT_INVALID_VERS_OPTION, RPC_S_INVALID_VERS_OPTION,
+
+ RPC_NT_NO_MORE_MEMBERS, RPC_S_NO_MORE_MEMBERS,
+
+ RPC_NT_NOT_ALL_OBJS_UNEXPORTED, RPC_S_NOT_ALL_OBJS_UNEXPORTED,
+
+ RPC_NT_INTERFACE_NOT_FOUND, RPC_S_INTERFACE_NOT_FOUND,
+
+ RPC_NT_ENTRY_ALREADY_EXISTS, RPC_S_ENTRY_ALREADY_EXISTS,
+
+ RPC_NT_ENTRY_NOT_FOUND, RPC_S_ENTRY_NOT_FOUND,
+
+ RPC_NT_NAME_SERVICE_UNAVAILABLE, RPC_S_NAME_SERVICE_UNAVAILABLE,
+
+ RPC_NT_INVALID_NAF_ID, RPC_S_INVALID_NAF_ID,
+
+ RPC_NT_CANNOT_SUPPORT, RPC_S_CANNOT_SUPPORT,
+
+ RPC_NT_NO_CONTEXT_AVAILABLE, RPC_S_NO_CONTEXT_AVAILABLE,
+
+ RPC_NT_INTERNAL_ERROR, RPC_S_INTERNAL_ERROR,
+
+ RPC_NT_ZERO_DIVIDE, RPC_S_ZERO_DIVIDE,
+
+ RPC_NT_ADDRESS_ERROR, RPC_S_ADDRESS_ERROR,
+
+ RPC_NT_FP_DIV_ZERO, RPC_S_FP_DIV_ZERO,
+
+ RPC_NT_FP_UNDERFLOW, RPC_S_FP_UNDERFLOW,
+
+ RPC_NT_FP_OVERFLOW, RPC_S_FP_OVERFLOW,
+
+ RPC_NT_NO_MORE_ENTRIES, RPC_X_NO_MORE_ENTRIES,
+
+ RPC_NT_SS_CHAR_TRANS_OPEN_FAIL, RPC_X_SS_CHAR_TRANS_OPEN_FAIL,
+
+ RPC_NT_SS_CHAR_TRANS_SHORT_FILE, RPC_X_SS_CHAR_TRANS_SHORT_FILE,
+
+ RPC_NT_SS_CONTEXT_MISMATCH, ERROR_INVALID_HANDLE,
+
+ RPC_NT_SS_CONTEXT_DAMAGED, RPC_X_SS_CONTEXT_DAMAGED,
+
+ RPC_NT_SS_HANDLES_MISMATCH, RPC_X_SS_HANDLES_MISMATCH,
+
+ RPC_NT_SS_CANNOT_GET_CALL_HANDLE, RPC_X_SS_CANNOT_GET_CALL_HANDLE,
+
+ RPC_NT_NULL_REF_POINTER, RPC_X_NULL_REF_POINTER,
+
+ RPC_NT_ENUM_VALUE_OUT_OF_RANGE, RPC_X_ENUM_VALUE_OUT_OF_RANGE,
+
+ RPC_NT_BYTE_COUNT_TOO_SMALL, RPC_X_BYTE_COUNT_TOO_SMALL,
+
+ RPC_NT_BAD_STUB_DATA, RPC_X_BAD_STUB_DATA,
+
+ RPC_NT_INVALID_OBJECT, RPC_S_INVALID_OBJECT,
+
+ STATUS_NO_TRUST_LSA_SECRET, ERROR_NO_TRUST_LSA_SECRET,
+
+ STATUS_NO_TRUST_SAM_ACCOUNT, ERROR_NO_TRUST_SAM_ACCOUNT,
+
+ STATUS_TRUSTED_DOMAIN_FAILURE, ERROR_TRUSTED_DOMAIN_FAILURE,
+
+ STATUS_TRUSTED_RELATIONSHIP_FAILURE, ERROR_TRUSTED_RELATIONSHIP_FAILURE,
+
+ STATUS_TRUST_FAILURE, ERROR_TRUST_FAILURE,
+
+ RPC_NT_CALL_IN_PROGRESS, RPC_S_CALL_IN_PROGRESS,
+
+ STATUS_LOG_FILE_FULL, ERROR_LOG_FILE_FULL,
+
+ STATUS_EVENTLOG_FILE_CHANGED, ERROR_EVENTLOG_FILE_CHANGED,
+
+ STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT, ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT,
+
+ STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT, ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT,
+
+ STATUS_NOLOGON_SERVER_TRUST_ACCOUNT, ERROR_NOLOGON_SERVER_TRUST_ACCOUNT,
+
+ STATUS_DOMAIN_TRUST_INCONSISTENT, ERROR_DOMAIN_TRUST_INCONSISTENT,
+
+ STATUS_NO_USER_SESSION_KEY, ERROR_NO_USER_SESSION_KEY,
+
+ STATUS_POSSIBLE_DEADLOCK, ERROR_POSSIBLE_DEADLOCK,
+
+ STATUS_IMAGE_ALREADY_LOADED, ERROR_SERVICE_ALREADY_RUNNING,
+
+ RPC_NT_GROUP_MEMBER_NOT_FOUND, RPC_S_GROUP_MEMBER_NOT_FOUND,
+
+ RPC_NT_NO_INTERFACES, RPC_S_NO_INTERFACES,
+
+ RPC_NT_CALL_CANCELLED, RPC_S_CALL_CANCELLED,
+
+ RPC_NT_BINDING_INCOMPLETE, RPC_S_BINDING_INCOMPLETE,
+
+ RPC_NT_COMM_FAILURE, RPC_S_COMM_FAILURE,
+
+ RPC_NT_UNSUPPORTED_AUTHN_LEVEL, RPC_S_UNSUPPORTED_AUTHN_LEVEL,
+
+ RPC_NT_NO_PRINC_NAME, RPC_S_NO_PRINC_NAME,
+
+ RPC_NT_NOT_RPC_ERROR, RPC_S_NOT_RPC_ERROR,
+
+ RPC_NT_UUID_LOCAL_ONLY, RPC_S_UUID_LOCAL_ONLY,
+
+ RPC_NT_SEC_PKG_ERROR, RPC_S_SEC_PKG_ERROR,
+
+ RPC_NT_NOT_CANCELLED, RPC_S_NOT_CANCELLED,
+
+ RPC_NT_INVALID_ES_ACTION, RPC_X_INVALID_ES_ACTION,
+ RPC_NT_WRONG_ES_VERSION, RPC_X_WRONG_ES_VERSION,
+ RPC_NT_WRONG_STUB_VERSION, RPC_X_WRONG_STUB_VERSION,
+
+ RPC_NT_INVALID_PIPE_OBJECT, RPC_X_INVALID_PIPE_OBJECT,
+ RPC_NT_INVALID_PIPE_OPERATION, RPC_X_INVALID_PIPE_OPERATION,
+ RPC_NT_WRONG_PIPE_VERSION, RPC_X_WRONG_PIPE_VERSION,
+
+ EPT_NT_CANT_CREATE, EPT_S_CANT_CREATE,
+
+ RPC_NT_SEND_INCOMPLETE, RPC_S_SEND_INCOMPLETE,
+
+ STATUS_NO_BROWSER_SERVERS_FOUND, ERROR_NO_BROWSER_SERVERS_FOUND,
+
+ STATUS_MAPPED_ALIGNMENT, ERROR_MAPPED_ALIGNMENT,
+
+ STATUS_CONNECTION_IN_USE, ERROR_DEVICE_IN_USE,
+
+ STATUS_VERIFY_REQUIRED, ERROR_MEDIA_CHANGED,
+
+ STATUS_ALREADY_DISCONNECTED, ERROR_ACTIVE_CONNECTIONS,
+
+ STATUS_CONNECTION_REFUSED, ERROR_CONNECTION_REFUSED,
+
+ STATUS_GRACEFUL_DISCONNECT, ERROR_GRACEFUL_DISCONNECT,
+
+ STATUS_ADDRESS_ALREADY_ASSOCIATED, ERROR_ADDRESS_ALREADY_ASSOCIATED,
+
+ STATUS_ADDRESS_NOT_ASSOCIATED, ERROR_ADDRESS_NOT_ASSOCIATED,
+
+ STATUS_CONNECTION_INVALID, ERROR_CONNECTION_INVALID,
+
+ STATUS_CONNECTION_ACTIVE, ERROR_CONNECTION_ACTIVE,
+
+ STATUS_NETWORK_UNREACHABLE, ERROR_NETWORK_UNREACHABLE,
+
+ STATUS_HOST_UNREACHABLE, ERROR_HOST_UNREACHABLE,
+
+ STATUS_PROTOCOL_UNREACHABLE, ERROR_PROTOCOL_UNREACHABLE,
+
+ STATUS_PORT_UNREACHABLE, ERROR_PORT_UNREACHABLE,
+
+ STATUS_REQUEST_ABORTED, ERROR_REQUEST_ABORTED,
+
+ STATUS_CONNECTION_ABORTED, ERROR_CONNECTION_ABORTED,
+
+ STATUS_CONNECTION_COUNT_LIMIT, ERROR_CONNECTION_COUNT_LIMIT,
+
+ STATUS_PATH_NOT_COVERED, ERROR_HOST_UNREACHABLE,
+
+ STATUS_LOGIN_TIME_RESTRICTION, ERROR_LOGIN_TIME_RESTRICTION,
+ STATUS_LOGIN_WKSTA_RESTRICTION, ERROR_LOGIN_WKSTA_RESTRICTION,
+ STATUS_LICENSE_QUOTA_EXCEEDED, ERROR_LICENSE_QUOTA_EXCEEDED,
+
+ STATUS_RESOURCE_NOT_OWNED, ERROR_NOT_OWNER,
+
+ STATUS_DUPLICATE_OBJECTID, STATUS_DUPLICATE_OBJECTID,
+ STATUS_OBJECTID_EXISTS, STATUS_OBJECTID_EXISTS,
+ SEC_E_INSUFFICIENT_MEMORY, ERROR_NO_SYSTEM_RESOURCES,
+ SEC_E_INVALID_HANDLE, ERROR_INVALID_HANDLE,
+ SEC_E_UNSUPPORTED_FUNCTION, ERROR_INVALID_FUNCTION,
+ SEC_E_TARGET_UNKNOWN, ERROR_BAD_NETPATH,
+ SEC_E_INTERNAL_ERROR, ERROR_INTERNAL_ERROR,
+ SEC_E_SECPKG_NOT_FOUND, ERROR_NO_SUCH_PACKAGE,
+ SEC_E_NOT_OWNER, ERROR_NOT_OWNER,
+ SEC_E_CANNOT_INSTALL, ERROR_NO_SUCH_PACKAGE,
+ SEC_E_INVALID_TOKEN, ERROR_INVALID_PARAMETER,
+ SEC_E_CANNOT_PACK, ERROR_INVALID_PARAMETER,
+ SEC_E_QOP_NOT_SUPPORTED, ERROR_NOT_SUPPORTED,
+ SEC_E_NO_IMPERSONATION, ERROR_CANNOT_IMPERSONATE,
+ SEC_E_LOGON_DENIED, ERROR_LOGON_FAILURE,
+ SEC_E_UNKNOWN_CREDENTIALS, ERROR_INVALID_PARAMETER,
+ SEC_E_NO_CREDENTIALS, ERROR_NO_SUCH_LOGON_SESSION,
+ SEC_E_MESSAGE_ALTERED, ERROR_ACCESS_DENIED,
+ SEC_E_OUT_OF_SEQUENCE, ERROR_ACCESS_DENIED,
+ SEC_E_NO_AUTHENTICATING_AUTHORITY, ERROR_NO_LOGON_SERVERS,
+ SEC_E_BAD_PKGID, ERROR_NO_SUCH_PACKAGE,
+ 0xffffffff, 0
+};
+
+//
+// Define run table entry structure.
+//
+
+typedef struct _RUN_ENTRY {
+ ULONG BaseCode;
+ USHORT RunLength;
+ USHORT CodeSize;
+} RUN_ENTRY, *PRUN_ENTRY;
+
+//
+// Define forward referenced procedure prptotypes.
+//
+
+ULONG
+ComputeCodeSize (
+ IN ULONG Start,
+ IN ULONG Length
+ );
+
+ULONG
+ComputeRunLength (
+ IN ULONG Start
+ );
+
+
+//
+// This program generates a header file that is included by the error
+// translation module in ntos/rtl.
+//
+
+VOID
+_cdecl
+main (argc, argv)
+ int argc;
+ char *argv[];
+
+{
+
+ ULONG Count;
+ ULONG Index1;
+ ULONG Index2;
+ ULONG Length;
+ FILE *OutFile;
+ PCHAR OutName;
+ RUN_ENTRY RunTable[sizeof(CodePairs) / 8];
+ ULONG Size;
+ ULONG Temp;
+
+ //
+ // Create file for output.
+ //
+
+ if (argc == 2) {
+ OutName = argv[1];
+
+ } else {
+ OutName = "\\nt\\private\\ntos\\rtl\\error.h";
+ }
+
+ OutFile = fopen(OutName, "w");
+ if (OutFile == NULL) {
+ fprintf(stderr, "GENERR: Cannot open %s for writing.\n", OutName);
+ perror("GENERR");
+ exit(1);
+ }
+
+ fprintf(stderr, "GENERR: Writing %s header file.\n", OutName );
+
+ //
+ // Sort the code translation table.
+ //
+
+ for (Index1 = 0; Index1 < (sizeof(CodePairs) / 4); Index1 += 2) {
+ for (Index2 = Index1; Index2 < (sizeof(CodePairs) / 4); Index2 += 2) {
+ if ((ULONG)CodePairs[Index2] < (ULONG)CodePairs[Index1]) {
+ Temp = CodePairs[Index1];
+ CodePairs[Index1] = CodePairs[Index2];
+ CodePairs[Index2] = Temp;
+ Temp = CodePairs[Index1 + 1];
+ CodePairs[Index1 + 1] = CodePairs[Index2 + 1];
+ CodePairs[Index2 + 1] = Temp;
+ }
+ }
+ }
+
+ //
+ // Output the initial structure definitions and the translation
+ // table declaration.
+ //
+
+ fprintf(OutFile, "//\n");
+ fprintf(OutFile, "// Define run length table entry structure type.\n");
+ fprintf(OutFile, "//\n");
+ fprintf(OutFile, "\n");
+ fprintf(OutFile, "typedef struct _RUN_ENTRY {\n");
+ fprintf(OutFile, " ULONG BaseCode;\n");
+ fprintf(OutFile, " USHORT RunLength;\n");
+ fprintf(OutFile, " USHORT CodeSize;\n");
+ fprintf(OutFile, "} RUN_ENTRY, *PRUN_ENTRY;\n");
+
+ fprintf(OutFile, "\n");
+ fprintf(OutFile, "//\n");
+ fprintf(OutFile, "// Declare translation table array.\n");
+ fprintf(OutFile, "//\n");
+ fprintf(OutFile, "\n");
+ fprintf(OutFile, "USHORT RtlpStatusTable[] = {");
+ fprintf(OutFile, "\n ");
+
+ //
+ // Calculate the run length entries and output the translation table
+ // entries.
+ //
+
+ Count = 0;
+ Index1 = 0;
+ Index2 = 0;
+ do {
+ Length = ComputeRunLength(Index1);
+ Size = ComputeCodeSize(Index1, Length);
+ RunTable[Index2].BaseCode = CodePairs[Index1];
+ RunTable[Index2].RunLength = (USHORT)Length;
+ RunTable[Index2].CodeSize = (USHORT)Size;
+ Index2 += 1;
+ do {
+ if (Size == 1) {
+ Count += 1;
+ fprintf(OutFile,
+ "0x%04lx, ",
+ CodePairs[Index1 + 1]);
+
+ } else {
+ Count += 2;
+ fprintf(OutFile,
+ "0x%04lx, 0x%04lx, ",
+ CodePairs[Index1 + 1] & 0xffff,
+ (ULONG)CodePairs[Index1 + 1] >> 16);
+ }
+
+ if (Count > 6) {
+ Count = 0;
+ fprintf(OutFile, "\n ");
+ }
+
+ Index1 += 2;
+ Length -= 1;
+ } while (Length > 0);
+ } while (Index1 < (sizeof(CodePairs) / 4));
+
+ fprintf(OutFile, "0x0};\n");
+
+ //
+ // Output the run length table declaration.
+ //
+
+ fprintf(OutFile, "\n");
+ fprintf(OutFile, "//\n");
+ fprintf(OutFile, "// Declare run length table array.\n");
+ fprintf(OutFile, "//\n");
+ fprintf(OutFile, "\n");
+ fprintf(OutFile, "RUN_ENTRY RtlpRunTable[] = {");
+ fprintf(OutFile, "\n");
+
+ //
+ // Output the run length table entires.
+ //
+
+ for (Index1 = 0; Index1 < Index2; Index1 += 1) {
+ fprintf(OutFile,
+ " {0x%08lx, 0x%04lx, 0x%04lx},\n",
+ RunTable[Index1].BaseCode,
+ RunTable[Index1].RunLength,
+ RunTable[Index1].CodeSize);
+ }
+
+ fprintf(OutFile, " {0x0, 0x0, 0x0}};\n");
+
+ //
+ // Close output file.
+ //
+
+ fclose(OutFile);
+ return;
+}
+
+ULONG
+ComputeCodeSize (
+ IN ULONG Start,
+ IN ULONG Length
+ )
+
+//
+// This function computes the size of the code entries required for the
+// specified run and returns the length in words.
+//
+
+{
+
+ ULONG Index;
+
+ for (Index = Start; Index < (Start + (Length * 2)); Index += 2) {
+ if (((ULONG)CodePairs[Index + 1] >> 16) != 0) {
+ return 2;
+ }
+ }
+
+ return 1;
+}
+
+ULONG
+ComputeRunLength (
+ IN ULONG Start
+ )
+
+//
+// This function locates the next set of monotonically increasing status
+// codes values and returns the length of the run.
+//
+
+{
+
+ ULONG Index;
+ ULONG Length;
+
+ Length = 1;
+ for (Index = Start + 2; Index < (sizeof(CodePairs) / 4); Index += 2) {
+ if ((ULONG)CodePairs[Index] != ((ULONG)CodePairs[Index - 2] + 1)) {
+ break;
+ }
+
+ Length += 1;
+ }
+
+ return Length;
+}
diff --git a/private/ntos/rtl/gentable.c b/private/ntos/rtl/gentable.c
new file mode 100644
index 000000000..69c63ddd2
--- /dev/null
+++ b/private/ntos/rtl/gentable.c
@@ -0,0 +1,1098 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ Gentable.c
+
+Abstract:
+
+ This module implements the generic table package.
+
+Author:
+
+ Gary Kimura [GaryKi] 23-May-1989
+
+Environment:
+
+ Pure Utility Routines
+
+Revision History:
+
+ Anthony V. Ercolano [tonye] 23-May-1990
+
+ Implement package.
+
+ Anthony V. Ercolano [tonye] 1-Jun-1990
+
+ Added ability to get elements out in the order
+ inserted. *NOTE* *NOTE* This depends on the implicit
+ ordering of record fields:
+
+ SPLAY_LINKS,
+ LIST_ENTRY,
+ USER_DATA
+
+--*/
+
+#include <nt.h>
+
+#include <ntrtl.h>
+
+
+//
+// This enumerated type is used as the function return
+// value of the function that is used to search the tree
+// for a key. FoundNode indicates that the function found
+// the key. InsertAsLeft indicates that the key was not found
+// and the node should be inserted as the left child of the
+// parent. InsertAsRight indicates that the key was not found
+// and the node should be inserted as the right child of the
+// parent.
+//
+typedef enum _SEARCH_RESULT{
+ EmptyTree,
+ FoundNode,
+ InsertAsLeft,
+ InsertAsRight
+} SEARCH_RESULT;
+
+
+static
+SEARCH_RESULT
+FindNodeOrParent(
+ IN PRTL_GENERIC_TABLE Table,
+ IN PVOID Buffer,
+ OUT PRTL_SPLAY_LINKS *NodeOrParent
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used by all of the routines of the generic
+ table package to locate the a node in the tree. It will
+ find and return (via the NodeOrParent parameter) the node
+ with the given key, or if that node is not in the tree it
+ will return (via the NodeOrParent parameter) a pointer to
+ the parent.
+
+Arguments:
+
+ Table - The generic table to search for the key.
+
+ Buffer - Pointer to a buffer holding the key. The table
+ package doesn't examine the key itself. It leaves
+ this up to the user supplied compare routine.
+
+ NodeOrParent - Will be set to point to the node containing the
+ the key or what should be the parent of the node
+ if it were in the tree. Note that this will *NOT*
+ be set if the search result is EmptyTree.
+
+Return Value:
+
+ SEARCH_RESULT - EmptyTree: The tree was empty. NodeOrParent
+ is *not* altered.
+
+ FoundNode: A node with the key is in the tree.
+ NodeOrParent points to that node.
+
+ InsertAsLeft: Node with key was not found.
+ NodeOrParent points to what would be
+ parent. The node would be the left
+ child.
+
+ InsertAsRight: Node with key was not found.
+ NodeOrParent points to what would be
+ parent. The node would be the right
+ child.
+
+--*/
+
+
+
+{
+
+ if (RtlIsGenericTableEmpty(Table)) {
+
+ return EmptyTree;
+
+ } else {
+
+ //
+ // Used as the iteration variable while stepping through
+ // the generic table.
+ //
+ PRTL_SPLAY_LINKS NodeToExamine = Table->TableRoot;
+
+ //
+ // Just a temporary. Hopefully a good compiler will get
+ // rid of it.
+ //
+ PRTL_SPLAY_LINKS Child;
+
+ //
+ // Holds the value of the comparasion.
+ //
+ RTL_GENERIC_COMPARE_RESULTS Result;
+
+ while (TRUE) {
+
+ //
+ // Compare the buffer with the key in the tree element.
+ //
+
+ Result = Table->CompareRoutine(
+ Table,
+ Buffer,
+ ((PLIST_ENTRY)((PVOID)(NodeToExamine+1)))+1
+ );
+
+ if (Result == GenericLessThan) {
+
+ if (Child = RtlLeftChild(NodeToExamine)) {
+
+ NodeToExamine = Child;
+
+ } else {
+
+ //
+ // Node is not in the tree. Set the output
+ // parameter to point to what would be its
+ // parent and return which child it would be.
+ //
+
+ *NodeOrParent = NodeToExamine;
+ return InsertAsLeft;
+
+ }
+
+ } else if (Result == GenericGreaterThan) {
+
+ if (Child = RtlRightChild(NodeToExamine)) {
+
+ NodeToExamine = Child;
+
+ } else {
+
+ //
+ // Node is not in the tree. Set the output
+ // parameter to point to what would be its
+ // parent and return which child it would be.
+ //
+
+ *NodeOrParent = NodeToExamine;
+ return InsertAsRight;
+
+ }
+
+
+ } else {
+
+ //
+ // Node is in the tree (or it better be because of the
+ // assert). Set the output parameter to point to
+ // the node and tell the caller that we found the node.
+ //
+
+ ASSERT(Result == GenericEqual);
+ *NodeOrParent = NodeToExamine;
+ return FoundNode;
+
+ }
+
+ }
+
+ }
+
+}
+
+VOID
+RtlInitializeGenericTable (
+ IN PRTL_GENERIC_TABLE Table,
+ IN PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine,
+ IN PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine,
+ IN PRTL_GENERIC_FREE_ROUTINE FreeRoutine,
+ IN PVOID TableContext
+ )
+
+/*++
+
+Routine Description:
+
+ The procedure InitializeGenericTable takes as input an uninitialized
+ generic table variable and pointers to the three user supplied routines.
+ This must be called for every individual generic table variable before
+ it can be used.
+
+Arguments:
+
+ Table - Pointer to the generic table to be initialized.
+
+ CompareRoutine - User routine to be used to compare to keys in the
+ table.
+
+ AllocateRoutine - User routine to call to allocate memory for a new
+ node in the generic table.
+
+ FreeRoutine - User routine to call to deallocate memory for
+ a node in the generic table.
+
+ TableContext - Supplies user supplied context for the table.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ //
+ // Initialize each field of the Table parameter.
+ //
+
+ Table->TableRoot = NULL;
+ InitializeListHead(&Table->InsertOrderList);
+ Table->NumberGenericTableElements = 0;
+ Table->OrderedPointer = &Table->InsertOrderList;
+ Table->WhichOrderedElement = 0;
+ Table->CompareRoutine = CompareRoutine;
+ Table->AllocateRoutine = AllocateRoutine;
+ Table->FreeRoutine = FreeRoutine;
+ Table->TableContext = TableContext;
+
+}
+
+
+PVOID
+RtlInsertElementGenericTable (
+ IN PRTL_GENERIC_TABLE Table,
+ IN PVOID Buffer,
+ IN CLONG BufferSize,
+ OUT PBOOLEAN NewElement OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The function InsertElementGenericTable will insert a new element
+ in a table. It does this by allocating space for the new element
+ (this includes splay links), inserting the element in the table, and
+ then returning to the user a pointer to the new element (which is
+ the first available space after the splay links). If an element
+ with the same key already exists in the table the return value is a pointer
+ to the old element. The optional output parameter NewElement is used
+ to indicate if the element previously existed in the table. Note: the user
+ supplied Buffer is only used for searching the table, upon insertion its
+ contents are copied to the newly created element. This means that
+ pointer to the input buffer will not point to the new element.
+
+Arguments:
+
+ Table - Pointer to the table in which to (possibly) insert the
+ key buffer.
+
+ Buffer - Passed to the user comparasion routine. Its contents are
+ up to the user but one could imagine that it contains some
+ sort of key value.
+
+ BufferSize - The amount of space to allocate when the (possible)
+ insertion is made. Note that if we actually do
+ not find the node and we do allocate space then we
+ will add the size of the SPLAY_LINKS to this buffer
+ size. The user should really take care not to depend
+ on anything in the first sizeof(SPLAY_LINKS) bytes
+ of the memory allocated via the memory allocation
+ routine.
+
+ NewElement - Optional Flag. If present then it will be set to
+ TRUE if the buffer was not "found" in the generic
+ table.
+
+Return Value:
+
+ PVOID - Pointer to the user defined data.
+
+--*/
+
+{
+
+ //
+ // Holds a pointer to the node in the table or what would be the
+ // parent of the node.
+ //
+ PRTL_SPLAY_LINKS NodeOrParent;
+
+ //
+ // Holds the result of the table lookup.
+ //
+ SEARCH_RESULT Lookup;
+
+ //
+ // Node will point to the splay links of what
+ // will be returned to the user.
+ //
+ PRTL_SPLAY_LINKS NodeToReturn;
+
+ Lookup = FindNodeOrParent(
+ Table,
+ Buffer,
+ &NodeOrParent
+ );
+
+ if (Lookup != FoundNode) {
+
+ //
+ // We just check that the table isn't getting
+ // too big.
+ //
+
+ ASSERT(Table->NumberGenericTableElements != (MAXULONG-1));
+
+ //
+ // The node wasn't in the (possibly empty) tree.
+ // Call the user allocation routine to get space
+ // for the new node.
+ //
+
+ NodeToReturn = Table->AllocateRoutine(
+ Table,
+ BufferSize+sizeof(RTL_SPLAY_LINKS)+sizeof(LIST_ENTRY)
+ );
+
+ //
+ // If the return is NULL, return NULL from here to indicate that
+ // the entry could not be added.
+ //
+
+ if (NodeToReturn == NULL) {
+
+ if (ARGUMENT_PRESENT(NewElement)) {
+
+ *NewElement = FALSE;
+
+ }
+
+ return(NULL);
+
+ }
+
+ RtlInitializeSplayLinks(NodeToReturn);
+ InitializeListHead((PLIST_ENTRY)((PVOID)(NodeToReturn+1)));
+
+ //
+ // Insert the new node at the end of the ordered linked list.
+ //
+
+ InsertTailList(
+ &Table->InsertOrderList,
+ (PLIST_ENTRY)((PVOID)(NodeToReturn+1))
+ );
+
+ Table->NumberGenericTableElements++;
+
+ //
+ // Insert the new node in the tree.
+ //
+
+ if (Lookup == EmptyTree) {
+
+ Table->TableRoot = NodeToReturn;
+
+ } else {
+
+ if (Lookup == InsertAsLeft) {
+
+ RtlInsertAsLeftChild(
+ NodeOrParent,
+ NodeToReturn
+ );
+
+ } else {
+
+ RtlInsertAsRightChild(
+ NodeOrParent,
+ NodeToReturn
+ );
+
+ }
+
+ }
+
+ //
+ // Copy the users buffer into the user data area of the table.
+ //
+
+ RtlCopyMemory(
+ ((PLIST_ENTRY)((PVOID)(NodeToReturn+1)))+1,
+ Buffer,
+ BufferSize
+ );
+
+ } else {
+
+ NodeToReturn = NodeOrParent;
+
+ }
+
+ //
+ // Always splay the (possibly) new node.
+ //
+
+ Table->TableRoot = RtlSplay(NodeToReturn);
+
+ if (ARGUMENT_PRESENT(NewElement)) {
+
+ *NewElement = ((Lookup == FoundNode)?(FALSE):(TRUE));
+
+ }
+
+ //
+ // Insert the element on the ordered list;
+ //
+
+ return ((PLIST_ENTRY)((PVOID)(NodeToReturn+1)))+1;
+}
+
+
+BOOLEAN
+RtlDeleteElementGenericTable (
+ IN PRTL_GENERIC_TABLE Table,
+ IN PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The function DeleteElementGenericTable will find and delete an element
+ from a generic table. If the element is located and deleted the return
+ value is TRUE, otherwise if the element is not located the return value
+ is FALSE. The user supplied input buffer is only used as a key in
+ locating the element in the table.
+
+Arguments:
+
+ Table - Pointer to the table in which to (possibly) delete the
+ memory accessed by the key buffer.
+
+ Buffer - Passed to the user comparasion routine. Its contents are
+ up to the user but one could imagine that it contains some
+ sort of key value.
+
+Return Value:
+
+ BOOLEAN - If the table contained the key then true, otherwise false.
+
+--*/
+
+{
+
+ //
+ // Holds a pointer to the node in the table or what would be the
+ // parent of the node.
+ //
+ PRTL_SPLAY_LINKS NodeOrParent;
+
+ //
+ // Holds the result of the table lookup.
+ //
+ SEARCH_RESULT Lookup;
+
+ Lookup = FindNodeOrParent(
+ Table,
+ Buffer,
+ &NodeOrParent
+ );
+
+ if ((Lookup == EmptyTree) || (Lookup != FoundNode)) {
+
+ return FALSE;
+
+ } else {
+
+ //
+ // Delete the node from the splay tree.
+ //
+
+ Table->TableRoot = RtlDelete(NodeOrParent);
+
+ //
+ // Delete the element from the linked list.
+ //
+
+ RemoveEntryList((PLIST_ENTRY)((PVOID)(NodeOrParent+1)));
+ Table->NumberGenericTableElements--;
+ Table->WhichOrderedElement = 0;
+ Table->OrderedPointer = &Table->InsertOrderList;
+
+ //
+ // The node has been deleted from the splay table.
+ // Now give the node to the user deletion routine.
+ // NOTE: We are giving the deletion routine a pointer
+ // to the splay links rather then the user data. It
+ // is assumed that the deallocation is rather stupid.
+ //
+
+ Table->FreeRoutine(Table,NodeOrParent);
+ return TRUE;
+
+ }
+
+}
+
+
+PVOID
+RtlLookupElementGenericTable (
+ IN PRTL_GENERIC_TABLE Table,
+ IN PVOID Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ The function LookupElementGenericTable will find an element in a generic
+ table. If the element is located the return value is a pointer to
+ the user defined structure associated with the element, otherwise if
+ the element is not located the return value is NULL. The user supplied
+ input buffer is only used as a key in locating the element in the table.
+
+Arguments:
+
+ Table - Pointer to the users Generic table to search for the key.
+
+ Buffer - Used for the comparasion.
+
+Return Value:
+
+ PVOID - returns a pointer to the user data.
+
+--*/
+
+{
+
+ //
+ // Holds a pointer to the node in the table or what would be the
+ // parent of the node.
+ //
+ PRTL_SPLAY_LINKS NodeOrParent;
+
+ //
+ // Holds the result of the table lookup.
+ //
+ SEARCH_RESULT Lookup;
+
+ Lookup = FindNodeOrParent(
+ Table,
+ Buffer,
+ &NodeOrParent
+ );
+
+ if ((Lookup == EmptyTree) || (Lookup != FoundNode)) {
+
+ return NULL;
+
+ } else {
+
+ //
+ // Splay the tree with this node.
+ //
+
+ Table->TableRoot = RtlSplay(NodeOrParent);
+
+ //
+ // Return a pointer to the user data.
+ //
+
+ return ((PLIST_ENTRY)((PVOID)(NodeOrParent+1)))+1;
+
+ }
+
+}
+
+
+PVOID
+RtlEnumerateGenericTable (
+ IN PRTL_GENERIC_TABLE Table,
+ IN BOOLEAN Restart
+ )
+
+/*++
+
+Routine Description:
+
+ The function EnumerateGenericTable will return to the caller one-by-one
+ the elements of of a table. The return value is a pointer to the user
+ defined structure associated with the element. The input parameter
+ Restart indicates if the enumeration should start from the beginning
+ or should return the next element. If the are no more new elements to
+ return the return value is NULL. As an example of its use, to enumerate
+ all of the elements in a table the user would write:
+
+ for (ptr = EnumerateGenericTable(Table,TRUE);
+ ptr != NULL;
+ ptr = EnumerateGenericTable(Table, FALSE)) {
+ :
+ }
+
+Arguments:
+
+ Table - Pointer to the generic table to enumerate.
+
+ Restart - Flag that if true we should start with the least
+ element in the tree otherwise, return we return
+ a pointer to the user data for the root and make
+ the real successor to the root the new root.
+
+Return Value:
+
+ PVOID - Pointer to the user data.
+
+--*/
+
+{
+
+ if (RtlIsGenericTableEmpty(Table)) {
+
+ //
+ // Nothing to do if the table is empty.
+ //
+
+ return NULL;
+
+ } else {
+
+ //
+ // Will be used as the "iteration" through the tree.
+ //
+ PRTL_SPLAY_LINKS NodeToReturn;
+
+ //
+ // If the restart flag is true then go to the least element
+ // in the tree.
+ //
+
+ if (Restart) {
+
+ //
+ // We just loop until we find the leftmost child of the root.
+ //
+
+ for (
+ NodeToReturn = Table->TableRoot;
+ RtlLeftChild(NodeToReturn);
+ NodeToReturn = RtlLeftChild(NodeToReturn)
+ ) {
+ ;
+ }
+
+ Table->TableRoot = RtlSplay(NodeToReturn);
+
+ } else {
+
+ //
+ // The assumption here is that the root of the
+ // tree is the last node that we returned. We
+ // find the real successor to the root and return
+ // it as next element of the enumeration. The
+ // node that is to be returned is splayed (thereby
+ // making it the root of the tree). Note that we
+ // need to take care when there are no more elements.
+ //
+
+ NodeToReturn = RtlRealSuccessor(Table->TableRoot);
+
+ if (NodeToReturn) {
+
+ Table->TableRoot = RtlSplay(NodeToReturn);
+
+ }
+
+ }
+
+ //
+ // If there actually is a next element in the enumeration
+ // then the pointer to return is right after the list links.
+ //
+
+ return ((NodeToReturn)?
+ ((PVOID)((PLIST_ENTRY)((PVOID)(NodeToReturn+1))+1))
+ :((PVOID)(NULL)));
+
+ }
+
+}
+
+
+BOOLEAN
+RtlIsGenericTableEmpty (
+ IN PRTL_GENERIC_TABLE Table
+ )
+
+/*++
+
+Routine Description:
+
+ The function IsGenericTableEmpty will return to the caller TRUE if
+ the input table is empty (i.e., does not contain any elements) and
+ FALSE otherwise.
+
+Arguments:
+
+ Table - Supplies a pointer to the Generic Table.
+
+Return Value:
+
+ BOOLEAN - if enabled the tree is empty.
+
+--*/
+
+{
+
+ //
+ // Table is empty if the root pointer is null.
+ //
+
+ return ((Table->TableRoot)?(FALSE):(TRUE));
+
+}
+
+PVOID
+RtlGetElementGenericTable (
+ IN PRTL_GENERIC_TABLE Table,
+ IN ULONG I
+ )
+
+/*++
+
+Routine Description:
+
+
+ The function GetElementGenericTable will return the i'th element
+ inserted in the generic table. I = 0 implies the first element,
+ I = (RtlNumberGenericTableElements(Table)-1) will return the last element
+ inserted into the generic table. The type of I is ULONG. Values
+ of I > than (NumberGenericTableElements(Table)-1) will return NULL. If
+ an arbitrary element is deleted from the generic table it will cause
+ all elements inserted after the deleted element to "move up".
+
+Arguments:
+
+ Table - Pointer to the generic table from which to get the ith element.
+
+ I - Which element to get.
+
+
+Return Value:
+
+ PVOID - Pointer to the user data.
+
+--*/
+
+{
+
+ //
+ // Current location in the table.
+ //
+ ULONG CurrentLocation = Table->WhichOrderedElement;
+
+ //
+ // Hold the number of elements in the table.
+ //
+ ULONG NumberInTable = Table->NumberGenericTableElements;
+
+ //
+ // Holds the value of I+1.
+ //
+ // Note that we don't care if this value overflows.
+ // If we end up accessing it we know that it didn't.
+ //
+ ULONG NormalizedI = I + 1;
+
+ //
+ // Will hold distances to travel to the desired node;
+ //
+ ULONG ForwardDistance,BackwardDistance;
+
+ //
+ // Will point to the current element in the linked list.
+ //
+ PLIST_ENTRY CurrentNode = Table->OrderedPointer;
+
+
+ //
+ // If it's out of bounds get out quick.
+ //
+
+ if ((I == MAXULONG) || (NormalizedI > NumberInTable)) return NULL;
+
+ //
+ // If we're already at the node then return it.
+ //
+
+ if (NormalizedI == CurrentLocation) return CurrentNode+1;
+
+ //
+ // Calculate the forward and backward distance to the node.
+ //
+
+ if (CurrentLocation > NormalizedI) {
+
+ //
+ // When CurrentLocation is greater than where we want to go,
+ // if moving forward gets us there quicker than moving backward
+ // then it follows that moving forward from the listhead is
+ // going to take fewer steps. (This is because, moving forward
+ // in this case must move *through* the listhead.)
+ //
+ // The work here is to figure out if moving backward would be quicker.
+ //
+ // Moving backward would be quicker only if the location we wish to
+ // go to is more than half way between the listhead and where we
+ // currently are.
+ //
+
+ if (NormalizedI > (CurrentLocation/2)) {
+
+ //
+ // Where we want to go is more than half way from the listhead
+ // We can traval backwards from our current location.
+ //
+
+ for (
+ BackwardDistance = CurrentLocation - NormalizedI;
+ BackwardDistance;
+ BackwardDistance--
+ ) {
+
+ CurrentNode = CurrentNode->Blink;
+
+ }
+ } else {
+
+ //
+ // Where we want to go is less than halfway between the start
+ // and where we currently are. Start from the listhead.
+ //
+
+ for (
+ CurrentNode = &Table->InsertOrderList;
+ NormalizedI;
+ NormalizedI--
+ ) {
+
+ CurrentNode = CurrentNode->Flink;
+
+ }
+
+ }
+
+ } else {
+
+
+ //
+ // When CurrentLocation is less than where we want to go,
+ // if moving backwards gets us there quicker than moving forwards
+ // then it follows that moving backwards from the listhead is
+ // going to take fewer steps. (This is because, moving backwards
+ // in this case must move *through* the listhead.)
+ //
+
+ ForwardDistance = NormalizedI - CurrentLocation;
+
+ //
+ // Do the backwards calculation as if we are starting from the
+ // listhead.
+ //
+
+ BackwardDistance = (NumberInTable - NormalizedI) + 1;
+
+ if (ForwardDistance <= BackwardDistance) {
+
+ for (
+ ;
+ ForwardDistance;
+ ForwardDistance--
+ ) {
+
+ CurrentNode = CurrentNode->Flink;
+
+ }
+
+
+ } else {
+
+ for (
+ CurrentNode = &Table->InsertOrderList;
+ BackwardDistance;
+ BackwardDistance--
+ ) {
+
+ CurrentNode = CurrentNode->Blink;
+
+ }
+
+ }
+
+ }
+
+ //
+ // We're where we want to be. Save our current location and return
+ // a pointer to the data to the user.
+ //
+
+ Table->OrderedPointer = CurrentNode;
+ Table->WhichOrderedElement = I+1;
+
+ return CurrentNode+1;
+
+}
+
+
+ULONG
+RtlNumberGenericTableElements(
+ IN PRTL_GENERIC_TABLE Table
+ )
+
+/*++
+
+Routine Description:
+
+ The function NumberGenericTableElements returns a ULONG value
+ which is the number of generic table elements currently inserted
+ in the generic table.
+
+Arguments:
+
+ Table - Pointer to the generic table from which to find out the number
+ of elements.
+
+
+Return Value:
+
+ ULONG - The number of elements in the generic table.
+
+--*/
+{
+
+ return Table->NumberGenericTableElements;
+
+}
+
+
+PVOID
+RtlEnumerateGenericTableWithoutSplaying (
+ IN PRTL_GENERIC_TABLE Table,
+ IN PVOID *RestartKey
+ )
+
+/*++
+
+Routine Description:
+
+ The function EnumerateGenericTableWithoutSplaying will return to the
+ caller one-by-one the elements of of a table. The return value is a
+ pointer to the user defined structure associated with the element.
+ The input parameter RestartKey indicates if the enumeration should
+ start from the beginning or should return the next element. If the
+ are no more new elements to return the return value is NULL. As an
+ example of its use, to enumerate all of the elements in a table the
+ user would write:
+
+ *RestartKey = NULL;
+
+ for (ptr = EnumerateGenericTableWithoutSplaying(Table, &RestartKey);
+ ptr != NULL;
+ ptr = EnumerateGenericTableWithoutSplaying(Table, &RestartKey)) {
+ :
+ }
+
+Arguments:
+
+ Table - Pointer to the generic table to enumerate.
+
+ RestartKey - Pointer that indicates if we should restart or return the next
+ element. If the contents of RestartKey is NULL, the search
+ will be started from the beginning.
+
+Return Value:
+
+ PVOID - Pointer to the user data.
+
+--*/
+
+{
+
+ if (RtlIsGenericTableEmpty(Table)) {
+
+ //
+ // Nothing to do if the table is empty.
+ //
+
+ return NULL;
+
+ } else {
+
+ //
+ // Will be used as the "iteration" through the tree.
+ //
+ PRTL_SPLAY_LINKS NodeToReturn;
+
+ //
+ // If the restart flag is true then go to the least element
+ // in the tree.
+ //
+
+ if (*RestartKey == NULL) {
+
+ //
+ // We just loop until we find the leftmost child of the root.
+ //
+
+ for (
+ NodeToReturn = Table->TableRoot;
+ RtlLeftChild(NodeToReturn);
+ NodeToReturn = RtlLeftChild(NodeToReturn)
+ ) {
+ ;
+ }
+
+ *RestartKey = NodeToReturn;
+
+ } else {
+
+ //
+ // The caller has passed in the previous entry found
+ // in the table to enable us to continue the search. We call
+ // RtlRealSuccessor to step to the next element in the tree.
+ //
+
+ NodeToReturn = RtlRealSuccessor(*RestartKey);
+
+ if (NodeToReturn) {
+
+ *RestartKey = NodeToReturn;
+
+ }
+
+ }
+
+ //
+ // If there actually is a next element in the enumeration
+ // then the pointer to return is right after the list links.
+ //
+
+ return ((NodeToReturn)?
+ ((PVOID)((PLIST_ENTRY)((PVOID)(NodeToReturn+1))+1))
+ :((PVOID)(NULL)));
+
+ }
+
+}
+
+
diff --git a/private/ntos/rtl/handle.c b/private/ntos/rtl/handle.c
new file mode 100644
index 000000000..509fb4300
--- /dev/null
+++ b/private/ntos/rtl/handle.c
@@ -0,0 +1,228 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ handle.c
+
+Abstract:
+
+ This module contains a simple handle allocator for use by the Local and
+ Global memory allocators.
+
+Author:
+
+ Steve Wood (stevewo) 25-Jul-1991
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+void
+RtlInitializeHandleTable(
+ IN ULONG MaximumNumberOfHandles,
+ IN ULONG SizeOfHandleTableEntry,
+ OUT PRTL_HANDLE_TABLE HandleTable
+ )
+{
+ RtlZeroMemory( HandleTable, sizeof( *HandleTable ) );
+ HandleTable->MaximumNumberOfHandles = MaximumNumberOfHandles;
+ HandleTable->SizeOfHandleTableEntry = SizeOfHandleTableEntry;
+
+ return;
+}
+
+NTSTATUS
+RtlDestroyHandleTable(
+ IN OUT PRTL_HANDLE_TABLE HandleTable
+ )
+{
+ NTSTATUS Status;
+ PVOID BaseAddress;
+ ULONG ReserveSize;
+
+ BaseAddress = HandleTable->CommittedHandles;
+ ReserveSize = (PUCHAR)(HandleTable->MaxReservedHandles) -
+ (PUCHAR)(HandleTable->CommittedHandles);
+
+ Status = NtFreeVirtualMemory( NtCurrentProcess(),
+ &BaseAddress,
+ &ReserveSize,
+ MEM_RELEASE
+ );
+ return Status;
+}
+
+PRTL_HANDLE_TABLE_ENTRY
+RtlAllocateHandle(
+ IN PRTL_HANDLE_TABLE HandleTable,
+ OUT PULONG HandleIndex OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PVOID BaseAddress;
+ ULONG n, ReserveSize, CommitSize;
+ PRTL_HANDLE_TABLE_ENTRY p, *pp;
+
+ if (HandleTable->FreeHandles == NULL) {
+ try {
+ if (HandleTable->UnCommittedHandles == NULL) {
+ ReserveSize = HandleTable->MaximumNumberOfHandles *
+ HandleTable->SizeOfHandleTableEntry;
+ BaseAddress = NULL;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &BaseAddress,
+ 0,
+ &ReserveSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+
+ if (NT_SUCCESS( Status )) {
+ HandleTable->CommittedHandles = (PRTL_HANDLE_TABLE_ENTRY)BaseAddress;
+ HandleTable->UnCommittedHandles = (PRTL_HANDLE_TABLE_ENTRY)BaseAddress;
+ HandleTable->MaxReservedHandles = (PRTL_HANDLE_TABLE_ENTRY)
+ ((PCHAR)BaseAddress + ReserveSize);
+ }
+ }
+ else {
+ Status = STATUS_SUCCESS;
+ }
+
+
+ if (NT_SUCCESS( Status )) {
+ p = HandleTable->UnCommittedHandles;
+ if (p >= HandleTable->MaxReservedHandles) {
+ Status = STATUS_NO_MEMORY;
+ }
+ else {
+ CommitSize = PAGE_SIZE;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&p,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (NT_SUCCESS( Status )) {
+ HandleTable->UnCommittedHandles = (PRTL_HANDLE_TABLE_ENTRY)
+ ((PCH)p + CommitSize);
+ }
+ }
+ }
+
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ Status = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ pp = &HandleTable->FreeHandles;
+ while (p < HandleTable->UnCommittedHandles) {
+ *pp = p;
+ pp = &p->NextFree;
+ p = (PRTL_HANDLE_TABLE_ENTRY)((PUCHAR)p + HandleTable->SizeOfHandleTableEntry);
+ }
+ }
+
+ //
+ // Remove handle table entry from head of free list.
+ //
+
+ p = HandleTable->FreeHandles;
+ HandleTable->FreeHandles = p->NextFree;
+
+ //
+ // Clear free list link field, which also leaves the handle allocated bit
+ // clear. This allows the caller to mark it is allocated after they are
+ // done filling in their portion.
+ //
+
+ p->NextFree = NULL;
+
+
+ //
+ // If requested, return the index of this handle table entry
+ //
+ if (ARGUMENT_PRESENT( HandleIndex )) {
+ *HandleIndex = ((ULONG)p - (ULONG)HandleTable->CommittedHandles) / HandleTable->SizeOfHandleTableEntry;
+ }
+
+ //
+ // Return a pointer to the handle table entry.
+ //
+
+ return p;
+}
+
+
+BOOLEAN
+RtlFreeHandle(
+ IN PRTL_HANDLE_TABLE HandleTable,
+ IN PRTL_HANDLE_TABLE_ENTRY Handle
+ )
+{
+#if DBG
+ if (!RtlIsValidHandle( HandleTable, Handle )) {
+ DbgPrint( "RTL: RtlFreeHandle( %lx ) - invalid handle\n", Handle );
+ if (NtCurrentPeb()->BeingDebugged) {
+ DbgBreakPoint();
+ }
+ return FALSE;
+ }
+#endif
+
+ RtlZeroMemory( Handle, HandleTable->SizeOfHandleTableEntry );
+ Handle->NextFree = HandleTable->FreeHandles;
+ HandleTable->FreeHandles = Handle;
+ return TRUE;
+}
+
+
+
+BOOLEAN
+RtlIsValidHandle(
+ IN PRTL_HANDLE_TABLE HandleTable,
+ IN PRTL_HANDLE_TABLE_ENTRY Handle
+ )
+{
+ if (Handle == NULL ||
+ Handle < HandleTable->CommittedHandles ||
+ Handle >= HandleTable->UnCommittedHandles ||
+ (ULONG)Handle & (HandleTable->SizeOfHandleTableEntry - 1) ||
+ !(Handle->Flags & RTL_HANDLE_ALLOCATED)
+ ) {
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+RtlIsValidIndexHandle(
+ IN PRTL_HANDLE_TABLE HandleTable,
+ IN ULONG HandleIndex,
+ OUT PRTL_HANDLE_TABLE_ENTRY *Handle
+ )
+{
+ PRTL_HANDLE_TABLE_ENTRY p;
+
+ p = (PRTL_HANDLE_TABLE_ENTRY)
+ ((PCHAR)HandleTable->CommittedHandles + (HandleIndex * HandleTable->SizeOfHandleTableEntry));
+
+ if (RtlIsValidHandle( HandleTable, p )) {
+ *Handle = p;
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
diff --git a/private/ntos/rtl/heap.c b/private/ntos/rtl/heap.c
new file mode 100644
index 000000000..041a2a87c
--- /dev/null
+++ b/private/ntos/rtl/heap.c
@@ -0,0 +1,2772 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ heap.c
+
+Abstract:
+
+ This module implements a heap allocator.
+
+Author:
+
+ Steve Wood (stevewo) 20-Sep-1989 (Adapted from URTL\alloc.c)
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "heap.h"
+#include "heappriv.h"
+
+#if defined(NTOS_KERNEL_RUNTIME)
+#if defined(ALLOC_PRAGMA)
+PHEAP_UNCOMMMTTED_RANGE
+RtlpCreateUnCommittedRange(
+ IN PHEAP_SEGMENT Segment
+ );
+
+VOID
+RtlpDestroyUnCommittedRange(
+ IN PHEAP_SEGMENT Segment,
+ IN PHEAP_UNCOMMMTTED_RANGE UnCommittedRange
+ );
+
+VOID
+RtlpInsertUnCommittedPages(
+ IN PHEAP_SEGMENT Segment,
+ IN ULONG Address,
+ IN ULONG Size
+ );
+
+NTSTATUS
+RtlpDestroyHeapSegment(
+ IN PHEAP_SEGMENT Segment
+ );
+
+PHEAP_FREE_ENTRY
+RtlpExtendHeap(
+ IN PHEAP Heap,
+ IN ULONG AllocationSize
+ );
+
+
+#pragma alloc_text(PAGE, RtlpCreateUnCommittedRange)
+#pragma alloc_text(PAGE, RtlpDestroyUnCommittedRange)
+#pragma alloc_text(PAGE, RtlpInsertUnCommittedPages)
+#pragma alloc_text(PAGE, RtlpFindAndCommitPages)
+#pragma alloc_text(PAGE, RtlpInitializeHeapSegment)
+#pragma alloc_text(PAGE, RtlpDestroyHeapSegment)
+#pragma alloc_text(PAGE, RtlCreateHeap)
+#pragma alloc_text(PAGE, RtlDestroyHeap)
+#pragma alloc_text(PAGE, RtlpExtendHeap)
+#pragma alloc_text(PAGE, RtlpCoalesceFreeBlocks)
+#pragma alloc_text(PAGE, RtlpDeCommitFreeBlock)
+#pragma alloc_text(PAGE, RtlpInsertFreeBlock)
+#pragma alloc_text(PAGE, RtlAllocateHeap)
+#pragma alloc_text(PAGE, RtlFreeHeap)
+#pragma alloc_text(PAGE, RtlpGetSizeOfBigBlock)
+#pragma alloc_text(PAGE, RtlpCheckBusyBlockTail)
+#pragma alloc_text(PAGE, RtlZeroHeap)
+#endif
+
+#else
+
+PVOID
+RtlDebugCreateHeap(
+ IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN ULONG ReserveSize OPTIONAL,
+ IN ULONG CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL
+ );
+
+BOOLEAN
+RtlDebugDestroyHeap(
+ IN PVOID HeapHandle
+ );
+
+PVOID
+RtlDebugAllocateHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ );
+
+BOOLEAN
+RtlDebugFreeHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ );
+
+NTSTATUS
+RtlDebugZeroHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ );
+
+PVOID
+RtlAllocateHeapSlowly(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ );
+
+BOOLEAN
+RtlFreeHeapSlowly(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ );
+
+#endif // NTOS_KERNEL_RUNTIME
+
+//
+// If any of these flags are set, the fast allocator punts
+// to the slow do-everything allocator.
+//
+#define HEAP_SLOW_FLAGS (HEAP_DEBUG_FLAGS | \
+ HEAP_SETTABLE_USER_FLAGS | \
+ HEAP_NEED_EXTRA_FLAGS | \
+ HEAP_CREATE_ALIGN_16 | \
+ HEAP_FREE_CHECKING_ENABLED | \
+ HEAP_TAIL_CHECKING_ENABLED)
+
+UCHAR CheckHeapFillPattern[ CHECK_HEAP_TAIL_SIZE ] = {
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL,
+ CHECK_HEAP_TAIL_FILL
+};
+
+PHEAP_UNCOMMMTTED_RANGE
+RtlpCreateUnCommittedRange(
+ IN PHEAP_SEGMENT Segment
+ )
+{
+ NTSTATUS Status;
+ PVOID FirstEntry, LastEntry;
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
+ ULONG ReserveSize, CommitSize;
+ PHEAP_UCR_SEGMENT UCRSegment;
+
+ RTL_PAGED_CODE();
+
+ pp = &Segment->Heap->UnusedUnCommittedRanges;
+ if (*pp == NULL) {
+ UCRSegment = Segment->Heap->UCRSegments;
+ if (UCRSegment == NULL ||
+ UCRSegment->CommittedSize == UCRSegment->ReservedSize
+ ) {
+ ReserveSize = 0x10000;
+ UCRSegment = NULL;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &UCRSegment,
+ 0,
+ &ReserveSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ CommitSize = 0x1000;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &UCRSegment,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ &UCRSegment,
+ &ReserveSize,
+ MEM_RELEASE
+ );
+ return NULL;
+ }
+
+ UCRSegment->Next = Segment->Heap->UCRSegments;
+ Segment->Heap->UCRSegments = UCRSegment;
+ UCRSegment->ReservedSize = ReserveSize;
+ UCRSegment->CommittedSize = CommitSize;
+ FirstEntry = (PCHAR)(UCRSegment + 1);
+ }
+ else {
+ CommitSize = 0x1000;
+ FirstEntry = (PCHAR)UCRSegment + UCRSegment->CommittedSize;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &FirstEntry,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ UCRSegment->CommittedSize += CommitSize;
+ }
+
+ LastEntry = (PCHAR)UCRSegment + UCRSegment->CommittedSize;
+ UnCommittedRange = (PHEAP_UNCOMMMTTED_RANGE)FirstEntry;
+ pp = &Segment->Heap->UnusedUnCommittedRanges;
+ while ((PCHAR)UnCommittedRange < (PCHAR)LastEntry) {
+ *pp = UnCommittedRange;
+ pp = &UnCommittedRange->Next;
+ UnCommittedRange += 1;
+ }
+ *pp = NULL;
+ pp = &Segment->Heap->UnusedUnCommittedRanges;
+ }
+
+ UnCommittedRange = *pp;
+ *pp = UnCommittedRange->Next;
+ return UnCommittedRange;
+}
+
+
+VOID
+RtlpDestroyUnCommittedRange(
+ IN PHEAP_SEGMENT Segment,
+ IN PHEAP_UNCOMMMTTED_RANGE UnCommittedRange
+ )
+{
+ RTL_PAGED_CODE();
+
+ UnCommittedRange->Next = Segment->Heap->UnusedUnCommittedRanges;
+ Segment->Heap->UnusedUnCommittedRanges = UnCommittedRange;
+ UnCommittedRange->Address = 0;
+ UnCommittedRange->Size = 0;
+ return;
+}
+
+VOID
+RtlpInsertUnCommittedPages(
+ IN PHEAP_SEGMENT Segment,
+ IN ULONG Address,
+ IN ULONG Size
+ )
+{
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
+
+ RTL_PAGED_CODE();
+
+ pp = &Segment->UnCommittedRanges;
+ while (UnCommittedRange = *pp) {
+ if (UnCommittedRange->Address > Address) {
+ if (Address + Size == UnCommittedRange->Address) {
+ UnCommittedRange->Address = Address;
+ UnCommittedRange->Size += Size;
+ if (UnCommittedRange->Size > Segment->LargestUnCommittedRange) {
+ Segment->LargestUnCommittedRange = UnCommittedRange->Size;
+ }
+
+ return;
+ }
+
+ break;
+ }
+ else
+ if ((UnCommittedRange->Address + UnCommittedRange->Size) == Address) {
+ Address = UnCommittedRange->Address;
+ Size += UnCommittedRange->Size;
+
+ *pp = UnCommittedRange->Next;
+ RtlpDestroyUnCommittedRange( Segment, UnCommittedRange );
+ Segment->NumberOfUnCommittedRanges -= 1;
+
+ if (Size > Segment->LargestUnCommittedRange) {
+ Segment->LargestUnCommittedRange = Size;
+ }
+ }
+ else {
+ pp = &UnCommittedRange->Next;
+ }
+ }
+
+ UnCommittedRange = RtlpCreateUnCommittedRange( Segment );
+ if (UnCommittedRange == NULL) {
+ HeapDebugPrint(( "Abandoning uncommitted range (%x for %x)\n", Address, Size ));
+ HeapDebugBreak( NULL );
+ return;
+ }
+
+ UnCommittedRange->Address = Address;
+ UnCommittedRange->Size = Size;
+ UnCommittedRange->Next = *pp;
+ *pp = UnCommittedRange;
+ Segment->NumberOfUnCommittedRanges += 1;
+ if (Size >= Segment->LargestUnCommittedRange) {
+ Segment->LargestUnCommittedRange = Size;
+ }
+
+ return;
+}
+
+
+
+PHEAP_FREE_ENTRY
+RtlpFindAndCommitPages(
+ IN PHEAP Heap,
+ IN PHEAP_SEGMENT Segment,
+ IN OUT PULONG Size,
+ IN PVOID AddressWanted OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PHEAP_ENTRY FirstEntry, LastEntry, PreviousLastEntry;
+ PHEAP_UNCOMMMTTED_RANGE PreviousUnCommittedRange, UnCommittedRange, *pp;
+ ULONG Address;
+
+ RTL_PAGED_CODE();
+
+ PreviousUnCommittedRange = NULL;
+ pp = &Segment->UnCommittedRanges;
+ while (UnCommittedRange = *pp) {
+ if (UnCommittedRange->Size >= *Size &&
+ (!ARGUMENT_PRESENT( AddressWanted ) || UnCommittedRange->Address == (ULONG)AddressWanted )
+ ) {
+ Address = UnCommittedRange->Address;
+ if (Heap->CommitRoutine != NULL) {
+ Status = (Heap->CommitRoutine)( Heap,
+ (PVOID *)&Address,
+ Size
+ );
+ }
+ else {
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Address,
+ 0,
+ Size,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ }
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ HeapInternalTrace( Segment->Heap, (Segment->Heap->TraceBuffer, HEAP_TRACE_COMMIT_MEMORY, 2, Address, *Size) );
+
+ Segment->NumberOfUnCommittedPages -= *Size / PAGE_SIZE;
+ if (Segment->LargestUnCommittedRange == UnCommittedRange->Size) {
+ Segment->LargestUnCommittedRange = 0;
+ }
+
+ FirstEntry = (PHEAP_ENTRY)Address;
+
+ if (PreviousUnCommittedRange == NULL) {
+ LastEntry = Segment->FirstEntry;
+ }
+ else {
+ LastEntry = (PHEAP_ENTRY)(PreviousUnCommittedRange->Address +
+ PreviousUnCommittedRange->Size);
+ }
+ while (!(LastEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ PreviousLastEntry = LastEntry;
+ LastEntry += LastEntry->Size;
+ if ((PCHAR)LastEntry >= (PCHAR)Segment->LastValidEntry || LastEntry->Size==0) {
+ HeapDebugPrint(( "Heap missing last entry in committed range near %x\n",
+ PreviousLastEntry
+ ));
+ HeapDebugBreak( PreviousLastEntry );
+ return NULL;
+ }
+ }
+
+
+ LastEntry->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
+ UnCommittedRange->Address += *Size;
+ UnCommittedRange->Size -= *Size;
+
+ HeapInternalTrace( Segment->Heap, (Segment->Heap->TraceBuffer, HEAP_TRACE_COMMIT_INSERT, 3, LastEntry, UnCommittedRange->Address, UnCommittedRange->Size) );
+
+ if (UnCommittedRange->Size == 0) {
+ if (UnCommittedRange->Address == (ULONG)Segment->LastValidEntry) {
+ FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
+ }
+ else {
+ FirstEntry->Flags = 0;
+ }
+
+ *pp = UnCommittedRange->Next;
+ RtlpDestroyUnCommittedRange( Segment, UnCommittedRange );
+ Segment->NumberOfUnCommittedRanges -= 1;
+ }
+ else {
+ FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
+ }
+ FirstEntry->SegmentIndex = LastEntry->SegmentIndex;
+ FirstEntry->Size = (USHORT)(*Size >> HEAP_GRANULARITY_SHIFT);
+ FirstEntry->PreviousSize = LastEntry->Size;
+
+ HeapInternalTrace( Segment->Heap, (Segment->Heap->TraceBuffer, HEAP_TRACE_COMMIT_NEW_ENTRY, 3, FirstEntry, *(PULONG)FirstEntry, *((PULONG)FirstEntry+1)) );
+
+ if (!(FirstEntry->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ (FirstEntry + FirstEntry->Size)->PreviousSize = FirstEntry->Size;
+ }
+ if (Segment->LargestUnCommittedRange == 0) {
+ UnCommittedRange = Segment->UnCommittedRanges;
+ while (UnCommittedRange != NULL) {
+ if (UnCommittedRange->Size >= Segment->LargestUnCommittedRange) {
+ Segment->LargestUnCommittedRange = UnCommittedRange->Size;
+ }
+ UnCommittedRange = UnCommittedRange->Next;
+ }
+ }
+
+ return (PHEAP_FREE_ENTRY)FirstEntry;
+ }
+ else {
+ PreviousUnCommittedRange = UnCommittedRange;
+ pp = &UnCommittedRange->Next;
+ }
+ }
+
+ return NULL;
+}
+
+
+BOOLEAN
+RtlpInitializeHeapSegment(
+ IN PHEAP Heap,
+ IN PHEAP_SEGMENT Segment,
+ IN UCHAR SegmentIndex,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN PVOID UnCommittedAddress,
+ IN PVOID CommitLimitAddress
+ )
+{
+ NTSTATUS Status;
+ PHEAP_ENTRY FirstEntry;
+ USHORT PreviousSize, Size;
+ ULONG NumberOfPages;
+ ULONG NumberOfCommittedPages;
+ ULONG NumberOfUnCommittedPages;
+ ULONG CommitSize;
+
+ RTL_PAGED_CODE();
+
+ NumberOfPages = ((ULONG)CommitLimitAddress - (ULONG)BaseAddress) / PAGE_SIZE;
+ FirstEntry = (PHEAP_ENTRY)ROUND_UP_TO_POWER2( Segment + 1,
+ HEAP_GRANULARITY
+ );
+
+ if ((PVOID)Heap == BaseAddress) {
+ PreviousSize = Heap->Entry.Size;
+ }
+ else {
+ PreviousSize = 0;
+ }
+
+ Size = (USHORT)(((ULONG)FirstEntry - (ULONG)Segment) >> HEAP_GRANULARITY_SHIFT);
+
+ if ((PCHAR)(FirstEntry + 1) >= (PCHAR)UnCommittedAddress) {
+ if ((PCHAR)(FirstEntry + 1) >= (PCHAR)CommitLimitAddress) {
+ return FALSE;
+ }
+
+ CommitSize = (PCHAR)(FirstEntry + 1) - (PCHAR)UnCommittedAddress;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&UnCommittedAddress,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return FALSE;
+ }
+
+ UnCommittedAddress = (PVOID)((PCHAR)UnCommittedAddress + CommitSize);
+ }
+
+ NumberOfUnCommittedPages = ((ULONG)CommitLimitAddress - (ULONG)UnCommittedAddress) / PAGE_SIZE;
+ NumberOfCommittedPages = NumberOfPages - NumberOfUnCommittedPages;
+
+ Segment->Entry.PreviousSize = PreviousSize;
+ Segment->Entry.Size = Size;
+ Segment->Entry.Flags = HEAP_ENTRY_BUSY;
+ Segment->Entry.SegmentIndex = SegmentIndex;
+#if i386 && !NTOS_KERNEL_RUNTIME
+ if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
+ Segment->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
+ }
+#endif // i386 && !NTOS_KERNEL_RUNTIME
+ Segment->Signature = HEAP_SEGMENT_SIGNATURE;
+ Segment->Flags = Flags;
+ Segment->Heap = Heap;
+ Segment->BaseAddress = BaseAddress;
+ Segment->FirstEntry = FirstEntry;
+ Segment->LastValidEntry = (PHEAP_ENTRY)((PCHAR)BaseAddress + (NumberOfPages * PAGE_SIZE));
+ Segment->NumberOfPages = NumberOfPages;
+ Segment->NumberOfUnCommittedPages = NumberOfUnCommittedPages;
+
+ if (NumberOfUnCommittedPages) {
+ RtlpInsertUnCommittedPages( Segment,
+ (ULONG)UnCommittedAddress,
+ NumberOfUnCommittedPages * PAGE_SIZE
+ );
+ }
+
+ Heap->Segments[ SegmentIndex ] = Segment;
+
+ PreviousSize = Segment->Entry.Size;
+ FirstEntry->Flags = HEAP_ENTRY_LAST_ENTRY;
+ FirstEntry->PreviousSize = PreviousSize;
+ FirstEntry->SegmentIndex = SegmentIndex;
+
+ RtlpInsertFreeBlock( Heap,
+ (PHEAP_FREE_ENTRY)FirstEntry,
+ (PHEAP_ENTRY)UnCommittedAddress - FirstEntry
+ );
+ return TRUE;
+}
+
+
+NTSTATUS
+RtlpDestroyHeapSegment(
+ IN PHEAP_SEGMENT Segment
+ )
+{
+ PVOID BaseAddress;
+ ULONG BytesToFree;
+
+ RTL_PAGED_CODE();
+
+ if (!(Segment->Flags & HEAP_SEGMENT_USER_ALLOCATED)) {
+ BaseAddress = Segment->BaseAddress;
+ BytesToFree = 0;
+ return ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&BaseAddress,
+ &BytesToFree,
+ MEM_RELEASE
+ );
+ }
+ else {
+ return STATUS_SUCCESS;
+ }
+}
+
+
+PVOID
+RtlCreateHeap(
+ IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN ULONG ReserveSize OPTIONAL,
+ IN ULONG CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes a heap.
+
+Arguments:
+
+ Flags - Specifies optional attributes of the heap.
+
+ Valid Flags Values:
+
+ HEAP_NO_SERIALIZE - if set, then allocations and deallocations on
+ this heap are NOT synchronized by these routines.
+
+ HEAP_GROWABLE - if set, then the heap is a "sparse" heap where
+ memory is committed only as necessary instead of
+ being preallocated.
+
+ HeapBase - if not NULL, this specifies the base address for memory
+ to use as the heap. If NULL, memory is allocated by these routines.
+
+ ReserveSize - if not zero, this specifies the amount of virtual address
+ space to reserve for the heap.
+
+ CommitSize - if not zero, this specifies the amount of virtual address
+ space to commit for the heap. Must be less than ReserveSize. If
+ zero, then defaults to one page.
+
+ Lock - if not NULL, this parameter points to the resource lock to
+ use. Only valid if HEAP_NO_SERIALIZE is NOT set.
+
+ Parameters - optional heap parameters.
+
+Return Value:
+
+ PVOID - a pointer to be used in accessing the created heap.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PHEAP Heap = NULL;
+ PHEAP_SEGMENT Segment = NULL;
+ PLIST_ENTRY FreeListHead;
+ ULONG SizeOfHeapHeader;
+ ULONG SegmentFlags;
+ PVOID CommittedBase;
+ PVOID UnCommittedBase;
+ MEMORY_BASIC_INFORMATION MemoryInformation;
+ ULONG n;
+ ULONG InitialCountOfUnusedUnCommittedRanges;
+ ULONG MaximumHeapBlockSize;
+ PVOID NextHeapHeaderAddress;
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
+ RTL_HEAP_PARAMETERS TempParameters;
+#ifndef NTOS_KERNEL_RUNTIME
+ PPEB Peb;
+#else
+ extern ULONG MmHeapSegmentReserve;
+ extern ULONG MmHeapSegmentCommit;
+ extern ULONG MmHeapDeCommitTotalFreeThreshold;
+ extern ULONG MmHeapDeCommitFreeBlockThreshold;
+#endif // NTOS_KERNEL_RUNTIME
+
+ RTL_PAGED_CODE();
+
+#ifdef DEBUG_PAGE_HEAP
+ if ( RtlpDebugPageHeap && ( HeapBase == NULL ) && ( Lock == NULL )) {
+ return RtlpDebugPageHeapCreate( Flags, HeapBase, ReserveSize, CommitSize, Lock, Parameters );
+ }
+ else {
+ Flags &= ~( HEAP_PROTECTION_ENABLED | HEAP_BREAK_WHEN_OUT_OF_VM | HEAP_NO_ALIGNMENT );
+ }
+#endif
+
+ if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS)) {
+ if (Flags & ~HEAP_CREATE_VALID_MASK) {
+ HeapDebugPrint(( "Invalid flags (%08x) specified to RtlCreateHeap\n", Flags ));
+ HeapDebugBreak( NULL );
+ Flags &= HEAP_CREATE_VALID_MASK;
+ }
+ }
+
+ MaximumHeapBlockSize = HEAP_MAXIMUM_BLOCK_SIZE << HEAP_GRANULARITY_SHIFT;
+
+ Status = STATUS_SUCCESS;
+ RtlZeroMemory( &TempParameters, sizeof( TempParameters ) );
+ if (ARGUMENT_PRESENT( Parameters )) {
+ try {
+ if (Parameters->Length == sizeof( *Parameters )) {
+ RtlMoveMemory( &TempParameters, Parameters, sizeof( *Parameters ) );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ Status = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+ }
+ Parameters = &TempParameters;
+
+ if (NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK) {
+ Flags |= HEAP_TAIL_CHECKING_ENABLED;
+ }
+
+ if (NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK) {
+ Flags |= HEAP_FREE_CHECKING_ENABLED;
+ }
+
+ if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) {
+ Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
+ }
+
+#ifndef NTOS_KERNEL_RUNTIME
+ Peb = NtCurrentPeb();
+
+ if (NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS) {
+ Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
+ }
+
+ if (NtGlobalFlag & FLG_HEAP_VALIDATE_ALL) {
+ Flags |= HEAP_VALIDATE_ALL_ENABLED;
+ }
+
+ if (NtGlobalFlag & FLG_HEAP_ENABLE_CALL_TRACING) {
+ Flags |= HEAP_CREATE_ENABLE_TRACING;
+ }
+ if (Parameters->SegmentReserve == 0) {
+ Parameters->SegmentReserve = Peb->HeapSegmentReserve;
+ }
+
+ if (Parameters->SegmentCommit == 0) {
+ Parameters->SegmentCommit = Peb->HeapSegmentCommit;
+ }
+
+ if (Parameters->DeCommitFreeBlockThreshold == 0) {
+ Parameters->DeCommitFreeBlockThreshold = Peb->HeapDeCommitFreeBlockThreshold;
+ }
+
+ if (Parameters->DeCommitTotalFreeThreshold == 0) {
+ Parameters->DeCommitTotalFreeThreshold = Peb->HeapDeCommitTotalFreeThreshold;
+ }
+#else
+ if (Parameters->SegmentReserve == 0) {
+ Parameters->SegmentReserve = MmHeapSegmentReserve;
+ }
+
+ if (Parameters->SegmentCommit == 0) {
+ Parameters->SegmentCommit = MmHeapSegmentCommit;
+ }
+
+ if (Parameters->DeCommitFreeBlockThreshold == 0) {
+ Parameters->DeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
+ }
+
+ if (Parameters->DeCommitTotalFreeThreshold == 0) {
+ Parameters->DeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ if (Parameters->MaximumAllocationSize == 0) {
+ Parameters->MaximumAllocationSize = ((ULONG)MM_HIGHEST_USER_ADDRESS -
+ (ULONG)MM_LOWEST_USER_ADDRESS -
+ PAGE_SIZE
+ );
+ }
+
+ if (Parameters->VirtualMemoryThreshold == 0 ||
+ Parameters->VirtualMemoryThreshold > MaximumHeapBlockSize
+ ) {
+ Parameters->VirtualMemoryThreshold = MaximumHeapBlockSize;
+ }
+
+ if (!ARGUMENT_PRESENT( CommitSize )) {
+ CommitSize = PAGE_SIZE;
+
+ if (!ARGUMENT_PRESENT( ReserveSize )) {
+ ReserveSize = 64 * CommitSize;
+ }
+ }
+ else
+ if (!ARGUMENT_PRESENT( ReserveSize )) {
+ ReserveSize = ROUND_UP_TO_POWER2( CommitSize, 64 * 1024 );
+ }
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugCreateHeap( Flags,
+ HeapBase,
+ ReserveSize,
+ CommitSize,
+ Lock,
+ Parameters
+ );
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ SizeOfHeapHeader = sizeof( HEAP );
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ if (ARGUMENT_PRESENT( Lock )) {
+ Flags |= HEAP_LOCK_USER_ALLOCATED;
+ }
+ else {
+ SizeOfHeapHeader += sizeof( HEAP_LOCK );
+ Lock = (PHEAP_LOCK)-1;
+ }
+ }
+ else
+ if (ARGUMENT_PRESENT( Lock )) {
+ return NULL;
+ }
+
+ //
+ // See if caller allocate the space for the heap.
+ //
+
+ if (ARGUMENT_PRESENT( HeapBase )) {
+ if (Parameters->CommitRoutine != NULL) {
+ if (Parameters->InitialCommit == 0 ||
+ Parameters->InitialReserve == 0 ||
+ Parameters->InitialCommit > Parameters->InitialReserve ||
+ Flags & HEAP_GROWABLE
+ ) {
+ return NULL;
+ }
+ CommittedBase = HeapBase;
+ UnCommittedBase = (PCHAR)CommittedBase + Parameters->InitialCommit;
+ ReserveSize = Parameters->InitialReserve;
+ RtlZeroMemory( CommittedBase, PAGE_SIZE );
+ }
+ else {
+ Status = ZwQueryVirtualMemory( NtCurrentProcess(),
+ HeapBase,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ if (MemoryInformation.BaseAddress != HeapBase) {
+ return NULL;
+ }
+
+ if (MemoryInformation.State == MEM_FREE) {
+ return NULL;
+ }
+
+ CommittedBase = MemoryInformation.BaseAddress;
+ if (MemoryInformation.State == MEM_COMMIT) {
+ RtlZeroMemory( CommittedBase, PAGE_SIZE );
+ CommitSize = MemoryInformation.RegionSize;
+ UnCommittedBase = (PCHAR)CommittedBase + CommitSize;
+ Status = ZwQueryVirtualMemory( NtCurrentProcess(),
+ UnCommittedBase,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ ReserveSize = CommitSize;
+ if (NT_SUCCESS( Status ) &&
+ MemoryInformation.State == MEM_RESERVE
+ ) {
+ ReserveSize += MemoryInformation.RegionSize;
+ }
+ }
+ else {
+ CommitSize = PAGE_SIZE;
+ UnCommittedBase = CommittedBase;
+ }
+ }
+
+ SegmentFlags = HEAP_SEGMENT_USER_ALLOCATED;
+ Heap = (PHEAP)HeapBase;
+ }
+ else {
+ if (Parameters->CommitRoutine != NULL) {
+ return NULL;
+ }
+
+ //
+ // Reserve the amount of virtual address space requested.
+ //
+
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Heap,
+ 0,
+ &ReserveSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ SegmentFlags = 0;
+
+ if (!ARGUMENT_PRESENT( CommitSize )) {
+ CommitSize = PAGE_SIZE;
+ }
+
+ CommittedBase = Heap;
+ UnCommittedBase = Heap;
+ }
+
+ if (CommittedBase == UnCommittedBase) {
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&CommittedBase,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (!ARGUMENT_PRESENT(HeapBase)) {
+ //
+ // Return the reserved virtual address space.
+ //
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Heap,
+ &ReserveSize,
+ MEM_RELEASE );
+ }
+ return NULL;
+ }
+
+ UnCommittedBase = (PVOID)((PCHAR)UnCommittedBase + CommitSize);
+ }
+
+ NextHeapHeaderAddress = Heap + 1;
+ UnCommittedRange = (PHEAP_UNCOMMMTTED_RANGE)ROUND_UP_TO_POWER2( NextHeapHeaderAddress,
+ sizeof( QUAD )
+ );
+ InitialCountOfUnusedUnCommittedRanges = 8;
+ SizeOfHeapHeader += InitialCountOfUnusedUnCommittedRanges * sizeof( *UnCommittedRange );
+ pp = &Heap->UnusedUnCommittedRanges;
+ while (InitialCountOfUnusedUnCommittedRanges--) {
+ *pp = UnCommittedRange;
+ pp = &UnCommittedRange->Next;
+ UnCommittedRange += 1;
+ }
+ NextHeapHeaderAddress = UnCommittedRange;
+ *pp = NULL;
+
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ Heap->PseudoTagEntries = (PHEAP_PSEUDO_TAG_ENTRY)ROUND_UP_TO_POWER2( NextHeapHeaderAddress,
+ sizeof( QUAD )
+ );
+ SizeOfHeapHeader += HEAP_NUMBER_OF_PSEUDO_TAG * sizeof( HEAP_PSEUDO_TAG_ENTRY );
+ NextHeapHeaderAddress = Heap->PseudoTagEntries + HEAP_NUMBER_OF_PSEUDO_TAG;
+ }
+
+ SizeOfHeapHeader = ROUND_UP_TO_POWER2( SizeOfHeapHeader,
+ HEAP_GRANULARITY
+ );
+
+ Heap->Entry.Size = (USHORT)(SizeOfHeapHeader >> HEAP_GRANULARITY_SHIFT);
+ Heap->Entry.Flags = HEAP_ENTRY_BUSY;
+
+ Heap->Signature = HEAP_SIGNATURE;
+ Heap->Flags = Flags;
+ Heap->ForceFlags = (Flags & (HEAP_NO_SERIALIZE |
+ HEAP_GENERATE_EXCEPTIONS |
+ HEAP_ZERO_MEMORY |
+ HEAP_REALLOC_IN_PLACE_ONLY |
+ HEAP_VALIDATE_PARAMETERS_ENABLED |
+ HEAP_VALIDATE_ALL_ENABLED |
+ HEAP_CREATE_ENABLE_TRACING |
+ HEAP_TAIL_CHECKING_ENABLED |
+ HEAP_CREATE_ALIGN_16 |
+ HEAP_FREE_CHECKING_ENABLED
+ )
+ );
+ Heap->EventLogMask = (0x00010000) << ((Flags & HEAP_CLASS_MASK) >> 12);
+
+ Heap->FreeListsInUseTerminate = 0xFFFF;
+ Heap->HeaderValidateLength = (USHORT)((ULONG)NextHeapHeaderAddress - (ULONG)Heap);
+ Heap->HeaderValidateCopy = NULL;
+
+ FreeListHead = &Heap->FreeLists[ 0 ];
+ n = HEAP_MAXIMUM_FREELISTS;
+ while (n--) {
+ InitializeListHead( FreeListHead );
+ FreeListHead++;
+ }
+ InitializeListHead( &Heap->VirtualAllocdBlocks );
+
+ //
+ // Initialize the cricital section that controls access to
+ // the free list.
+ //
+
+ if (Lock == (PHEAP_LOCK)-1) {
+ Lock = (PHEAP_LOCK)NextHeapHeaderAddress;
+ Status = RtlInitializeLockRoutine( Lock );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+
+ NextHeapHeaderAddress = (PHEAP_LOCK)Lock + 1;
+ }
+ Heap->LockVariable = Lock;
+
+ if (!RtlpInitializeHeapSegment( Heap,
+ (PHEAP_SEGMENT)
+ ((PCHAR)Heap + SizeOfHeapHeader),
+ 0,
+ SegmentFlags,
+ CommittedBase,
+ UnCommittedBase,
+ (PCHAR)CommittedBase + ReserveSize
+ )
+ ) {
+ return NULL;
+ }
+
+ Heap->ProcessHeapsListIndex = 0;
+ Heap->SegmentReserve = Parameters->SegmentReserve;
+ Heap->SegmentCommit = Parameters->SegmentCommit;
+ Heap->DeCommitFreeBlockThreshold = Parameters->DeCommitFreeBlockThreshold >> HEAP_GRANULARITY_SHIFT;
+ Heap->DeCommitTotalFreeThreshold = Parameters->DeCommitTotalFreeThreshold >> HEAP_GRANULARITY_SHIFT;
+ Heap->MaximumAllocationSize = Parameters->MaximumAllocationSize;
+ Heap->VirtualMemoryThreshold = ROUND_UP_TO_POWER2( Parameters->VirtualMemoryThreshold,
+ HEAP_GRANULARITY
+ ) >> HEAP_GRANULARITY_SHIFT;
+ if (Flags & HEAP_CREATE_ALIGN_16) {
+ Heap->AlignRound = 15 + sizeof( HEAP_ENTRY );
+ Heap->AlignMask = (ULONG)~15;
+ }
+ else {
+ Heap->AlignRound = 7 + sizeof( HEAP_ENTRY );
+ Heap->AlignMask = (ULONG)~7;
+ }
+
+ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
+ Heap->AlignRound += CHECK_HEAP_TAIL_SIZE;
+
+ }
+ Heap->CommitRoutine = Parameters->CommitRoutine;
+
+#if !defined(NTOS_KERNEL_RUNTIME)
+ RtlpAddHeapToProcessList( Heap );
+#endif // !defined(NTOS_KERNEL_RUNTIME)
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpCreateHeapEventId,
+ Heap->EventLogMask,
+ Flags,
+ Heap,
+ ReserveSize,
+ CommitSize
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ return (PVOID)Heap;
+} // RtlCreateHeap
+
+
+PVOID
+RtlDestroyHeap(
+ IN PVOID HeapHandle
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_SEGMENT Segment;
+ PHEAP_UCR_SEGMENT UCRSegments;
+ PLIST_ENTRY Head, Next;
+ PVOID BaseAddress;
+ ULONG RegionSize;
+ UCHAR SegmentIndex;
+
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ RTL_PAGED_CODE();
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapDestroy( HeapHandle )
+ );
+
+ if (Heap == NULL) {
+ return NULL;
+ }
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (DEBUG_HEAP( Heap->Flags )) {
+ if (!RtlDebugDestroyHeap( HeapHandle )) {
+ return HeapHandle;
+ }
+ }
+
+ if (HeapHandle == NtCurrentPeb()->ProcessHeap) {
+ return HeapHandle;
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpDestroyHeapEventId,
+ Heap->EventLogMask,
+ Heap
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ Head = &Heap->VirtualAllocdBlocks;
+ Next = Head->Flink;
+ while (Head != Next) {
+ BaseAddress = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
+ Next = Next->Flink;
+ RegionSize = 0;
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&BaseAddress,
+ &RegionSize,
+ MEM_RELEASE
+ );
+ }
+
+#if !defined(NTOS_KERNEL_RUNTIME)
+ RtlpDestroyTags( Heap );
+ RtlpRemoveHeapFromProcessList( Heap );
+#endif // !defined(NTOS_KERNEL_RUNTIME)
+
+ //
+ // If the heap is serialized, delete the critical section created
+ // by RtlCreateHeap.
+ //
+
+ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
+ if (!(Heap->Flags & HEAP_LOCK_USER_ALLOCATED)) {
+ (VOID)RtlDeleteLockRoutine( Heap->LockVariable );
+ }
+
+ Heap->LockVariable = NULL;
+ }
+
+ UCRSegments = Heap->UCRSegments;
+ Heap->UCRSegments = NULL;
+ while (UCRSegments) {
+ BaseAddress = UCRSegments;
+ UCRSegments = UCRSegments->Next;
+ RegionSize = 0;
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ &BaseAddress,
+ &RegionSize,
+ MEM_RELEASE
+ );
+ }
+
+ SegmentIndex = HEAP_MAXIMUM_SEGMENTS;
+ while (SegmentIndex--) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment) {
+ RtlpDestroyHeapSegment( Segment );
+ }
+ }
+
+ return NULL;
+} // RtlDestroyHeap
+
+
+PHEAP_FREE_ENTRY
+RtlpExtendHeap(
+ IN PHEAP Heap,
+ IN ULONG AllocationSize
+ )
+{
+ NTSTATUS Status;
+ PHEAP_SEGMENT Segment;
+ PHEAP_FREE_ENTRY FreeBlock;
+ UCHAR SegmentIndex, EmptySegmentIndex;
+ ULONG NumberOfPages;
+ ULONG CommitSize;
+ ULONG ReserveSize;
+ ULONG FreeSize;
+
+ RTL_PAGED_CODE();
+
+ NumberOfPages = ((AllocationSize + PAGE_SIZE - 1) / PAGE_SIZE);
+ FreeSize = NumberOfPages * PAGE_SIZE;
+
+ HeapInternalTrace( Heap, (Heap->TraceBuffer, HEAP_TRACE_EXTEND_HEAP, 3, AllocationSize, NumberOfPages, FreeSize) );
+
+ EmptySegmentIndex = HEAP_MAXIMUM_SEGMENTS;
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment &&
+ NumberOfPages <= Segment->NumberOfUnCommittedPages &&
+ FreeSize <= Segment->LargestUnCommittedRange
+ ) {
+ FreeBlock = RtlpFindAndCommitPages( Heap,
+ Segment,
+ &FreeSize,
+ NULL
+ );
+ if (FreeBlock != NULL) {
+ FreeSize = FreeSize >> HEAP_GRANULARITY_SHIFT;
+ FreeBlock = RtlpCoalesceFreeBlocks( Heap, FreeBlock, &FreeSize, FALSE );
+ RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
+ return FreeBlock;
+ }
+ }
+ else
+ if (Segment == NULL && EmptySegmentIndex == HEAP_MAXIMUM_SEGMENTS) {
+ EmptySegmentIndex = SegmentIndex;
+ }
+ }
+
+ if (EmptySegmentIndex != HEAP_MAXIMUM_SEGMENTS &&
+ Heap->Flags & HEAP_GROWABLE
+ ) {
+ Segment = NULL;
+ if ((AllocationSize + PAGE_SIZE) > Heap->SegmentReserve) {
+ ReserveSize = AllocationSize + PAGE_SIZE;
+ }
+ else {
+ ReserveSize = Heap->SegmentReserve;
+ }
+
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Segment,
+ 0,
+ &ReserveSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if (NT_SUCCESS( Status )) {
+ Heap->SegmentReserve += ReserveSize;
+ if ((AllocationSize + PAGE_SIZE) > Heap->SegmentCommit) {
+ CommitSize = AllocationSize + PAGE_SIZE;
+ }
+ else {
+ CommitSize = Heap->SegmentCommit;
+ }
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Segment,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (NT_SUCCESS( Status ) &&
+ !RtlpInitializeHeapSegment( Heap,
+ Segment,
+ EmptySegmentIndex,
+ 0,
+ Segment,
+ (PCHAR)Segment + CommitSize,
+ (PCHAR)Segment + ReserveSize
+ )
+ ) {
+ Status = STATUS_NO_MEMORY;
+ }
+
+ if (NT_SUCCESS(Status)) {
+ return (PHEAP_FREE_ENTRY)Segment->FirstEntry;
+ }
+
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Segment,
+ &ReserveSize,
+ MEM_RELEASE
+ );
+ }
+ }
+
+#if !defined(NTOS_KERNEL_RUNTIME)
+ if (Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE) {
+ FreeBlock = RtlpCoalesceHeap( Heap );
+ if ((FreeBlock != NULL) && (FreeBlock->Size >= AllocationSize)) {
+ return(FreeBlock);
+ }
+ }
+#endif
+ return NULL;
+}
+
+
+PHEAP_FREE_ENTRY
+RtlpCoalesceFreeBlocks(
+ IN PHEAP Heap,
+ IN PHEAP_FREE_ENTRY FreeBlock,
+ IN OUT PULONG FreeSize,
+ IN BOOLEAN RemoveFromFreeList
+ )
+{
+ PHEAP_FREE_ENTRY FreeBlock1, NextFreeBlock;
+
+ RTL_PAGED_CODE();
+
+ FreeBlock1 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock - FreeBlock->PreviousSize);
+ if (FreeBlock1 != FreeBlock &&
+ !(FreeBlock1->Flags & HEAP_ENTRY_BUSY) &&
+ (*FreeSize + FreeBlock1->Size) <= HEAP_MAXIMUM_BLOCK_SIZE
+ ) {
+ HEAPASSERT(FreeBlock->PreviousSize == FreeBlock1->Size);
+ HeapInternalTrace( Heap, (Heap->TraceBuffer, HEAP_TRACE_COALESCE_FREE_BLOCKS,
+ 7,
+ FreeBlock1, *(PULONG)FreeBlock1, *((PULONG)FreeBlock1+1),
+ FreeBlock, *(PULONG)FreeBlock, *((PULONG)FreeBlock+1),
+ *FreeSize + FreeBlock1->Size
+ )
+ );
+
+ if (RemoveFromFreeList) {
+ RtlpRemoveFreeBlock( Heap, FreeBlock );
+ Heap->TotalFreeSize -= FreeBlock->Size;
+ RemoveFromFreeList = FALSE;
+ }
+
+ RtlpRemoveFreeBlock( Heap, FreeBlock1 );
+ FreeBlock1->Flags = FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY;
+ FreeBlock = FreeBlock1;
+ *FreeSize += FreeBlock1->Size;
+ Heap->TotalFreeSize -= FreeBlock1->Size;
+ FreeBlock->Size = (USHORT)*FreeSize;
+ if (!(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_ENTRY)FreeBlock + *FreeSize)->PreviousSize = (USHORT)*FreeSize;
+ }
+ }
+
+ if (!(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ NextFreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock + *FreeSize);
+ if (!(NextFreeBlock->Flags & HEAP_ENTRY_BUSY) &&
+ (*FreeSize + NextFreeBlock->Size) <= HEAP_MAXIMUM_BLOCK_SIZE
+ ) {
+ HEAPASSERT(*FreeSize == NextFreeBlock->PreviousSize);
+ HeapInternalTrace( Heap, (Heap->TraceBuffer, HEAP_TRACE_COALESCE_FREE_BLOCKS,
+ 7,
+ FreeBlock, *(PULONG)FreeBlock, *((PULONG)FreeBlock+1),
+ NextFreeBlock, *(PULONG)NextFreeBlock, *((PULONG)NextFreeBlock+1),
+ *FreeSize + NextFreeBlock->Size
+ )
+ );
+ if (RemoveFromFreeList) {
+ RtlpRemoveFreeBlock( Heap, FreeBlock );
+ Heap->TotalFreeSize -= FreeBlock->Size;
+ RemoveFromFreeList = FALSE;
+ }
+
+ FreeBlock->Flags = NextFreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY;
+ RtlpRemoveFreeBlock( Heap, NextFreeBlock );
+ *FreeSize += NextFreeBlock->Size;
+ Heap->TotalFreeSize -= NextFreeBlock->Size;
+ FreeBlock->Size = (USHORT)*FreeSize;
+ if (!(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_ENTRY)FreeBlock + *FreeSize)->PreviousSize = (USHORT)*FreeSize;
+ }
+ }
+ }
+
+ return FreeBlock;
+}
+
+
+VOID
+RtlpDeCommitFreeBlock(
+ IN PHEAP Heap,
+ IN PHEAP_FREE_ENTRY FreeBlock,
+ IN ULONG FreeSize
+ )
+{
+ NTSTATUS Status;
+ ULONG DeCommitAddress, DeCommitSize;
+ USHORT LeadingFreeSize, TrailingFreeSize;
+ PHEAP_SEGMENT Segment;
+ PHEAP_FREE_ENTRY LeadingFreeBlock, TrailingFreeBlock;
+ PHEAP_ENTRY LeadingBusyBlock, TrailingBusyBlock;
+
+ RTL_PAGED_CODE();
+
+ if (Heap->CommitRoutine != NULL) {
+ RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
+ return;
+ }
+
+ Segment = Heap->Segments[ FreeBlock->SegmentIndex ];
+
+ LeadingBusyBlock = NULL;
+ LeadingFreeBlock = FreeBlock;
+ DeCommitAddress = ROUND_UP_TO_POWER2( LeadingFreeBlock, PAGE_SIZE );
+ LeadingFreeSize = (USHORT)((PHEAP_ENTRY)DeCommitAddress - (PHEAP_ENTRY)LeadingFreeBlock);
+ if (LeadingFreeSize == 1) {
+ DeCommitAddress += PAGE_SIZE;
+ LeadingFreeSize += PAGE_SIZE >> HEAP_GRANULARITY_SHIFT;
+ }
+ else
+ if (LeadingFreeBlock->PreviousSize != 0) {
+ if (DeCommitAddress == (ULONG)LeadingFreeBlock) {
+ LeadingBusyBlock = (PHEAP_ENTRY)LeadingFreeBlock - LeadingFreeBlock->PreviousSize;
+ }
+ }
+
+ TrailingBusyBlock = NULL;
+ TrailingFreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock + FreeSize);
+ DeCommitSize = ROUND_DOWN_TO_POWER2( (ULONG)TrailingFreeBlock, PAGE_SIZE );
+ TrailingFreeSize = (PHEAP_ENTRY)TrailingFreeBlock - (PHEAP_ENTRY)DeCommitSize;
+ if (TrailingFreeSize == (sizeof( HEAP_ENTRY ) >> HEAP_GRANULARITY_SHIFT)) {
+ DeCommitSize -= PAGE_SIZE;
+ TrailingFreeSize += PAGE_SIZE >> HEAP_GRANULARITY_SHIFT;
+ }
+ else
+ if (TrailingFreeSize == 0 && !(FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ TrailingBusyBlock = (PHEAP_ENTRY)TrailingFreeBlock;
+ }
+
+ TrailingFreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)TrailingFreeBlock - TrailingFreeSize);
+ if (DeCommitSize > DeCommitAddress) {
+ DeCommitSize -= DeCommitAddress;
+ }
+ else {
+ DeCommitSize = 0;
+ }
+
+ if (DeCommitSize != 0) {
+ Status = ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&DeCommitAddress,
+ &DeCommitSize,
+ MEM_DECOMMIT
+ );
+ if (NT_SUCCESS( Status )) {
+ RtlpInsertUnCommittedPages( Segment,
+ DeCommitAddress,
+ DeCommitSize
+ );
+ Segment->NumberOfUnCommittedPages += DeCommitSize / PAGE_SIZE;
+
+ if (LeadingFreeSize != 0) {
+ LeadingFreeBlock->Flags = HEAP_ENTRY_LAST_ENTRY;
+ LeadingFreeBlock->Size = LeadingFreeSize;
+ Heap->TotalFreeSize += LeadingFreeSize;
+ RtlpInsertFreeBlockDirect( Heap, LeadingFreeBlock, LeadingFreeSize );
+ }
+ else
+ if (LeadingBusyBlock != NULL) {
+ LeadingBusyBlock->Flags |= HEAP_ENTRY_LAST_ENTRY;
+ }
+
+ if (TrailingFreeSize != 0) {
+ TrailingFreeBlock->PreviousSize = 0;
+ TrailingFreeBlock->SegmentIndex = Segment->Entry.SegmentIndex;
+ TrailingFreeBlock->Flags = 0;
+ TrailingFreeBlock->Size = TrailingFreeSize;
+ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)TrailingFreeBlock + TrailingFreeSize))->PreviousSize = (USHORT)TrailingFreeSize;
+ RtlpInsertFreeBlockDirect( Heap, TrailingFreeBlock, TrailingFreeSize );
+ Heap->TotalFreeSize += TrailingFreeSize;
+ }
+ else
+ if (TrailingBusyBlock != NULL) {
+ TrailingBusyBlock->PreviousSize = 0;
+ }
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
+ }
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, LeadingFreeBlock, FreeSize );
+ }
+
+ return;
+}
+
+
+VOID
+RtlpInsertFreeBlock(
+ IN PHEAP Heap,
+ IN PHEAP_FREE_ENTRY FreeBlock,
+ IN ULONG FreeSize
+ )
+{
+ USHORT PreviousSize, Size;
+ UCHAR Flags;
+ UCHAR SegmentIndex;
+ PHEAP_SEGMENT Segment;
+
+ RTL_PAGED_CODE();
+
+ PreviousSize = FreeBlock->PreviousSize;
+ SegmentIndex = FreeBlock->SegmentIndex;
+ Segment = Heap->Segments[ SegmentIndex ];
+ Flags = FreeBlock->Flags;
+ Heap->TotalFreeSize += FreeSize;
+
+ while (FreeSize != 0) {
+ if (FreeSize > (ULONG)HEAP_MAXIMUM_BLOCK_SIZE) {
+ Size = HEAP_MAXIMUM_BLOCK_SIZE;
+ if (FreeSize == (ULONG)HEAP_MAXIMUM_BLOCK_SIZE + 1) {
+ Size -= 16;
+ }
+
+ FreeBlock->Flags = 0;
+ }
+ else {
+ Size = (USHORT)FreeSize;
+ FreeBlock->Flags = Flags;
+ }
+
+ FreeBlock->PreviousSize = PreviousSize;
+ FreeBlock->SegmentIndex = SegmentIndex;
+ FreeBlock->Size = Size;
+ RtlpInsertFreeBlockDirect( Heap, FreeBlock, Size );
+ PreviousSize = Size;
+ FreeSize -= Size;
+ FreeBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)FreeBlock + Size);
+ if ((PHEAP_ENTRY)FreeBlock >= Segment->LastValidEntry) {
+ return;
+ }
+ }
+
+ if (!(Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ FreeBlock->PreviousSize = PreviousSize;
+ }
+ return;
+}
+
+
+#define RtlFindFirstSetRightMember(Set) \
+ (((Set) & 0xFFFF) ? \
+ (((Set) & 0xFF) ? \
+ RtlpBitsClearLow[(Set) & 0xFF] : \
+ RtlpBitsClearLow[((Set) >> 8) & 0xFF] + 8) : \
+ ((((Set) >> 16) & 0xFF) ? \
+ RtlpBitsClearLow[ ((Set) >> 16) & 0xFF] + 16 : \
+ RtlpBitsClearLow[ (Set) >> 24] + 24) \
+ )
+
+PVOID
+RtlAllocateHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PULONG FreeListsInUse;
+ ULONG FreeListsInUseUlong;
+ ULONG AllocationSize;
+ ULONG FreeSize, AllocationIndex;
+ PLIST_ENTRY FreeListHead, Next;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
+ ULONG InUseIndex;
+ UCHAR FreeFlags;
+ NTSTATUS Status;
+ EXCEPTION_RECORD ExceptionRecord;
+ PVOID ReturnValue;
+
+ RTL_PAGED_CODE();
+
+ Flags |= Heap->ForceFlags;
+
+ //
+ // Check for special features that force us to call the slow, do-everything
+ // version.
+ //
+
+ if (( ! ( Flags & HEAP_SLOW_FLAGS )) && ( Size < 0x80000000 )) {
+
+ //
+ // Round the requested size up to the allocation granularity. Note
+ // that if the request is for 0 bytes, we still allocate memory, because
+ // we add in an extra 1 byte to protect ourselves from idiots.
+ //
+
+ AllocationSize = ((Size ? Size : 1) + 7 + sizeof( HEAP_ENTRY )) & (ULONG)~7;
+ AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+
+ //
+ // Lock the free list.
+ //
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ }
+
+ if (AllocationIndex < HEAP_MAXIMUM_FREELISTS) {
+ FreeListHead = &Heap->FreeLists[ AllocationIndex ];
+ if ( !IsListEmpty( FreeListHead )) {
+ FreeBlock = CONTAINING_RECORD( FreeListHead->Blink,
+ HEAP_FREE_ENTRY,
+ FreeList );
+ FreeFlags = FreeBlock->Flags;
+ RtlpFastRemoveDedicatedFreeBlock( Heap, FreeBlock );
+ Heap->TotalFreeSize -= AllocationIndex;
+ BusyBlock = (PHEAP_ENTRY)FreeBlock;
+ BusyBlock->Flags = HEAP_ENTRY_BUSY | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
+ BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
+ BusyBlock->SmallTagIndex = 0;
+ } else {
+
+ //
+ // Scan the free list in use vector to find the smallest
+ // available free block large enough for our allocations.
+ //
+
+ //
+ // Compute the index of the ULONG where the scan should begin
+ //
+ InUseIndex = AllocationIndex >> 5;
+ FreeListsInUse = &Heap->u.FreeListsInUseUlong[InUseIndex];
+
+ //
+ // Mask off the bits in the first ULONG that represent allocations
+ // smaller than we need.
+ //
+ FreeListsInUseUlong = *FreeListsInUse++ & ~((1 << (AllocationIndex & 0x1f)) - 1);
+
+ //
+ // Begin unrolled loop to scan bit vector.
+ //
+ switch (InUseIndex) {
+ case 0:
+ if (FreeListsInUseUlong) {
+ FreeListHead = &Heap->FreeLists[0];
+ break;
+ }
+ FreeListsInUseUlong = *FreeListsInUse++;
+
+ // deliberate fallthrough to next ULONG
+
+ case 1:
+ if (FreeListsInUseUlong) {
+ FreeListHead = &Heap->FreeLists[32];
+ break;
+ }
+ FreeListsInUseUlong = *FreeListsInUse++;
+
+ // deliberate fallthrough to next ULONG
+
+ case 2:
+ if (FreeListsInUseUlong) {
+ FreeListHead = &Heap->FreeLists[64];
+ break;
+ }
+ FreeListsInUseUlong = *FreeListsInUse++;
+
+ // deliberate fallthrough to next ULONG
+
+ case 3:
+ if (FreeListsInUseUlong) {
+ FreeListHead = &Heap->FreeLists[96];
+ break;
+ }
+
+ // deliberate fallthrough to non dedicated list
+
+ default:
+
+ //
+ // No suitable entry on the free list was found.
+ //
+ goto LookInNonDedicatedList;
+ }
+
+ //
+ // A free list has been found with a large enough allocation. FreeListHead
+ // contains the base of the vector it was found in. FreeListsInUseUlong
+ // contains the vector.
+ //
+ FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
+
+ FreeBlock = CONTAINING_RECORD( FreeListHead->Blink,
+ HEAP_FREE_ENTRY,
+ FreeList );
+ RtlpFastRemoveDedicatedFreeBlock( Heap, FreeBlock );
+SplitFreeBlock:
+ FreeFlags = FreeBlock->Flags;
+ Heap->TotalFreeSize -= FreeBlock->Size;
+
+ BusyBlock = (PHEAP_ENTRY)FreeBlock;
+ BusyBlock->Flags = HEAP_ENTRY_BUSY;
+ FreeSize = BusyBlock->Size - AllocationIndex;
+ BusyBlock->Size = (USHORT)AllocationIndex;
+ BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
+ BusyBlock->SmallTagIndex = 0;
+
+ if (FreeSize != 0) {
+ if (FreeSize == 1) {
+ BusyBlock->Size += 1;
+ BusyBlock->UnusedBytes += sizeof( HEAP_ENTRY );
+ } else {
+ SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
+ SplitBlock->Flags = FreeFlags;
+ SplitBlock->PreviousSize = (USHORT)AllocationIndex;
+ SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
+ RtlpFastInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize);
+ Heap->TotalFreeSize += FreeSize;
+ } else {
+ SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
+ if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
+ SplitBlock2->PreviousSize = (USHORT)FreeSize;
+ RtlpFastInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ } else {
+ SplitBlock->Flags = SplitBlock2->Flags;
+ RtlpFastRemoveFreeBlock( Heap, SplitBlock2 );
+ Heap->TotalFreeSize -= SplitBlock2->Size;
+ FreeSize += SplitBlock2->Size;
+ if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
+ }
+ RtlpFastInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ } else {
+ RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
+ }
+ }
+ }
+ FreeFlags = 0;
+ }
+ }
+ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
+ BusyBlock->Flags |= HEAP_ENTRY_LAST_ENTRY;
+ }
+ }
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ //
+ // Unlock the free list.
+ //
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ //
+ // Return the address of the user portion of the allocated block.
+ // This is the byte following the header.
+ //
+ ReturnValue = BusyBlock + 1;
+
+ if (Flags & HEAP_ZERO_MEMORY) {
+ RtlZeroMemory( ReturnValue, Size );
+ }
+
+ return(ReturnValue);
+ } else if (AllocationIndex <= Heap->VirtualMemoryThreshold) {
+
+LookInNonDedicatedList:
+ FreeListHead = &Heap->FreeLists[0];
+ Next = FreeListHead->Flink;
+ while (FreeListHead != Next) {
+ FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
+ if (FreeBlock->Size >= AllocationIndex) {
+ RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
+ goto SplitFreeBlock;
+ }
+ Next = Next->Flink;
+ }
+
+ FreeBlock = RtlpExtendHeap( Heap, AllocationSize );
+ if (FreeBlock != NULL) {
+ RtlpFastRemoveNonDedicatedFreeBlock( Heap, FreeBlock );
+ goto SplitFreeBlock;
+ }
+ Status = STATUS_NO_MEMORY;
+
+ } else if (Heap->Flags & HEAP_GROWABLE) {
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ VirtualAllocBlock = NULL;
+ AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&VirtualAllocBlock,
+ 0,
+ &AllocationSize,
+ MEM_COMMIT,
+ PAGE_READWRITE );
+ if (NT_SUCCESS(Status)) {
+ //
+ // Just committed, already zero.
+ //
+ VirtualAllocBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
+ VirtualAllocBlock->BusyBlock.Flags = HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT | HEAP_ENTRY_BUSY;
+ VirtualAllocBlock->CommitSize = AllocationSize;
+ VirtualAllocBlock->ReserveSize = AllocationSize;
+ InsertTailList( &Heap->VirtualAllocdBlocks, (PLIST_ENTRY)VirtualAllocBlock );
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ //
+ // Unlock the free list.
+ //
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ //
+ // Return the address of the user portion of the allocated block.
+ // This is the byte following the header.
+ //
+ return (PHEAP_ENTRY)(VirtualAllocBlock + 1);
+ }
+ } else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // This is the error return.
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ //
+ // Unlock the free list.
+ //
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ if (Flags & HEAP_GENERATE_EXCEPTIONS) {
+ //
+ // Construct an exception record.
+ //
+ ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 1;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.ExceptionInformation[ 0 ] = AllocationSize;
+ RtlRaiseException( &ExceptionRecord );
+ }
+
+ SET_LAST_STATUS(Status);
+ return(NULL);
+ } else {
+ return(RtlAllocateHeapSlowly(HeapHandle, Flags, Size));
+ }
+}
+
+PVOID
+RtlAllocateHeapSlowly(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ PVOID ReturnValue=NULL;
+ PULONG FreeListsInUse;
+ ULONG FreeListsInUseUlong;
+ ULONG AllocationSize;
+ ULONG FreeSize, AllocationIndex;
+ UCHAR EntryFlags, FreeFlags;
+ PLIST_ENTRY FreeListHead, Next;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ NTSTATUS Status;
+ EXCEPTION_RECORD ExceptionRecord;
+ ULONG ZeroSize = 0;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Note that Flags has already been OR'd with Heap->ForceFlags.
+ //
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugAllocateHeap( HeapHandle, Flags, Size );
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ if (Size > 0x7fffffff) {
+ SET_LAST_STATUS( STATUS_NO_MEMORY );
+ return NULL;
+ }
+
+ AllocationSize = ((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask;
+ EntryFlags = (UCHAR)(HEAP_ENTRY_BUSY | ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4));
+ if (Flags & HEAP_NEED_EXTRA_FLAGS || Heap->PseudoTagEntries != NULL) {
+ EntryFlags |= HEAP_ENTRY_EXTRA_PRESENT;
+ AllocationSize += sizeof( HEAP_ENTRY_EXTRA );
+ }
+ AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
+
+ //
+ // Lock the free list.
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+
+ try {
+ if (AllocationIndex < HEAP_MAXIMUM_FREELISTS) {
+ FreeListHead = &Heap->FreeLists[ AllocationIndex ];
+ if ( !IsListEmpty( FreeListHead )) {
+ FreeBlock = CONTAINING_RECORD( FreeListHead->Flink,
+ HEAP_FREE_ENTRY,
+ FreeList
+ );
+ FreeFlags = FreeBlock->Flags;
+ RtlpRemoveFreeBlock( Heap, FreeBlock );
+ Heap->TotalFreeSize -= AllocationIndex;
+ BusyBlock = (PHEAP_ENTRY)FreeBlock;
+ BusyBlock->Flags = EntryFlags | (FreeFlags & HEAP_ENTRY_LAST_ENTRY);
+ BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
+ }
+ else {
+ if (AllocationIndex < (HEAP_MAXIMUM_FREELISTS * 1) / 4) {
+ FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 0 ];
+ FreeListsInUseUlong = *FreeListsInUse++ >> (AllocationIndex & 0x1F);
+ if (FreeListsInUseUlong) {
+ FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ FreeListsInUseUlong = *FreeListsInUse++;
+ if (FreeListsInUseUlong) {
+ FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 1) / 4) -
+ (AllocationIndex & 0x1F) +
+ RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ FreeListsInUseUlong = *FreeListsInUse++;
+ if (FreeListsInUseUlong) {
+ FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 2) / 4) -
+ (AllocationIndex & 0x1F) +
+ RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ FreeListsInUseUlong = *FreeListsInUse++;
+ if (FreeListsInUseUlong) {
+ FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 3) / 4) -
+ (AllocationIndex & 0x1F) +
+ RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ goto LookInNonDedicatedList;
+ }
+ }
+ }
+ }
+ }
+ else
+ if (AllocationIndex < (HEAP_MAXIMUM_FREELISTS * 2) / 4) {
+ FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 1 ];
+ FreeListsInUseUlong = *FreeListsInUse++ >> (AllocationIndex & 0x1F);
+ if (FreeListsInUseUlong) {
+ FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ FreeListsInUseUlong = *FreeListsInUse++;
+ if (FreeListsInUseUlong) {
+ FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 1) / 4) -
+ (AllocationIndex & 0x1F) +
+ RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ FreeListsInUseUlong = *FreeListsInUse++;
+ if (FreeListsInUseUlong) {
+ FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 2) / 4) -
+ (AllocationIndex & 0x1F) +
+ RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ goto LookInNonDedicatedList;
+ }
+ }
+ }
+ }
+ else
+ if (AllocationIndex < (HEAP_MAXIMUM_FREELISTS * 3) / 4) {
+ FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 2 ];
+ FreeListsInUseUlong = *FreeListsInUse++ >> (AllocationIndex & 0x1F);
+ if (FreeListsInUseUlong) {
+ FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ FreeListsInUseUlong = *FreeListsInUse++;
+ if (FreeListsInUseUlong) {
+ FreeListHead += ((HEAP_MAXIMUM_FREELISTS * 1) / 4) -
+ (AllocationIndex & 0x1F) +
+ RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ goto LookInNonDedicatedList;
+ }
+ }
+ }
+ else {
+ FreeListsInUse = &Heap->u.FreeListsInUseUlong[ 3 ];
+ FreeListsInUseUlong = *FreeListsInUse++ >> (AllocationIndex & 0x1F);
+ if (FreeListsInUseUlong) {
+ FreeListHead += RtlFindFirstSetRightMember( FreeListsInUseUlong );
+ }
+ else {
+ goto LookInNonDedicatedList;
+ }
+ }
+
+ FreeBlock = CONTAINING_RECORD( FreeListHead->Flink,
+ HEAP_FREE_ENTRY,
+ FreeList
+ );
+SplitFreeBlock:
+ FreeFlags = FreeBlock->Flags;
+ RtlpRemoveFreeBlock( Heap, FreeBlock );
+ Heap->TotalFreeSize -= FreeBlock->Size;
+
+ BusyBlock = (PHEAP_ENTRY)FreeBlock;
+ BusyBlock->Flags = EntryFlags;
+ FreeSize = BusyBlock->Size - AllocationIndex;
+ BusyBlock->Size = (USHORT)AllocationIndex;
+ BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
+ if (FreeSize != 0) {
+ if (FreeSize == 1) {
+ BusyBlock->Size += 1;
+ BusyBlock->UnusedBytes += sizeof( HEAP_ENTRY );
+ }
+ else {
+ SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
+ SplitBlock->Flags = FreeFlags;
+ SplitBlock->PreviousSize = (USHORT)AllocationIndex;
+ SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
+ if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
+ SplitBlock2->PreviousSize = (USHORT)FreeSize;
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ SplitBlock->Flags = SplitBlock2->Flags;
+ RtlpRemoveFreeBlock( Heap, SplitBlock2 );
+ Heap->TotalFreeSize -= SplitBlock2->Size;
+ FreeSize += SplitBlock2->Size;
+ if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
+ }
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
+ }
+ }
+ }
+
+ FreeFlags = 0;
+ }
+ }
+
+ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
+ BusyBlock->Flags |= HEAP_ENTRY_LAST_ENTRY;
+ }
+ }
+
+ ReturnValue = BusyBlock + 1;
+ if (Flags & HEAP_ZERO_MEMORY) {
+ ZeroSize = Size;
+ }
+ else
+ if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
+ RtlFillMemoryUlong( (PCHAR)(BusyBlock + 1), Size & ~0x3, ALLOC_HEAP_FILL );
+ }
+
+ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
+ RtlFillMemory( (PCHAR)ReturnValue + Size,
+ CHECK_HEAP_TAIL_SIZE,
+ CHECK_HEAP_TAIL_FILL
+ );
+
+ BusyBlock->Flags |= HEAP_ENTRY_FILL_PATTERN;
+ }
+
+ BusyBlock->SmallTagIndex = 0;
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ ExtraStuff->ZeroInit = 0;
+#ifndef NTOS_KERNEL_RUNTIME
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ ExtraStuff->TagIndex = RtlpUpdateTagEntry( Heap,
+ (USHORT)((Flags & HEAP_TAG_MASK) >> HEAP_TAG_SHIFT),
+ 0,
+ BusyBlock->Size,
+ AllocationAction
+ );
+ }
+ }
+ else
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ BusyBlock->SmallTagIndex = (UCHAR)RtlpUpdateTagEntry( Heap,
+ (USHORT)((Flags & HEAP_SMALL_TAG_MASK) >> HEAP_TAG_SHIFT),
+ 0,
+ BusyBlock->Size,
+ AllocationAction
+ );
+#endif // NTOS_KERNEL_RUNTIME
+ }
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpAllocHeapEventId,
+ Heap->EventLogMask,
+ Heap,
+ Flags,
+ Size,
+ ReturnValue
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ //
+ // Return the address of the user portion of the allocated block.
+ // This is the byte following the header.
+ //
+
+ leave;
+ }
+ else
+ if (AllocationIndex <= Heap->VirtualMemoryThreshold) {
+LookInNonDedicatedList:
+ FreeListHead = &Heap->FreeLists[ 0 ];
+ Next = FreeListHead->Flink;
+ while (FreeListHead != Next) {
+ FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
+ if (FreeBlock->Size >= AllocationIndex) {
+ goto SplitFreeBlock;
+ }
+ else {
+ Next = Next->Flink;
+ }
+ }
+
+ FreeBlock = RtlpExtendHeap( Heap, AllocationSize );
+ if (FreeBlock != NULL) {
+ goto SplitFreeBlock;
+ }
+
+ Status = STATUS_NO_MEMORY;
+ }
+ else
+ if (Heap->Flags & HEAP_GROWABLE) {
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ VirtualAllocBlock = NULL;
+ AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&VirtualAllocBlock,
+ 0,
+ &AllocationSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (NT_SUCCESS( Status )) {
+ //
+ // Just committed, already zero.
+ //
+ VirtualAllocBlock->BusyBlock.Size = (USHORT)(AllocationSize - Size);
+ VirtualAllocBlock->BusyBlock.Flags = EntryFlags | HEAP_ENTRY_VIRTUAL_ALLOC | HEAP_ENTRY_EXTRA_PRESENT;
+ VirtualAllocBlock->CommitSize = AllocationSize;
+ VirtualAllocBlock->ReserveSize = AllocationSize;
+#ifndef NTOS_KERNEL_RUNTIME
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ VirtualAllocBlock->ExtraStuff.TagIndex =
+ RtlpUpdateTagEntry( Heap,
+ (USHORT)((Flags & HEAP_SMALL_TAG_MASK) >> HEAP_TAG_SHIFT),
+ 0,
+ VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT,
+ VirtualAllocationAction
+ );
+ }
+#endif // NTOS_KERNEL_RUNTIME
+ InsertTailList( &Heap->VirtualAllocdBlocks, (PLIST_ENTRY)VirtualAllocBlock );
+
+ //
+ // Return the address of the user portion of the allocated block.
+ // This is the byte following the header.
+ //
+
+ ReturnValue = (PHEAP_ENTRY)(VirtualAllocBlock + 1);
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpAllocHeapEventId,
+ Heap->EventLogMask,
+ Heap,
+ Flags,
+ Size,
+ ReturnValue
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ leave;
+ }
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ SET_LAST_STATUS( Status );
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpAllocHeapEventId,
+ Heap->EventLogMask,
+ Heap,
+ Flags,
+ Size,
+ NULL
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ //
+ // Release the free list lock if held
+ //
+
+ if (LockAcquired) {
+ LockAcquired = FALSE;
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ if (Flags & HEAP_GENERATE_EXCEPTIONS) {
+ //
+ // Construct an exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 1;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.ExceptionInformation[ 0 ] = AllocationSize;
+ RtlRaiseException( &ExceptionRecord );
+ }
+ }
+ except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH :
+ EXCEPTION_EXECUTE_HANDLER
+ ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ //
+ // Release the free list lock if held
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ if ( ZeroSize ) {
+ RtlZeroMemory( ReturnValue, ZeroSize );
+ }
+
+ return ReturnValue;
+}
+
+
+BOOLEAN
+RtlFreeHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ )
+{
+ NTSTATUS Status;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ ULONG FreeSize;
+
+ RTL_PAGED_CODE();
+
+ if ( BaseAddress != NULL ) {
+
+ Flags |= Heap->ForceFlags;
+
+ if ( ! ( Flags & HEAP_SLOW_FLAGS )) {
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+
+ //
+ // Protect ourselves from idiots by refusing to free blocks
+ // that do not have the busy bit set.
+ //
+ // Also refuse to free blocks that are not eight-byte aligned.
+ // The specific idiot in this case is Office95, which likes
+ // to free a random pointer when you start Word95 from a desktop
+ // shortcut.
+ //
+ // As further insurance against idiots, check the segment index
+ // to make sure it is less than HEAP_MAXIMUM_SEGMENTS (16). This
+ // should fix all the dorks who have ASCII or Unicode where the
+ // heap header is supposed to be.
+ //
+
+ if ((BusyBlock->Flags & HEAP_ENTRY_BUSY) &&
+ (((ULONG)BaseAddress & 0x7) == 0) &&
+ (BusyBlock->SegmentIndex < HEAP_MAXIMUM_SEGMENTS)) {
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ }
+
+ if (!(BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC)) {
+ FreeSize = BusyBlock->Size;
+#ifdef NTOS_KERNEL_RUNTIME
+ BusyBlock = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks( Heap,
+ (PHEAP_FREE_ENTRY)BusyBlock,
+ &FreeSize,
+ FALSE );
+#else
+ if (!(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)) {
+ BusyBlock = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks( Heap,
+ (PHEAP_FREE_ENTRY)BusyBlock,
+ &FreeSize,
+ FALSE );
+ }
+#endif
+ //
+ // Check for a small allocation that can go on a freelist
+ // first, these should never trigger a decommit.
+ //
+ HEAPASSERT(HEAP_MAXIMUM_FREELISTS < Heap->DeCommitFreeBlockThreshold);
+ if (FreeSize < HEAP_MAXIMUM_FREELISTS) {
+ RtlpFastInsertDedicatedFreeBlockDirect( Heap,
+ (PHEAP_FREE_ENTRY)BusyBlock,
+ (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ if (!(BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ HEAPASSERT((BusyBlock + FreeSize)->PreviousSize == (USHORT)FreeSize);
+ }
+ } else if ((FreeSize < Heap->DeCommitFreeBlockThreshold) ||
+ ((Heap->TotalFreeSize + FreeSize) < Heap->DeCommitTotalFreeThreshold)) {
+ if (FreeSize <= (ULONG)HEAP_MAXIMUM_BLOCK_SIZE) {
+ RtlpFastInsertNonDedicatedFreeBlockDirect( Heap,
+ (PHEAP_FREE_ENTRY)BusyBlock,
+ (USHORT)FreeSize );
+ if (!(BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ HEAPASSERT((BusyBlock + FreeSize)->PreviousSize == (USHORT)FreeSize);
+ }
+ Heap->TotalFreeSize += FreeSize;
+ } else {
+ RtlpInsertFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
+ }
+
+ } else {
+ RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+ } else {
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ RemoveEntryList( &VirtualAllocBlock->Entry );
+
+ //
+ // Release lock here as there is no reason to hold it across
+ // the system call.
+ //
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ FreeSize = 0;
+ Status = ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&VirtualAllocBlock,
+ &FreeSize,
+ MEM_RELEASE
+ );
+ if (!NT_SUCCESS( Status )) {
+ SET_LAST_STATUS( Status );
+ return(FALSE);
+ }
+ }
+
+ return(TRUE);
+
+ } else {
+
+ //
+ // Not a busy block, fail the call.
+ //
+
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+ return(FALSE);
+ }
+
+ } else {
+
+ //
+ // Call the do-everything allocator.
+ //
+
+ return(RtlFreeHeapSlowly(HeapHandle, Flags, BaseAddress));
+ }
+
+ } else {
+
+ //
+ // BaseAddress is NULL, just return success
+ //
+
+ return(TRUE);
+ }
+
+} // RtlFreeHeap
+
+
+BOOLEAN
+RtlFreeHeapSlowly(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ )
+{
+ NTSTATUS Status;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ ULONG FreeSize;
+ BOOLEAN Result, LockAcquired;
+#ifndef NTOS_KERNEL_RUNTIME
+ USHORT TagIndex;
+#endif // NTOS_KERNEL_RUNTIME
+
+ RTL_PAGED_CODE();
+
+ //
+ // Note that Flags has already been OR'd with Heap->ForceFlags.
+ //
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugFreeHeap( HeapHandle, Flags, BaseAddress );
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ Result = FALSE;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ try {
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+
+ if ((BusyBlock->Flags & HEAP_ENTRY_BUSY) &&
+ (((ULONG)BaseAddress & 0x7) == 0) &&
+ (BusyBlock->SegmentIndex < HEAP_MAXIMUM_SEGMENTS)) {
+
+ if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ RemoveEntryList( &VirtualAllocBlock->Entry );
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ RtlpUpdateTagEntry( Heap,
+ VirtualAllocBlock->ExtraStuff.TagIndex,
+ VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT,
+ 0,
+ VirtualFreeAction
+ );
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ FreeSize = 0;
+ Status = ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&VirtualAllocBlock,
+ &FreeSize,
+ MEM_RELEASE
+ );
+ if (NT_SUCCESS( Status )) {
+ Result = TRUE;
+ }
+ else {
+ SET_LAST_STATUS( Status );
+ }
+ }
+ else {
+#ifndef NTOS_KERNEL_RUNTIME
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
+ TagIndex = RtlpUpdateTagEntry( Heap,
+ ExtraStuff->TagIndex,
+ BusyBlock->Size,
+ 0,
+ FreeAction
+ );
+ }
+ else {
+ TagIndex = RtlpUpdateTagEntry( Heap,
+ BusyBlock->SmallTagIndex,
+ BusyBlock->Size,
+ 0,
+ FreeAction
+ );
+ }
+ }
+ else {
+ TagIndex = 0;
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ FreeSize = BusyBlock->Size;
+#ifndef NTOS_KERNEL_RUNTIME
+ if (!(Heap->Flags & HEAP_DISABLE_COALESCE_ON_FREE)) {
+#endif // NTOS_KERNEL_RUNTIME
+ BusyBlock = (PHEAP_ENTRY)RtlpCoalesceFreeBlocks( Heap, (PHEAP_FREE_ENTRY)BusyBlock, &FreeSize, FALSE );
+#ifndef NTOS_KERNEL_RUNTIME
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ if (FreeSize < Heap->DeCommitFreeBlockThreshold ||
+ (Heap->TotalFreeSize + FreeSize) < Heap->DeCommitTotalFreeThreshold
+ ) {
+ if (FreeSize <= (ULONG)HEAP_MAXIMUM_BLOCK_SIZE) {
+ RtlpInsertFreeBlockDirect( Heap, (PHEAP_FREE_ENTRY)BusyBlock, (USHORT)FreeSize );
+ if (!(BusyBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ HEAPASSERT((BusyBlock + FreeSize)->PreviousSize == (USHORT)FreeSize);
+ }
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
+ }
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (TagIndex != 0) {
+ PHEAP_FREE_ENTRY_EXTRA FreeExtra;
+
+ BusyBlock->Flags |= HEAP_ENTRY_EXTRA_PRESENT;
+ FreeExtra = (PHEAP_FREE_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size) - 1;
+ FreeExtra->TagIndex = TagIndex;
+ FreeExtra->FreeBackTraceIndex = 0;
+#if i386
+ if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
+ FreeExtra->FreeBackTraceIndex = (USHORT)RtlLogStackBackTrace();
+ }
+#endif // i386
+ }
+#endif // NTOS_KERNEL_RUNTIME
+ }
+ else {
+ RtlpDeCommitFreeBlock( Heap, (PHEAP_FREE_ENTRY)BusyBlock, FreeSize );
+ }
+
+ Result = TRUE;
+ }
+ }
+ else {
+
+ //
+ // Not a busy block, fail the call.
+ //
+
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+ }
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpFreeHeapEventId,
+ Heap->EventLogMask,
+ Heap,
+ Flags,
+ BaseAddress,
+ Result
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ Result = FALSE;
+ }
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+} // RtlFreeHeap
+
+
+
+PHEAP_ENTRY_EXTRA
+RtlpGetExtraStuffPointer(
+ PHEAP_ENTRY BusyBlock
+ )
+{
+ ULONG AllocationIndex;
+
+ if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ return &VirtualAllocBlock->ExtraStuff;
+ }
+ else {
+ AllocationIndex = BusyBlock->Size;
+ return (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
+ }
+}
+
+
+ULONG
+RtlpGetSizeOfBigBlock(
+ IN PHEAP_ENTRY BusyBlock
+ )
+{
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ RTL_PAGED_CODE();
+
+ VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ return VirtualAllocBlock->CommitSize - BusyBlock->Size;
+}
+
+
+BOOLEAN
+RtlpCheckBusyBlockTail(
+ IN PHEAP_ENTRY BusyBlock
+ )
+{
+ PCHAR Tail;
+ ULONG Size, cbEqual;
+
+ RTL_PAGED_CODE();
+
+ if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ Size = RtlpGetSizeOfBigBlock( BusyBlock );
+ }
+ else {
+ Size = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) - BusyBlock->UnusedBytes;
+ }
+
+ Tail = (PCHAR)(BusyBlock + 1) + Size;
+ cbEqual = RtlCompareMemory( Tail,
+ CheckHeapFillPattern,
+ CHECK_HEAP_TAIL_SIZE
+ );
+ if (cbEqual != CHECK_HEAP_TAIL_SIZE) {
+ HeapDebugPrint(( "Heap block at %lx modified at %lx past requested size of %lx\n",
+ BusyBlock,
+ Tail + cbEqual,
+ Size
+ ));
+ HeapDebugBreak( BusyBlock );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+NTSTATUS
+RtlZeroHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ NTSTATUS Status;
+ BOOLEAN LockAcquired;
+ PHEAP_SEGMENT Segment;
+ ULONG SegmentIndex;
+ PHEAP_ENTRY CurrentBlock;
+ PHEAP_FREE_ENTRY FreeBlock;
+ ULONG Size;
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
+
+ RTL_PAGED_CODE();
+
+ Flags |= Heap->ForceFlags;
+
+#ifndef NTOS_KERNEL_RUNTIME
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugZeroHeap( HeapHandle, Flags );
+ }
+#endif // NTOS_KERNEL_RUNTIME
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ try { try {
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (!Segment) {
+ continue;
+ }
+
+ UnCommittedRange = Segment->UnCommittedRanges;
+ CurrentBlock = Segment->FirstEntry;
+ while (CurrentBlock < Segment->LastValidEntry) {
+ Size = CurrentBlock->Size << HEAP_GRANULARITY_SHIFT;
+ if (!(CurrentBlock->Flags & HEAP_ENTRY_BUSY)) {
+ FreeBlock = (PHEAP_FREE_ENTRY)CurrentBlock;
+ if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED &&
+ CurrentBlock->Flags & HEAP_ENTRY_FILL_PATTERN
+ ) {
+ RtlFillMemoryUlong( FreeBlock + 1,
+ Size - sizeof( *FreeBlock ),
+ FREE_HEAP_FILL
+ );
+ }
+ else {
+ RtlFillMemoryUlong( FreeBlock + 1,
+ Size - sizeof( *FreeBlock ),
+ 0
+ );
+ }
+ }
+
+ if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
+ CurrentBlock += CurrentBlock->Size;
+ if (UnCommittedRange == NULL) {
+ CurrentBlock = Segment->LastValidEntry;
+ }
+ else {
+ CurrentBlock = (PHEAP_ENTRY)
+ ((PCHAR)UnCommittedRange->Address + UnCommittedRange->Size);
+ UnCommittedRange = UnCommittedRange->Next;
+ }
+ }
+ else {
+ CurrentBlock += CurrentBlock->Size;
+ }
+ }
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ Status = GetExceptionCode();
+ }
+ } finally {
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+ }
+
+ return Status;
+}
diff --git a/private/ntos/rtl/heapdbg.c b/private/ntos/rtl/heapdbg.c
new file mode 100644
index 000000000..26bdce151
--- /dev/null
+++ b/private/ntos/rtl/heapdbg.c
@@ -0,0 +1,1883 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ heapdbg.c
+
+Abstract:
+
+ This module implements a debugging layer on top of heap allocator.
+
+Author:
+
+ Steve Wood (stevewo) 20-Sep-1994
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "heap.h"
+#include "heappriv.h"
+
+BOOLEAN RtlpValidateHeapHdrsEnable = FALSE; // Set to TRUE if headers are being corrupted
+BOOLEAN RtlpValidateHeapTagsEnable; // Set to TRUE if tag counts are off and you want to know why
+
+HEAP_STOP_ON_VALUES RtlpHeapStopOn;
+
+VOID
+RtlpUpdateHeapListIndex(
+ USHORT OldIndex,
+ USHORT NewIndex
+ )
+{
+ if (RtlpHeapStopOn.AllocTag.HeapIndex == OldIndex) {
+ RtlpHeapStopOn.AllocTag.HeapIndex = NewIndex;
+ }
+
+ if (RtlpHeapStopOn.ReAllocTag.HeapIndex == OldIndex) {
+ RtlpHeapStopOn.ReAllocTag.HeapIndex = NewIndex;
+ }
+
+ if (RtlpHeapStopOn.FreeTag.HeapIndex == OldIndex) {
+ RtlpHeapStopOn.FreeTag.HeapIndex = NewIndex;
+ }
+
+ return;
+}
+
+struct {
+ ULONG Offset;
+ LPSTR Description;
+} RtlpHeapHeaderFieldOffsets[] = {
+ FIELD_OFFSET( HEAP, Entry ), "Entry",
+ FIELD_OFFSET( HEAP, Signature ), "Signature",
+ FIELD_OFFSET( HEAP, Flags ), "Flags",
+ FIELD_OFFSET( HEAP, ForceFlags ), "ForceFlags",
+ FIELD_OFFSET( HEAP, VirtualMemoryThreshold ), "VirtualMemoryThreshold",
+ FIELD_OFFSET( HEAP, SegmentReserve ), "SegmentReserve",
+ FIELD_OFFSET( HEAP, SegmentCommit ), "SegmentCommit",
+ FIELD_OFFSET( HEAP, DeCommitFreeBlockThreshold ), "DeCommitFreeBlockThreshold",
+ FIELD_OFFSET( HEAP, DeCommitTotalFreeThreshold ), "DeCommitTotalFreeThreshold",
+ FIELD_OFFSET( HEAP, TotalFreeSize ), "TotalFreeSize",
+ FIELD_OFFSET( HEAP, MaximumAllocationSize ), "MaximumAllocationSize",
+ FIELD_OFFSET( HEAP, ProcessHeapsListIndex ), "ProcessHeapsListIndex",
+ FIELD_OFFSET( HEAP, HeaderValidateLength ), "HeaderValidateLength",
+ FIELD_OFFSET( HEAP, HeaderValidateCopy ), "HeaderValidateCopy",
+ FIELD_OFFSET( HEAP, NextAvailableTagIndex ), "NextAvailableTagIndex",
+ FIELD_OFFSET( HEAP, MaximumTagIndex ), "MaximumTagIndex",
+ FIELD_OFFSET( HEAP, TagEntries ), "TagEntries",
+ FIELD_OFFSET( HEAP, UCRSegments ), "UCRSegments",
+ FIELD_OFFSET( HEAP, UnusedUnCommittedRanges ), "UnusedUnCommittedRanges",
+ FIELD_OFFSET( HEAP, AlignRound ), "AlignRound",
+ FIELD_OFFSET( HEAP, AlignMask ), "AlignMask",
+ FIELD_OFFSET( HEAP, VirtualAllocdBlocks ), "VirtualAllocdBlocks",
+ FIELD_OFFSET( HEAP, Segments ), "Segments",
+ FIELD_OFFSET( HEAP, u ), "FreeListsInUse",
+ FIELD_OFFSET( HEAP, FreeListsInUseTerminate ), "FreeListsInUseTerminate",
+ FIELD_OFFSET( HEAP, AllocatorBackTraceIndex ), "AllocatorBackTraceIndex",
+ FIELD_OFFSET( HEAP, TraceBuffer ), "TraceBuffer",
+ FIELD_OFFSET( HEAP, EventLogMask ), "EventLogMask",
+ FIELD_OFFSET( HEAP, PseudoTagEntries ), "PseudoTagEntries",
+ FIELD_OFFSET( HEAP, FreeLists ), "FreeLists",
+ FIELD_OFFSET( HEAP, LockVariable ), "LockVariable",
+ FIELD_OFFSET( HEAP, Reserved ), "Reserved",
+ sizeof( HEAP ), "Uncommitted Ranges",
+ 0xFFFF, NULL
+};
+
+BOOLEAN
+RtlpValidateHeapHeaders(
+ IN PHEAP Heap,
+ IN BOOLEAN Recompute
+ )
+{
+ ULONG i, n, nEqual;
+ NTSTATUS Status;
+
+ if (!RtlpValidateHeapHdrsEnable) {
+ return TRUE;
+ }
+
+ if (Heap->HeaderValidateCopy == NULL) {
+ n = Heap->HeaderValidateLength;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &Heap->HeaderValidateCopy,
+ 0,
+ &n,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return TRUE;
+ }
+
+ Recompute = TRUE;
+ }
+
+ n = Heap->HeaderValidateLength;
+ if (!Recompute) {
+ nEqual = RtlCompareMemory( Heap,
+ Heap->HeaderValidateCopy,
+ n
+ );
+ }
+ else {
+ RtlMoveMemory( Heap->HeaderValidateCopy,
+ Heap,
+ n
+ );
+ nEqual = n;
+ }
+
+ if (n != nEqual) {
+ HeapDebugPrint(( "Heap %x - headers modified (%x is %x instead of %x)\n",
+ Heap,
+ (PCHAR)Heap + nEqual,
+ *(PULONG)((PCHAR)Heap + nEqual),
+ *(PULONG)((PCHAR)Heap->HeaderValidateCopy + nEqual)
+ ));
+ for (i=0; RtlpHeapHeaderFieldOffsets[ i ].Description != NULL; i++) {
+ if (nEqual >= RtlpHeapHeaderFieldOffsets[ i ].Offset &&
+ nEqual < RtlpHeapHeaderFieldOffsets[ i+1 ].Offset
+ ) {
+ DbgPrint( " This is located in the %s field of the heap header.\n",
+ RtlpHeapHeaderFieldOffsets[ i ].Description
+ );
+ break;
+ }
+ }
+
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+PRTL_TRACE_BUFFER
+RtlpHeapCreateTraceBuffer(
+ IN PHEAP Heap
+ )
+{
+ if (Heap->Flags & HEAP_CREATE_ENABLE_TRACING) {
+ if (Heap->TraceBuffer == NULL) {
+ Heap->TraceBuffer = RtlCreateTraceBuffer( 0x10000, HEAP_TRACE_MAX_EVENT );
+ if (Heap->TraceBuffer != NULL) {
+ DbgPrint( "Created Trace buffer (%x) for heap (%x)\n", Heap->TraceBuffer, Heap );
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_ALLOC ] = "Alloc - Result: %08x Flags: %04x Size: %08x ";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_REALLOC ] = "ReAlloc - Block: %08x Flags: %04x Size: %08x Result: %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_FREE ] = "Free - Block: %08x Flags: %04x Size: %08x Result: %02x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_SIZE ] = "Size - Block: %08x Flags: %04x Result: %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_GET_INFO ] = "GetInfo - Block: %08x Flags: %04x Value: %08x UserFlags: %08x Result: %u";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_SET_VALUE ] = "SetValue - Block: %08x Flags: %04x Value: %08x Result: %u";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_SET_FLAGS ] = "SetFlags - Block: %08x Flags: %04x Reset: %02x Set: %02x Result: %u";
+#if DBG
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COMMIT_MEMORY ] = " Commit VA - %08x %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COMMIT_INSERT ] = " Commit Insert - %08x %08x %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COMMIT_NEW_ENTRY ] = " Commit NewEntry - %08x %08x %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_INSERT_FREE_BLOCK ] = " Insert Free - %08x %08x %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_UNLINK_FREE_BLOCK ] = " Unlink Free - %08x %08x %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_COALESCE_FREE_BLOCKS ] = " Coalesce Free - %08x %08x %08x %08x %08x %08x %08x";
+ Heap->TraceBuffer->EventIdFormatString[ HEAP_TRACE_EXTEND_HEAP ] = " Extended Heap - Size: %08x Pages: %08x Commit: %08x";
+#endif // DBG
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ }
+ }
+ }
+
+ return Heap->TraceBuffer;
+}
+
+PVOID
+RtlDebugCreateHeap(
+ IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN ULONG ReserveSize OPTIONAL,
+ IN ULONG CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters
+ )
+{
+ PHEAP Heap;
+ NTSTATUS Status;
+ MEMORY_BASIC_INFORMATION MemoryInformation;
+
+ if (ReserveSize <= sizeof( HEAP_ENTRY )) {
+ HeapDebugPrint(( "Invalid ReserveSize parameter - %lx\n", ReserveSize ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ if (ReserveSize < CommitSize) {
+ HeapDebugPrint(( "Invalid CommitSize parameter - %lx\n", CommitSize ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ if ((Flags & HEAP_NO_SERIALIZE) && ARGUMENT_PRESENT( Lock )) {
+ HeapDebugPrint(( "May not specify Lock parameter with HEAP_NO_SERIALIZE\n" ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ if (ARGUMENT_PRESENT( HeapBase )) {
+ Status = NtQueryVirtualMemory( NtCurrentProcess(),
+ HeapBase,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ HeapDebugPrint(( "Specified HeapBase (%lx) invalid, Status = %lx\n",
+ HeapBase,
+ Status
+ ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ if (MemoryInformation.BaseAddress != HeapBase) {
+ HeapDebugPrint(( "Specified HeapBase (%lx) != to BaseAddress (%lx)\n",
+ HeapBase,
+ MemoryInformation.BaseAddress
+ ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ if (MemoryInformation.State == MEM_FREE) {
+ HeapDebugPrint(( "Specified HeapBase (%lx) is free or not writable\n",
+ MemoryInformation.BaseAddress
+ ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+ }
+
+ Heap = RtlCreateHeap( Flags |
+ HEAP_SKIP_VALIDATION_CHECKS |
+ HEAP_TAIL_CHECKING_ENABLED |
+ HEAP_FREE_CHECKING_ENABLED,
+ HeapBase,
+ ReserveSize,
+ CommitSize,
+ Lock,
+ Parameters
+ );
+ if (Heap != NULL) {
+#if i386
+ if ((NtGlobalFlag & FLG_USER_STACK_TRACE_DB)) {
+ Heap->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
+ }
+#endif // i386
+
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ }
+
+ return Heap;
+}
+
+
+
+BOOLEAN
+RtlpSerializeHeap(
+ IN PVOID HeapHandle
+ )
+{
+ NTSTATUS Status;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_LOCK Lock;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapSerialize( HeapHandle )
+ );
+
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlpSerializeHeap" )) {
+ return FALSE;
+ }
+
+ //
+ // Lock the heap.
+ //
+
+ if (Heap->Flags & HEAP_NO_SERIALIZE) {
+ Lock = RtlAllocateHeap( HeapHandle, HEAP_NO_SERIALIZE, sizeof( *Lock ) );
+ Status = RtlInitializeLockRoutine( Lock );
+ if (!NT_SUCCESS( Status )) {
+ RtlFreeHeap( HeapHandle, HEAP_NO_SERIALIZE, Lock );
+ return FALSE;
+ }
+
+ Heap->LockVariable = Lock;
+ Heap->Flags &= ~HEAP_NO_SERIALIZE;
+ Heap->ForceFlags &= ~HEAP_NO_SERIALIZE;
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ }
+
+ return TRUE;
+}
+
+
+
+BOOLEAN
+RtlDebugDestroyHeap(
+ IN PVOID HeapHandle
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ LIST_ENTRY ListEntry;
+ ULONG n;
+
+ if (HeapHandle == NtCurrentPeb()->ProcessHeap) {
+ HeapDebugPrint(( "May not destroy the process heap at %x\n", HeapHandle ));
+ return FALSE;
+ }
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlDestroyHeap" )) {
+ return FALSE;
+ }
+
+ if (!RtlpValidateHeap( Heap, FALSE )) {
+ return FALSE;
+ }
+
+ //
+ // Now mark the heap as invalid by zeroing the signature field.
+ //
+
+ Heap->Signature = 0;
+
+ if (Heap->HeaderValidateCopy != NULL) {
+ n = 0;
+ NtFreeVirtualMemory( NtCurrentProcess(),
+ &Heap->HeaderValidateCopy,
+ &n,
+ MEM_RELEASE
+ );
+ }
+
+ return TRUE;
+}
+
+
+PVOID
+RtlDebugAllocateHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ PVOID ReturnValue;
+ ULONG AllocationSize;
+ USHORT TagIndex;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapAllocate( HeapHandle, Flags, Size )
+ );
+
+ LockAcquired = FALSE;
+
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlAllocateHeap" )) {
+ return NULL;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Verify that the size did not wrap or exceed the limit for this heap.
+ //
+
+ AllocationSize = (((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask) +
+ sizeof( HEAP_ENTRY_EXTRA );
+ if (AllocationSize < Size || AllocationSize > Heap->MaximumAllocationSize) {
+ HeapDebugPrint(( "Invalid allocation size - %lx (exceeded %x)\n",
+ Size,
+ Heap->MaximumAllocationSize
+ ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+
+
+ ReturnValue = RtlAllocateHeapSlowly( HeapHandle, Flags, Size );
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_ALLOC,
+ 3,
+ ReturnValue,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ Size
+ )
+ );
+
+ RtlpValidateHeapHeaders( Heap, TRUE );
+
+ if (ReturnValue != NULL) {
+ BusyBlock = (PHEAP_ENTRY)ReturnValue - 1;
+
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+#if i386
+ if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
+ ExtraStuff->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
+ }
+ else {
+ ExtraStuff->AllocatorBackTraceIndex = 0;
+ }
+#endif // i386
+ TagIndex = ExtraStuff->TagIndex;
+ }
+ else {
+ TagIndex = BusyBlock->SmallTagIndex;
+ }
+
+ if (Heap->Flags & HEAP_VALIDATE_ALL_ENABLED) {
+ RtlpValidateHeap( Heap, FALSE );
+ }
+ }
+
+ if (LockAcquired) {
+ LockAcquired = FALSE;
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ if (ReturnValue != NULL) {
+ if ((ULONG)ReturnValue == RtlpHeapStopOn.AllocAddress) {
+ HeapDebugPrint(( "Just allocated block at %lx for 0x%x bytes\n",
+ RtlpHeapStopOn.AllocAddress,
+ Size
+ ));
+ HeapDebugBreak( NULL );
+ }
+ else
+ if (IS_HEAP_TAGGING_ENABLED() && TagIndex != 0 &&
+ TagIndex == RtlpHeapStopOn.AllocTag.TagIndex &&
+ Heap->ProcessHeapsListIndex == RtlpHeapStopOn.AllocTag.HeapIndex
+ ) {
+ HeapDebugPrint(( "Just allocated block at %lx for 0x%x bytes with tag %ws\n",
+ ReturnValue,
+ Size,
+ RtlpGetTagName( Heap, TagIndex )
+ ));
+ HeapDebugBreak( NULL );
+ }
+ }
+ }
+ except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH :
+ EXCEPTION_EXECUTE_HANDLER
+ ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ ReturnValue = NULL;
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return ReturnValue;
+}
+
+
+PVOID
+RtlDebugReAllocateHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN ULONG Size
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ ULONG AllocationSize;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ BOOLEAN LockAcquired;
+ PVOID ReturnValue;
+ USHORT TagIndex;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapReAllocate( HeapHandle, Flags, BaseAddress, Size )
+ );
+
+ ReturnValue = NULL;
+ LockAcquired = FALSE;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlReAllocateHeap" )) {
+ return NULL;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SETTABLE_USER_VALUE | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Verify that the size did not wrap or exceed the limit for this heap.
+ //
+
+ AllocationSize = (((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask) +
+ sizeof( HEAP_ENTRY_EXTRA );
+ if (AllocationSize < Size || AllocationSize > Heap->MaximumAllocationSize) {
+ HeapDebugPrint(( "Invalid allocation size - %lx (exceeded %x)\n",
+ Size,
+ Heap->MaximumAllocationSize
+ ));
+ HeapDebugBreak( NULL );
+ return NULL;
+ }
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlReAllocateHeap" )) {
+ if ((ULONG)BaseAddress == RtlpHeapStopOn.ReAllocAddress) {
+ HeapDebugPrint(( "About to reallocate block at %lx to 0x%x bytes\n",
+ RtlpHeapStopOn.ReAllocAddress,
+ Size
+ ));
+ HeapDebugBreak( NULL );
+ }
+ else
+ if (IS_HEAP_TAGGING_ENABLED() && RtlpHeapStopOn.ReAllocTag.HeapAndTagIndex != 0) {
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ TagIndex = ExtraStuff->TagIndex;
+ }
+ else {
+ TagIndex = BusyBlock->SmallTagIndex;
+ }
+
+ if (TagIndex != 0 &&
+ TagIndex == RtlpHeapStopOn.ReAllocTag.TagIndex &&
+ Heap->ProcessHeapsListIndex == RtlpHeapStopOn.ReAllocTag.HeapIndex
+ ) {
+ HeapDebugPrint(( "About to rellocate block at %lx to 0x%x bytes with tag %ws\n",
+ BaseAddress,
+ Size,
+ RtlpGetTagName( Heap, TagIndex )
+ ));
+ HeapDebugBreak( NULL );
+ }
+ }
+
+ ReturnValue = RtlReAllocateHeap( HeapHandle, Flags, BaseAddress, Size );
+ if (ReturnValue != NULL) {
+ BusyBlock = (PHEAP_ENTRY)ReturnValue - 1;
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+#if i386
+ if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
+ ExtraStuff->AllocatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
+ }
+ else {
+ ExtraStuff->AllocatorBackTraceIndex = 0;
+ }
+#endif // i386
+ TagIndex = ExtraStuff->TagIndex;
+ }
+ else {
+ TagIndex = BusyBlock->SmallTagIndex;
+ }
+ }
+
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ RtlpValidateHeap( Heap, FALSE );
+ }
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_REALLOC,
+ 4,
+ BaseAddress,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ Size,
+ ReturnValue
+ )
+ );
+
+ if (ReturnValue != NULL) {
+ if ((ULONG)ReturnValue == RtlpHeapStopOn.ReAllocAddress) {
+ HeapDebugPrint(( "Just reallocated block at %lx to 0x%x bytes\n",
+ RtlpHeapStopOn.ReAllocAddress,
+ Size
+ ));
+ HeapDebugBreak( NULL );
+ }
+ else
+ if (IS_HEAP_TAGGING_ENABLED() &&
+ TagIndex == RtlpHeapStopOn.ReAllocTag.TagIndex &&
+ Heap->ProcessHeapsListIndex == RtlpHeapStopOn.ReAllocTag.HeapIndex
+
+ ) {
+ HeapDebugPrint(( "Just reallocated block at %lx to 0x%x bytes with tag %ws\n",
+ ReturnValue,
+ Size,
+ RtlpGetTagName( Heap, TagIndex )
+ ));
+ HeapDebugBreak( NULL );
+ }
+ }
+ }
+ except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH :
+ EXCEPTION_EXECUTE_HANDLER
+ ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ ReturnValue = NULL;
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return ReturnValue;
+}
+
+
+BOOLEAN
+RtlDebugFreeHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ ULONG Size;
+ BOOLEAN Result, LockAcquired;
+ USHORT TagIndex;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapFree( HeapHandle, Flags, BaseAddress )
+ );
+
+ LockAcquired = FALSE;
+ Result = FALSE;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlFreeHeap" )) {
+ return FALSE;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ Size = BusyBlock->Size << HEAP_GRANULARITY_SHIFT;
+ if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlFreeHeap" )) {
+ if ((ULONG)BaseAddress == RtlpHeapStopOn.FreeAddress) {
+ HeapDebugPrint(( "About to free block at %lx\n",
+ RtlpHeapStopOn.FreeAddress
+ ));
+ HeapDebugBreak( NULL );
+ }
+ else
+ if (IS_HEAP_TAGGING_ENABLED() && RtlpHeapStopOn.FreeTag.HeapAndTagIndex != 0) {
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ TagIndex = ExtraStuff->TagIndex;
+ }
+ else {
+ TagIndex = BusyBlock->SmallTagIndex;
+ }
+
+ if (TagIndex != 0 &&
+ TagIndex == RtlpHeapStopOn.FreeTag.TagIndex &&
+ Heap->ProcessHeapsListIndex == RtlpHeapStopOn.FreeTag.HeapIndex
+ ) {
+ HeapDebugPrint(( "About to free block at %lx with tag %ws\n",
+ BaseAddress,
+ RtlpGetTagName( Heap, TagIndex )
+ ));
+ HeapDebugBreak( NULL );
+ }
+ }
+
+ Result = RtlFreeHeapSlowly( HeapHandle, Flags, BaseAddress );
+
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ RtlpValidateHeap( Heap, FALSE );
+ }
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_FREE,
+ 4,
+ BaseAddress,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ Size,
+ (ULONG)Result
+ )
+ );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ Result = FALSE;
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+RtlDebugGetUserInfoHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ OUT PVOID *UserValue OPTIONAL,
+ OUT PULONG UserFlags OPTIONAL
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ BOOLEAN Result, LockAcquired;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapGetUserInfo( HeapHandle, Flags, BaseAddress, UserValue, UserFlags )
+ );
+
+ LockAcquired = FALSE;
+ Result = FALSE;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlGetUserInfoHeap" )) {
+ return FALSE;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlGetUserInfoHeap" )) {
+ Result = RtlGetUserInfoHeap( HeapHandle, Flags, BaseAddress, UserValue, UserFlags );
+ }
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_GET_INFO,
+ 5,
+ BaseAddress,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ ARGUMENT_PRESENT( UserValue ) ? *UserValue : 0,
+ ARGUMENT_PRESENT( UserFlags ) ? *UserFlags : 0,
+ (ULONG)Result
+ )
+ );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+RtlDebugSetUserValueHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN PVOID UserValue
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ BOOLEAN Result, LockAcquired;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapSetUserValue( HeapHandle, Flags, BaseAddress, UserValue )
+ );
+
+ LockAcquired = FALSE;
+ Result = FALSE;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlSetUserValueHeap" )) {
+ return FALSE;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_SET_VALUE,
+ 4,
+ BaseAddress,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ UserValue,
+ (ULONG)Result
+ )
+ );
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlSetUserValueHeap" )) {
+ Result = RtlSetUserValueHeap( HeapHandle, Flags, BaseAddress, UserValue );
+
+ RtlpValidateHeap( Heap, FALSE );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+RtlDebugSetUserFlagsHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN ULONG UserFlagsReset,
+ IN ULONG UserFlagsSet
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ BOOLEAN Result, LockAcquired;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapSetUserFlags( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet )
+ );
+
+ if (UserFlagsReset & ~HEAP_SETTABLE_USER_FLAGS ||
+ UserFlagsSet & ~HEAP_SETTABLE_USER_FLAGS
+ ) {
+ return FALSE;
+ }
+
+ LockAcquired = FALSE;
+ Result = FALSE;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlSetUserFlagsHeap" )) {
+ return FALSE;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_SET_FLAGS,
+ 5,
+ BaseAddress,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ UserFlagsReset,
+ UserFlagsSet,
+ (ULONG)TRUE
+ )
+ );
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlSetUserFlagsHeap" )) {
+ Result = RtlSetUserFlagsHeap( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet );
+
+ RtlpValidateHeap( Heap, FALSE );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+ULONG
+RtlDebugSizeHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ BOOLEAN LockAcquired;
+ ULONG BusySize;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapSize( HeapHandle, Flags, BaseAddress )
+ );
+
+ LockAcquired = FALSE;
+ BusySize = 0xFFFFFFFF;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlSizeHeap" )) {
+ return FALSE;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (RtlpValidateHeapEntry( Heap, BusyBlock, "RtlSizeHeap" )) {
+ BusySize = RtlSizeHeap( HeapHandle, Flags, BaseAddress );
+ }
+
+ HeapTrace( Heap, (Heap->TraceBuffer,
+ HEAP_TRACE_SIZE,
+ 3,
+ BaseAddress,
+ Flags & ~HEAP_SKIP_VALIDATION_CHECKS,
+ BusySize
+ )
+ );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return BusySize;
+}
+
+ULONG
+RtlDebugCompactHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ ULONG LargestFreeSize;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapCompact( HeapHandle, Flags )
+ );
+
+ LockAcquired = FALSE;
+ LargestFreeSize = 0;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlCompactHeap" )) {
+ return 0;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ RtlpValidateHeap( Heap, FALSE );
+
+ LargestFreeSize = RtlCompactHeap( HeapHandle, Flags );
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return LargestFreeSize;
+
+
+}
+
+NTSTATUS
+RtlDebugZeroHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ )
+{
+ NTSTATUS Status;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ ULONG LargestFreeSize;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapZero( HeapHandle, Flags )
+ );
+
+ Status = STATUS_SUCCESS;
+ LockAcquired = FALSE;
+ LargestFreeSize = 0;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlZeroHeap" )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ if (!RtlpValidateHeap( Heap, FALSE )) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ Status = RtlZeroHeap( HeapHandle, Flags );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ Status = GetExceptionCode();
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+RtlDebugCreateTagHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PWSTR TagPrefix OPTIONAL,
+ IN PWSTR TagNames
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ ULONG TagIndex;
+
+ LockAcquired = FALSE;
+ TagIndex = 0;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlCreateTagHeap" )) {
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ if (RtlpValidateHeap( Heap, FALSE )) {
+ TagIndex = RtlCreateTagHeap( HeapHandle, Flags, TagPrefix, TagNames );
+ }
+
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return TagIndex;
+}
+
+
+
+NTSYSAPI
+PWSTR
+NTAPI
+RtlDebugQueryTagHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN USHORT TagIndex,
+ IN BOOLEAN ResetCounters,
+ OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ PWSTR Result;
+
+ LockAcquired = FALSE;
+ Result = NULL;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlQueryTagHeap" )) {
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ if (RtlpValidateHeap( Heap, FALSE )) {
+ Result = RtlQueryTagHeap( HeapHandle, Flags, TagIndex, ResetCounters, TagInfo );
+ }
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+
+NTSTATUS
+RtlDebugUsageHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN OUT PRTL_HEAP_USAGE Usage
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ NTSTATUS Status;
+ BOOLEAN LockAcquired;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapUsage( HeapHandle, Flags, Usage )
+ );
+
+ LockAcquired = FALSE;
+ Status = STATUS_SUCCESS;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlUsageHeap" )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ Flags |= Heap->ForceFlags | HEAP_SKIP_VALIDATION_CHECKS;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ Flags |= HEAP_NO_SERIALIZE;
+ LockAcquired = TRUE;
+ }
+
+ if (!RtlpValidateHeap( Heap, FALSE )) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ Status = RtlUsageHeap( HeapHandle, Flags, Usage );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ Status = GetExceptionCode();
+ }
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Status;
+}
+
+BOOLEAN
+RtlDebugWalkHeap(
+ IN PVOID HeapHandle,
+ IN OUT PRTL_HEAP_WALK_ENTRY Entry
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN Result;
+
+ //
+ // Assumed the caller has serialized via RtlLockHeap or their own locking mechanism.
+ //
+
+ Result = FALSE;
+ try {
+ if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlWalkHeap" )) {
+ Result = RtlpValidateHeap( Heap, FALSE );
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ return Result;
+}
+
+BOOLEAN
+RtlpValidateHeapEntry(
+ IN PHEAP Heap,
+ IN PHEAP_ENTRY BusyBlock,
+ IN PCHAR Reason
+ )
+{
+ PHEAP_SEGMENT Segment;
+ UCHAR SegmentIndex;
+ BOOLEAN Result;
+
+ if ((BusyBlock == NULL) ||
+ ((ULONG)BusyBlock & (HEAP_GRANULARITY-1)) ||
+ ((BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) &&
+ (ULONG)BusyBlock & (PAGE_SIZE-1) != FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock )
+ ) ||
+ (!(BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) &&
+ ((BusyBlock->SegmentIndex >= HEAP_MAXIMUM_SEGMENTS) ||
+ !(Segment = Heap->Segments[ BusyBlock->SegmentIndex ]) ||
+ (BusyBlock < Segment->FirstEntry) ||
+ (BusyBlock >= Segment->LastValidEntry)
+ )
+ ) ||
+ !(BusyBlock->Flags & HEAP_ENTRY_BUSY) ||
+ (BusyBlock->Flags & HEAP_ENTRY_FILL_PATTERN && !RtlpCheckBusyBlockTail( BusyBlock ))
+ ) {
+InvalidBlock:
+ HeapDebugPrint(( "Invalid Address specified to %s( %lx, %lx )\n",
+ Reason,
+ Heap,
+ BusyBlock + 1
+ ));
+ HeapDebugBreak( BusyBlock );
+ return FALSE;
+ }
+ else {
+
+ if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ Result = TRUE;
+ }
+ else
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment) {
+ if (BusyBlock >= Segment->FirstEntry &&
+ BusyBlock < Segment->LastValidEntry
+ ) {
+ Result = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!Result) {
+ goto InvalidBlock;
+ }
+
+ return TRUE;
+ }
+}
+
+BOOLEAN
+RtlpValidateHeapSegment(
+ IN PHEAP Heap,
+ IN PHEAP_SEGMENT Segment,
+ IN UCHAR SegmentIndex,
+ IN OUT PULONG CountOfFreeBlocks,
+ IN OUT PULONG TotalFreeSize,
+ OUT PVOID *BadAddress,
+ IN OUT PULONG ComputedTagEntries,
+ IN OUT PULONG ComputedPseudoTagEntries
+ )
+{
+ PHEAP_ENTRY CurrentBlock, PreviousBlock;
+ ULONG Size;
+ USHORT PreviousSize, TagIndex;
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ ULONG NumberOfUnCommittedPages;
+ ULONG NumberOfUnCommittedRanges;
+
+ RTL_PAGED_CODE();
+
+ NumberOfUnCommittedPages = 0;
+ NumberOfUnCommittedRanges = 0;
+ UnCommittedRange = Segment->UnCommittedRanges;
+ if (Segment->BaseAddress == Heap) {
+ CurrentBlock = &Heap->Entry;
+ }
+ else {
+ CurrentBlock = &Segment->Entry;
+ }
+
+ while (CurrentBlock < Segment->LastValidEntry) {
+ *BadAddress = CurrentBlock;
+ if (UnCommittedRange != NULL &&
+ (ULONG)CurrentBlock >= UnCommittedRange->Address
+ ) {
+ HeapDebugPrint(( "Heap entry %lx is beyond uncommited range [%x .. %x)\n",
+ CurrentBlock,
+ UnCommittedRange->Address,
+ (PCHAR)UnCommittedRange->Address + UnCommittedRange->Size
+ ));
+ return FALSE;
+ }
+
+ PreviousSize = 0;
+ while (CurrentBlock < Segment->LastValidEntry) {
+ *BadAddress = CurrentBlock;
+ if (PreviousSize != CurrentBlock->PreviousSize) {
+ HeapDebugPrint(( "Heap entry %lx has incorrect PreviousSize field (%04x instead of %04x)\n",
+ CurrentBlock, CurrentBlock->PreviousSize, PreviousSize
+ ));
+ return FALSE;
+ }
+
+ PreviousSize = CurrentBlock->Size;
+ Size = (ULONG)CurrentBlock->Size << HEAP_GRANULARITY_SHIFT;
+ if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
+ if (ComputedTagEntries != NULL) {
+ if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( CurrentBlock );
+ TagIndex = ExtraStuff->TagIndex;
+ }
+ else {
+ TagIndex = CurrentBlock->SmallTagIndex;
+ }
+
+ if (TagIndex != 0) {
+ if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
+ TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
+ if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) {
+ ComputedPseudoTagEntries[ TagIndex ] += CurrentBlock->Size;
+ }
+ }
+ else
+ if (TagIndex & HEAP_GLOBAL_TAG) {
+ //
+ // Ignore these since they are global across more than
+ // one heap.
+ //
+ }
+ else
+ if (TagIndex < Heap->NextAvailableTagIndex) {
+ ComputedTagEntries[ TagIndex ] += CurrentBlock->Size;
+ }
+ }
+ }
+
+ if (CurrentBlock->Flags & HEAP_ENTRY_FILL_PATTERN) {
+ if (!RtlpCheckBusyBlockTail( CurrentBlock )) {
+ return FALSE;
+ }
+ }
+ }
+ else {
+ *CountOfFreeBlocks += 1;
+ *TotalFreeSize += CurrentBlock->Size;
+
+ if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED &&
+ CurrentBlock->Flags & HEAP_ENTRY_FILL_PATTERN
+ ) {
+ ULONG cb, cbEqual;
+
+ cb = Size - sizeof( HEAP_FREE_ENTRY );
+ if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT &&
+ cb > sizeof( HEAP_FREE_ENTRY_EXTRA )
+ ) {
+ cb -= sizeof( HEAP_FREE_ENTRY_EXTRA );
+ }
+ cbEqual = RtlCompareMemoryUlong( (PCHAR)((PHEAP_FREE_ENTRY)CurrentBlock + 1), cb, FREE_HEAP_FILL );
+ if (cbEqual != cb) {
+ HeapDebugPrint(( "Free Heap block %lx modified at %lx after it was freed\n",
+ CurrentBlock,
+ (PCHAR)(CurrentBlock + 1) + cbEqual
+ ));
+ return FALSE;
+ }
+ }
+ }
+
+ if (CurrentBlock->SegmentIndex != SegmentIndex) {
+ HeapDebugPrint(( "Heap block at %lx has incorrect segment index (%x)\n",
+ CurrentBlock,
+ SegmentIndex
+ ));
+ return FALSE;
+ }
+
+ if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
+ CurrentBlock = (PHEAP_ENTRY)((PCHAR)CurrentBlock + Size);
+ if (UnCommittedRange == NULL) {
+ if (CurrentBlock != Segment->LastValidEntry) {
+ HeapDebugPrint(( "Heap block at %lx is not last block in segment (%x)\n",
+ CurrentBlock,
+ Segment->LastValidEntry
+ ));
+ return FALSE;
+ }
+ }
+ else
+ if ((ULONG)CurrentBlock != UnCommittedRange->Address) {
+ HeapDebugPrint(( "Heap block at %lx does not match address of next uncommitted address (%x)\n",
+ CurrentBlock,
+ UnCommittedRange->Address
+ ));
+ return FALSE;
+ }
+ else {
+ NumberOfUnCommittedPages += UnCommittedRange->Size / PAGE_SIZE;
+ NumberOfUnCommittedRanges += 1;
+ CurrentBlock = (PHEAP_ENTRY)
+ ((PCHAR)UnCommittedRange->Address + UnCommittedRange->Size);
+ UnCommittedRange = UnCommittedRange->Next;
+ }
+
+ break;
+ }
+
+
+
+ CurrentBlock = (PHEAP_ENTRY)((PCHAR)CurrentBlock + Size);
+ }
+ }
+
+ *BadAddress = Segment;
+ if (Segment->NumberOfUnCommittedPages != NumberOfUnCommittedPages) {
+ HeapDebugPrint(( "Heap Segment at %lx contains invalid NumberOfUnCommittedPages (%x != %x)\n",
+ Segment,
+ Segment->NumberOfUnCommittedPages,
+ NumberOfUnCommittedPages
+ ));
+ return FALSE;
+ }
+
+ if (Segment->NumberOfUnCommittedRanges != NumberOfUnCommittedRanges) {
+ HeapDebugPrint(( "Heap Segment at %lx contains invalid NumberOfUnCommittedRanges (%x != %x)\n",
+ Segment,
+ Segment->NumberOfUnCommittedRanges,
+ NumberOfUnCommittedRanges
+ ));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+RtlpValidateHeap(
+ IN PHEAP Heap,
+ IN BOOLEAN AlwaysValidate
+ )
+{
+ NTSTATUS Status;
+ PHEAP_SEGMENT Segment;
+ PLIST_ENTRY Head, Next;
+ PHEAP_FREE_ENTRY FreeBlock;
+ BOOLEAN EmptyFreeList;
+ ULONG NumberOfFreeListEntries;
+ ULONG CountOfFreeBlocks;
+ ULONG TotalFreeSize;
+ ULONG Size;
+ USHORT PreviousSize;
+ UCHAR SegmentIndex;
+ PVOID BadAddress;
+ PULONG ComputedTagEntries;
+ PULONG ComputedPseudoTagEntries;
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+ USHORT TagIndex;
+
+ RTL_PAGED_CODE();
+
+ BadAddress = Heap;
+ if (!RtlpValidateHeapHeaders( Heap, FALSE )) {
+ goto errorExit;
+ }
+
+ if (!AlwaysValidate && !(Heap->Flags & HEAP_VALIDATE_ALL_ENABLED)) {
+ goto exit;
+ }
+
+ NumberOfFreeListEntries = 0;
+ Head = &Heap->FreeLists[ 0 ];
+ for (Size = 0; Size < HEAP_MAXIMUM_FREELISTS; Size++) {
+ if (Size != 0) {
+ EmptyFreeList = (BOOLEAN)(IsListEmpty( Head ));
+ BadAddress = &Heap->u.FreeListsInUseBytes[ Size / 8 ];
+ if (Heap->u.FreeListsInUseBytes[ Size / 8 ] & (1 << (Size & 7)) ) {
+ if (EmptyFreeList) {
+ HeapDebugPrint(( "dedicated (%04x) free list empty but marked as non-empty\n",
+ Size
+ ));
+ goto errorExit;
+ }
+ }
+ else {
+ if (!EmptyFreeList) {
+ HeapDebugPrint(( "dedicated (%04x) free list non-empty but marked as empty\n",
+ Size
+ ));
+ goto errorExit;
+ }
+ }
+ }
+
+ Next = Head->Flink;
+ PreviousSize = 0;
+ while (Head != Next) {
+ FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
+ Next = Next->Flink;
+ BadAddress = FreeBlock;
+ if (FreeBlock->Flags & HEAP_ENTRY_BUSY) {
+ HeapDebugPrint(( "dedicated (%04x) free list element %lx is marked busy\n",
+ Size,
+ FreeBlock
+ ));
+ goto errorExit;
+ }
+
+ if (Size != 0 && FreeBlock->Size != Size) {
+ HeapDebugPrint(( "Dedicated (%04x) free list element %lx is wrong size (%04x)\n",
+ Size,
+ FreeBlock,
+ FreeBlock->Size
+ ));
+ goto errorExit;
+ }
+ else
+ if (Size == 0 && FreeBlock->Size < HEAP_MAXIMUM_FREELISTS) {
+ HeapDebugPrint(( "Non-Dedicated free list element %lx with too small size (%04x)\n",
+ FreeBlock,
+ FreeBlock->Size
+ ));
+ goto errorExit;
+ }
+ else
+ if (Size == 0 && FreeBlock->Size < PreviousSize) {
+ HeapDebugPrint(( "Non-Dedicated free list element %lx is out of order\n",
+ FreeBlock
+ ));
+ goto errorExit;
+ }
+ else {
+ PreviousSize = FreeBlock->Size;
+ }
+
+ NumberOfFreeListEntries++;
+ }
+
+ Head++;
+ }
+
+ Size = (HEAP_NUMBER_OF_PSEUDO_TAG +
+ Heap->NextAvailableTagIndex +
+ 1
+ ) * sizeof( ULONG );
+ ComputedTagEntries = NULL;
+ ComputedPseudoTagEntries = NULL;
+ if (RtlpValidateHeapTagsEnable && Heap->PseudoTagEntries != NULL) {
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &ComputedPseudoTagEntries,
+ 0,
+ &Size,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (NT_SUCCESS( Status )) {
+ ComputedTagEntries = ComputedPseudoTagEntries + HEAP_NUMBER_OF_PSEUDO_TAG;
+ }
+ }
+
+ Head = &Heap->VirtualAllocdBlocks;
+ Next = Head->Flink;
+ while (Head != Next) {
+ VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
+ if (ComputedTagEntries != NULL) {
+ TagIndex = VirtualAllocBlock->ExtraStuff.TagIndex;
+ if (TagIndex != 0) {
+ if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
+ TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
+ if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG) {
+ ComputedPseudoTagEntries[ TagIndex ] +=
+ VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT;
+ }
+ }
+ else
+ if (TagIndex & HEAP_GLOBAL_TAG) {
+ //
+ // Ignore these since they are global across more than
+ // one heap.
+ //
+ }
+ else
+ if (TagIndex < Heap->NextAvailableTagIndex) {
+ ComputedTagEntries[ TagIndex ] +=
+ VirtualAllocBlock->CommitSize >> HEAP_GRANULARITY_SHIFT;
+ }
+ }
+ }
+
+ if (VirtualAllocBlock->BusyBlock.Flags & HEAP_ENTRY_FILL_PATTERN) {
+ if (!RtlpCheckBusyBlockTail( &VirtualAllocBlock->BusyBlock )) {
+ return FALSE;
+ }
+ }
+
+ Next = Next->Flink;
+ }
+
+ CountOfFreeBlocks = 0;
+ TotalFreeSize = 0;
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment) {
+ if (!RtlpValidateHeapSegment( Heap,
+ Segment,
+ SegmentIndex,
+ &CountOfFreeBlocks,
+ &TotalFreeSize,
+ &BadAddress,
+ ComputedTagEntries,
+ ComputedPseudoTagEntries
+ )
+ ) {
+ goto errorExit;
+ }
+ }
+ }
+
+ BadAddress = Heap;
+ if (NumberOfFreeListEntries != CountOfFreeBlocks) {
+ HeapDebugPrint(( "Number of free blocks in arena (%ld) does not match number in the free lists (%ld)\n",
+ CountOfFreeBlocks,
+ NumberOfFreeListEntries
+ ));
+ goto errorExit;
+ }
+
+ if (Heap->TotalFreeSize != TotalFreeSize) {
+ HeapDebugPrint(( "Total size of free blocks in arena (%ld) does not match number total in heap header (%ld)\n",
+ TotalFreeSize,
+ Heap->TotalFreeSize
+ ));
+ goto errorExit;
+ }
+
+ if (ComputedPseudoTagEntries != NULL) {
+ PHEAP_PSEUDO_TAG_ENTRY PseudoTagEntries;
+ PHEAP_TAG_ENTRY TagEntries;
+ USHORT TagIndex;
+
+ PseudoTagEntries = Heap->PseudoTagEntries;
+ if (PseudoTagEntries != NULL) {
+ for (TagIndex=1; TagIndex<HEAP_NUMBER_OF_PSEUDO_TAG; TagIndex++) {
+ PseudoTagEntries += 1;
+ if (ComputedPseudoTagEntries[ TagIndex ] != PseudoTagEntries->Size) {
+ HeapDebugPrint(( "Pseudo Tag %04x size incorrect (%x != %x) %x\n",
+ TagIndex,
+ PseudoTagEntries->Size,
+ ComputedPseudoTagEntries[ TagIndex ]
+ &ComputedPseudoTagEntries[ TagIndex ]
+ ));
+ goto errorExit;
+ }
+ }
+ }
+ TagEntries = Heap->TagEntries;
+ if (TagEntries != NULL) {
+ for (TagIndex=1; TagIndex<Heap->NextAvailableTagIndex; TagIndex++) {
+ TagEntries += 1;
+ if (ComputedTagEntries[ TagIndex ] != TagEntries->Size) {
+ HeapDebugPrint(( "Tag %04x (%ws) size incorrect (%x != %x) %x\n",
+ TagIndex,
+ TagEntries->TagName,
+ TagEntries->Size,
+ ComputedTagEntries[ TagIndex ],
+ &ComputedTagEntries[ TagIndex ]
+ ));
+ goto errorExit;
+ }
+ }
+ }
+
+ Size = 0;
+ NtFreeVirtualMemory( NtCurrentProcess(),
+ &ComputedPseudoTagEntries,
+ &Size,
+ MEM_RELEASE
+ );
+ }
+
+exit:
+ return TRUE;
+
+errorExit:
+ HeapDebugBreak( BadAddress );
+
+ if (ComputedPseudoTagEntries != NULL) {
+ Size = 0;
+ NtFreeVirtualMemory( NtCurrentProcess(),
+ &ComputedPseudoTagEntries,
+ &Size,
+ MEM_RELEASE
+ );
+ }
+ return FALSE;
+
+} // RtlpValidateHeap
+
+
+BOOLEAN RtlpHeapInvalidBreakPoint;
+PVOID RtlpHeapInvalidBadAddress;
+
+VOID
+RtlpBreakPointHeap(
+ IN PVOID BadAddress
+ )
+{
+ if (NtCurrentPeb()->BeingDebugged) {
+ *(BOOLEAN volatile *)&RtlpHeapInvalidBreakPoint = TRUE;
+ RtlpHeapInvalidBadAddress = BadAddress;
+ DbgBreakPoint();
+ *(BOOLEAN volatile *)&RtlpHeapInvalidBreakPoint = FALSE;
+ }
+}
diff --git a/private/ntos/rtl/heapdll.c b/private/ntos/rtl/heapdll.c
new file mode 100644
index 000000000..9b0a1f7f6
--- /dev/null
+++ b/private/ntos/rtl/heapdll.c
@@ -0,0 +1,2732 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ heapdll.c
+
+Abstract:
+
+ This module implements the user mode only portions of the heap allocator.
+
+Author:
+
+ Steve Wood (stevewo) 20-Sep-1994
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "heap.h"
+#include "heappriv.h"
+
+BOOLEAN
+RtlpGrowBlockInPlace(
+ IN PHEAP Heap,
+ IN ULONG Flags,
+ IN PHEAP_ENTRY BusyBlock,
+ IN ULONG Size,
+ IN ULONG AllocationIndex
+ );
+
+PVOID
+RtlDebugReAllocateHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN ULONG Size
+ );
+
+BOOLEAN
+RtlDebugGetUserInfoHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ OUT PVOID *UserValue OPTIONAL,
+ OUT PULONG UserFlags OPTIONAL
+ );
+
+BOOLEAN
+RtlDebugSetUserValueHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN PVOID UserValue
+ );
+
+BOOLEAN
+RtlDebugSetUserFlagsHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN ULONG UserFlagsReset,
+ IN ULONG UserFlagsSet
+ );
+
+ULONG
+RtlDebugSizeHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ );
+
+ULONG
+RtlDebugCompactHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ );
+
+NTSTATUS
+RtlDebugCreateTagHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PWSTR TagPrefix OPTIONAL,
+ IN PWSTR TagNames
+ );
+
+PWSTR
+RtlDebugQueryTagHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN USHORT TagIndex,
+ IN BOOLEAN ResetCounters,
+ OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL
+ );
+
+NTSTATUS
+RtlDebugUsageHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN OUT PRTL_HEAP_USAGE Usage
+ );
+
+BOOLEAN
+RtlDebugWalkHeap(
+ IN PVOID HeapHandle,
+ IN OUT PRTL_HEAP_WALK_ENTRY Entry
+ );
+
+HEAP_LOCK RtlpProcessHeapsListLock;
+
+#define RTLP_STATIC_HEAP_LIST_SIZE 16
+PHEAP RtlpProcessHeapsListBuffer[ RTLP_STATIC_HEAP_LIST_SIZE ];
+
+NTSTATUS
+RtlInitializeHeapManager( VOID )
+{
+ PPEB Peb = NtCurrentPeb();
+
+#if DBG
+ if (sizeof( HEAP_ENTRY ) != sizeof( HEAP_ENTRY_EXTRA )) {
+ HeapDebugPrint(( "Heap header and extra header sizes disagree\n" ));
+ HeapDebugBreak( NULL );
+ }
+
+ if (sizeof( HEAP_ENTRY ) != CHECK_HEAP_TAIL_SIZE) {
+ HeapDebugPrint(( "Heap header and tail fill sizes disagree\n" ));
+ HeapDebugBreak( NULL );
+ }
+
+ if (sizeof( HEAP_FREE_ENTRY ) != (2 * sizeof( HEAP_ENTRY ))) {
+ HeapDebugPrint(( "Heap header and free header sizes disagree\n" ));
+ HeapDebugBreak( NULL );
+ }
+#endif // DBG
+
+ Peb->NumberOfHeaps = 0;
+ Peb->MaximumNumberOfHeaps = RTLP_STATIC_HEAP_LIST_SIZE;
+ Peb->ProcessHeaps = RtlpProcessHeapsListBuffer;
+ return RtlInitializeLockRoutine( &RtlpProcessHeapsListLock.Lock );
+}
+
+BOOLEAN
+RtlpCheckHeapSignature(
+ IN PHEAP Heap,
+ IN PCHAR Caller
+ )
+{
+
+ if (Heap->Signature == HEAP_SIGNATURE) {
+ return TRUE;
+ }
+ else {
+ HeapDebugPrint(( "Invalid heap signature for heap at %x", Heap ));
+ if (Caller != NULL) {
+ DbgPrint( ", passed to %s", Caller );
+ }
+ DbgPrint( "\n" );
+ HeapDebugBreak( &Heap->Signature );
+ return FALSE;
+ }
+}
+
+PHEAP_FREE_ENTRY
+RtlpCoalesceHeap(
+ IN PHEAP Heap
+ )
+{
+ ULONG OldFreeSize, FreeSize, n;
+ PHEAP_FREE_ENTRY FreeBlock, LargestFreeBlock;
+ PLIST_ENTRY FreeListHead, Next;
+
+ RTL_PAGED_CODE();
+
+ LargestFreeBlock = NULL;
+ FreeListHead = &Heap->FreeLists[ 1 ];
+ n = HEAP_MAXIMUM_FREELISTS;
+ while (n--) {
+ Next = FreeListHead->Blink;
+ while (FreeListHead != Next) {
+ FreeBlock = CONTAINING_RECORD( Next, HEAP_FREE_ENTRY, FreeList );
+ Next = Next->Flink;
+ OldFreeSize = FreeSize = FreeBlock->Size;
+ FreeBlock = RtlpCoalesceFreeBlocks( Heap,
+ FreeBlock,
+ &FreeSize,
+ TRUE
+ );
+ if (FreeSize != OldFreeSize) {
+ if (FreeBlock->Size >= (PAGE_SIZE >> HEAP_GRANULARITY_SHIFT) &&
+ (FreeBlock->PreviousSize == 0 ||
+ (FreeBlock->Flags & HEAP_ENTRY_LAST_ENTRY)
+ )
+ ) {
+ RtlpDeCommitFreeBlock( Heap, FreeBlock, FreeSize );
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
+ }
+
+ Next = FreeListHead->Blink;
+ }
+ else {
+ if (LargestFreeBlock == NULL ||
+ LargestFreeBlock->Size < FreeBlock->Size
+ ) {
+ LargestFreeBlock = FreeBlock;
+ }
+ }
+ }
+
+ if (n == 1) {
+ FreeListHead = &Heap->FreeLists[ 0 ];
+ }
+ else {
+ FreeListHead++;
+ }
+ }
+
+ return LargestFreeBlock;
+}
+
+VOID
+RtlpAddHeapToProcessList(
+ IN PHEAP Heap
+ )
+{
+ PPEB Peb = NtCurrentPeb();
+ PHEAP *NewList;
+
+ RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ try {
+ if (Peb->NumberOfHeaps == Peb->MaximumNumberOfHeaps) {
+ Peb->MaximumNumberOfHeaps *= 2;
+ NewList = RtlAllocateHeap( RtlProcessHeap(),
+ 0,
+ Peb->MaximumNumberOfHeaps * sizeof( *NewList )
+ );
+ if (NewList == NULL) {
+ leave;
+ }
+
+ RtlMoveMemory( NewList,
+ Peb->ProcessHeaps,
+ Peb->NumberOfHeaps * sizeof( *NewList )
+ );
+ if (Peb->ProcessHeaps != RtlpProcessHeapsListBuffer) {
+ RtlFreeHeap( RtlProcessHeap(), 0, Peb->ProcessHeaps );
+ }
+
+ Peb->ProcessHeaps = NewList;
+ }
+
+ Peb->ProcessHeaps[ Peb->NumberOfHeaps++ ] = Heap;
+ Heap->ProcessHeapsListIndex = (USHORT)Peb->NumberOfHeaps;
+ }
+ finally {
+ RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ }
+
+ return;
+}
+
+VOID
+RtlpRemoveHeapFromProcessList(
+ IN PHEAP Heap
+ )
+{
+ PPEB Peb = NtCurrentPeb();
+ PHEAP *p, *p1;
+ ULONG n;
+
+ RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ if (Peb->NumberOfHeaps != 0 &&
+ Heap->ProcessHeapsListIndex != 0 &&
+ Heap->ProcessHeapsListIndex <= Peb->NumberOfHeaps
+ ) {
+ p = (PHEAP *)&Peb->ProcessHeaps[ Heap->ProcessHeapsListIndex - 1 ];
+ p1 = p + 1;
+ n = Peb->NumberOfHeaps - (Heap->ProcessHeapsListIndex - 1);
+ while (--n) {
+ *p = *p1++;
+ RtlpUpdateHeapListIndex( (*p)->ProcessHeapsListIndex,
+ (USHORT)((*p)->ProcessHeapsListIndex - 1)
+ );
+ (*p)->ProcessHeapsListIndex -= 1;
+ p += 1;
+ }
+ Peb->ProcessHeaps[ --Peb->NumberOfHeaps ] = NULL;
+ Heap->ProcessHeapsListIndex = 0;
+ }
+
+ RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ return;
+}
+
+
+ULONG
+RtlGetProcessHeaps(
+ ULONG NumberOfHeaps,
+ PVOID *ProcessHeaps
+ )
+{
+ PPEB Peb = NtCurrentPeb();
+ ULONG ActualNumberOfHeaps;
+
+ ActualNumberOfHeaps = 0;
+ RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ try {
+ ActualNumberOfHeaps = Peb->NumberOfHeaps;
+ if (ActualNumberOfHeaps <= NumberOfHeaps) {
+ RtlMoveMemory( ProcessHeaps,
+ Peb->ProcessHeaps,
+ ActualNumberOfHeaps * sizeof( *ProcessHeaps )
+ );
+ }
+ }
+ finally {
+ RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ }
+
+#ifdef DEBUG_PAGE_HEAP
+
+ if ( RtlpDebugPageHeap ) {
+
+ ULONG RemainingHeaps = ( NumberOfHeaps > ActualNumberOfHeaps ) ?
+ ( NumberOfHeaps - ActualNumberOfHeaps ) :
+ ( 0 );
+
+ ActualNumberOfHeaps += RtlpDebugPageHeapGetProcessHeaps(
+ RemainingHeaps,
+ ProcessHeaps + ActualNumberOfHeaps );
+ }
+#endif
+
+ return ActualNumberOfHeaps;
+}
+
+
+NTSTATUS
+RtlEnumProcessHeaps(
+ PRTL_ENUM_HEAPS_ROUTINE EnumRoutine,
+ PVOID Parameter
+ )
+{
+ PPEB Peb = NtCurrentPeb();
+ NTSTATUS Status;
+ ULONG i;
+
+ Status = STATUS_SUCCESS;
+ RtlAcquireLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ try {
+ for (i=0; i<Peb->NumberOfHeaps; i++) {
+ Status = (*EnumRoutine)( (PHEAP)(Peb->ProcessHeaps[ i ]), Parameter );
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+ }
+ }
+ finally {
+ RtlReleaseLockRoutine( &RtlpProcessHeapsListLock.Lock );
+ }
+
+ return Status;
+}
+
+
+BOOLEAN
+RtlLockHeap(
+ IN PVOID HeapHandle
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+
+ RTL_PAGED_CODE();
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapLock( HeapHandle )
+ );
+
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlLockHeap" )) {
+ return FALSE;
+ }
+
+ //
+ // Lock the heap.
+ //
+
+ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+RtlUnlockHeap(
+ IN PVOID HeapHandle
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+
+ RTL_PAGED_CODE();
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapUnlock( HeapHandle )
+ );
+
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (!HEAP_VALIDATE_SIGNATURE( Heap, "RtlUnlockHeap" )) {
+ return FALSE;
+ }
+
+ //
+ // Unlock the heap.
+ //
+
+ if (!(Heap->Flags & HEAP_NO_SERIALIZE)) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return TRUE;
+}
+
+
+BOOLEAN
+RtlpGrowBlockInPlace(
+ IN PHEAP Heap,
+ IN ULONG Flags,
+ IN PHEAP_ENTRY BusyBlock,
+ IN ULONG Size,
+ IN ULONG AllocationIndex
+ )
+{
+ ULONG FreeSize, OldSize;
+ UCHAR EntryFlags, FreeFlags;
+ PHEAP_FREE_ENTRY FreeBlock, SplitBlock, SplitBlock2;
+ PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
+
+ if (AllocationIndex > Heap->VirtualMemoryThreshold) {
+ return FALSE;
+ }
+
+ EntryFlags = BusyBlock->Flags;
+ FreeBlock = (PHEAP_FREE_ENTRY)(BusyBlock + BusyBlock->Size);
+ if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
+ FreeSize = (AllocationIndex - BusyBlock->Size) << HEAP_GRANULARITY_SHIFT;
+ FreeSize = ROUND_UP_TO_POWER2( FreeSize, PAGE_SIZE );
+ FreeBlock = RtlpFindAndCommitPages( Heap,
+ Heap->Segments[ BusyBlock->SegmentIndex ],
+ &FreeSize,
+ (PHEAP_ENTRY)FreeBlock
+ );
+ if (FreeBlock == NULL) {
+ return FALSE;
+ }
+
+ FreeSize = FreeSize >> HEAP_GRANULARITY_SHIFT;
+ FreeBlock = RtlpCoalesceFreeBlocks( Heap, FreeBlock, &FreeSize, FALSE );
+ FreeFlags = FreeBlock->Flags;
+ if ((FreeSize + BusyBlock->Size) < AllocationIndex) {
+ RtlpInsertFreeBlock( Heap, FreeBlock, FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ if (DEBUG_HEAP(Flags)) {
+ RtlpValidateHeapHeaders( Heap, TRUE );
+ }
+
+ return FALSE;
+ }
+
+ FreeSize += BusyBlock->Size;
+ }
+ else {
+ FreeFlags = FreeBlock->Flags;
+ if (FreeFlags & HEAP_ENTRY_BUSY) {
+ return FALSE;
+ }
+
+ FreeSize = BusyBlock->Size + FreeBlock->Size;
+ if (FreeSize < AllocationIndex) {
+ return FALSE;
+ }
+
+ RtlpRemoveFreeBlock( Heap, FreeBlock );
+ Heap->TotalFreeSize -= FreeBlock->Size;
+ }
+
+ OldSize = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) -
+ BusyBlock->UnusedBytes;
+ FreeSize -= AllocationIndex;
+ if (FreeSize <= 2) {
+ AllocationIndex += FreeSize;
+ FreeSize = 0;
+ }
+
+ if (EntryFlags & HEAP_ENTRY_EXTRA_PRESENT) {
+ OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
+ NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
+ *NewExtraStuff = *OldExtraStuff;
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ NewExtraStuff->TagIndex =
+ RtlpUpdateTagEntry( Heap,
+ NewExtraStuff->TagIndex,
+ BusyBlock->Size,
+ AllocationIndex,
+ ReAllocationAction
+ );
+ }
+ }
+ else
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ BusyBlock->SmallTagIndex = (UCHAR)
+ RtlpUpdateTagEntry( Heap,
+ BusyBlock->SmallTagIndex,
+ BusyBlock->Size,
+ AllocationIndex,
+ ReAllocationAction
+ );
+ }
+
+ if (FreeSize == 0) {
+ BusyBlock->Flags |= FreeFlags & HEAP_ENTRY_LAST_ENTRY;
+ BusyBlock->Size = (USHORT)AllocationIndex;
+ BusyBlock->UnusedBytes = (UCHAR)
+ ((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size);
+ if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
+ (BusyBlock + BusyBlock->Size)->PreviousSize = BusyBlock->Size;
+ }
+ }
+ else {
+ BusyBlock->Size = (USHORT)AllocationIndex;
+ BusyBlock->UnusedBytes = (UCHAR)
+ ((AllocationIndex << HEAP_GRANULARITY_SHIFT) - Size);
+ SplitBlock = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)BusyBlock + AllocationIndex);
+ SplitBlock->PreviousSize = (USHORT)AllocationIndex;
+ SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
+ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
+ SplitBlock->Flags = FreeFlags;
+ SplitBlock->Size = (USHORT)FreeSize;
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
+ if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
+ SplitBlock->Flags = FreeFlags & (~HEAP_ENTRY_LAST_ENTRY);
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
+ }
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ FreeFlags = SplitBlock2->Flags;
+ RtlpRemoveFreeBlock( Heap, SplitBlock2 );
+ Heap->TotalFreeSize -= SplitBlock2->Size;
+ FreeSize += SplitBlock2->Size;
+ SplitBlock->Flags = FreeFlags;
+ if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (!(FreeFlags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_ENTRY)SplitBlock + FreeSize)->PreviousSize = (USHORT)FreeSize;
+ }
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
+ }
+ }
+ }
+ }
+
+ if (Flags & HEAP_ZERO_MEMORY) {
+ if (Size > OldSize) {
+ RtlZeroMemory( (PCHAR)(BusyBlock + 1) + OldSize,
+ Size - OldSize
+ );
+ }
+ }
+ else
+ if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
+ ULONG PartialBytes, ExtraSize;
+
+ PartialBytes = OldSize & (sizeof( ULONG ) - 1);
+ if (PartialBytes) {
+ PartialBytes = 4 - PartialBytes;
+ }
+ if (Size > (OldSize + PartialBytes)) {
+ ExtraSize = (Size - (OldSize + PartialBytes)) & ~(sizeof( ULONG ) - 1);
+ if (ExtraSize != 0) {
+ RtlFillMemoryUlong( (PCHAR)(BusyBlock + 1) + OldSize + PartialBytes,
+ ExtraSize,
+ ALLOC_HEAP_FILL
+ );
+ }
+ }
+ }
+
+ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
+ RtlFillMemory( (PCHAR)(BusyBlock + 1) + Size,
+ CHECK_HEAP_TAIL_SIZE,
+ CHECK_HEAP_TAIL_FILL
+ );
+ }
+
+ BusyBlock->Flags &= ~HEAP_ENTRY_SETTABLE_FLAGS;
+ BusyBlock->Flags |= ((Flags & HEAP_SETTABLE_USER_FLAGS) >> 4);
+
+ return TRUE;
+}
+
+
+PVOID
+RtlReAllocateHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN ULONG Size
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ ULONG AllocationSize;
+ PHEAP_ENTRY BusyBlock, NewBusyBlock;
+ PHEAP_ENTRY_EXTRA OldExtraStuff, NewExtraStuff;
+ ULONG FreeSize;
+ BOOLEAN LockAcquired;
+ PVOID NewBaseAddress;
+ PHEAP_FREE_ENTRY SplitBlock, SplitBlock2;
+ ULONG OldSize;
+ ULONG AllocationIndex;
+ ULONG OldAllocationIndex;
+ UCHAR FreeFlags;
+ NTSTATUS Status;
+ PVOID DeCommitAddress;
+ ULONG DeCommitSize;
+ EXCEPTION_RECORD ExceptionRecord;
+#if ENABLE_HEAP_EVENT_LOGGING
+ PVOID OldBaseAddress = BaseAddress;
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ if (BaseAddress == NULL) {
+ SET_LAST_STATUS( STATUS_SUCCESS );
+ return NULL;
+ }
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags)) {
+ return RtlDebugReAllocateHeap( HeapHandle, Flags, BaseAddress, Size );
+ }
+
+ if (Size > 0x7fffffff) {
+ SET_LAST_STATUS( STATUS_NO_MEMORY );
+ return NULL;
+ }
+
+ //
+ // Round the requested size up to the allocation granularity. Note
+ // that if the request is for 0 bytes, we still allocate memory, because
+ // we add in an extra byte to protect ourselves from idiots.
+ //
+
+ AllocationSize = ((Size ? Size : 1) + Heap->AlignRound) & Heap->AlignMask;
+ if (Flags & HEAP_NEED_EXTRA_FLAGS || Heap->PseudoTagEntries != NULL) {
+ AllocationSize += sizeof( HEAP_ENTRY_EXTRA );
+ }
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ Flags ^= HEAP_NO_SERIALIZE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ try { try {
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+
+ //
+ // Bail if not a busy block.
+ //
+ leave;
+ }
+ else
+ if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ OldSize = RtlpGetSizeOfBigBlock( BusyBlock );
+ OldAllocationIndex = (OldSize + BusyBlock->Size) >> HEAP_GRANULARITY_SHIFT;
+ AllocationSize += FIELD_OFFSET( HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ AllocationSize = ROUND_UP_TO_POWER2( AllocationSize, PAGE_SIZE );
+ }
+ else {
+ OldAllocationIndex = BusyBlock->Size;
+ OldSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) -
+ BusyBlock->UnusedBytes;
+ }
+
+ AllocationIndex = AllocationSize >> HEAP_GRANULARITY_SHIFT;
+
+ //
+ // See if new size less than or equal to the current size.
+ //
+
+ if (AllocationIndex <= OldAllocationIndex) {
+ if (AllocationIndex + 1 == OldAllocationIndex) {
+ AllocationIndex += 1;
+ AllocationSize += sizeof( HEAP_ENTRY );
+ }
+
+ //
+ // Then shrinking block. Calculate new residual amount and fill
+ // in the tail padding if enabled.
+ //
+
+ if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ BusyBlock->Size = (USHORT)(AllocationSize - Size);
+ }
+ else
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ OldExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + BusyBlock->Size - 1);
+ NewExtraStuff = (PHEAP_ENTRY_EXTRA)(BusyBlock + AllocationIndex - 1);
+ *NewExtraStuff = *OldExtraStuff;
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ NewExtraStuff->TagIndex =
+ RtlpUpdateTagEntry( Heap,
+ NewExtraStuff->TagIndex,
+ OldAllocationIndex,
+ AllocationIndex,
+ ReAllocationAction
+ );
+ }
+
+ BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
+ }
+ else {
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ BusyBlock->SmallTagIndex = (UCHAR)
+ RtlpUpdateTagEntry( Heap,
+ BusyBlock->SmallTagIndex,
+ BusyBlock->Size,
+ AllocationIndex,
+ ReAllocationAction
+ );
+ }
+
+ BusyBlock->UnusedBytes = (UCHAR)(AllocationSize - Size);
+ }
+
+ //
+ // If block is getting bigger, then fill in the extra
+ // space.
+ //
+
+ if (Size > OldSize) {
+ if (Flags & HEAP_ZERO_MEMORY) {
+ RtlZeroMemory( (PCHAR)BaseAddress + OldSize,
+ Size - OldSize
+ );
+ }
+ else
+ if (Heap->Flags & HEAP_FREE_CHECKING_ENABLED) {
+ ULONG PartialBytes, ExtraSize;
+
+ PartialBytes = OldSize & (sizeof( ULONG ) - 1);
+ if (PartialBytes) {
+ PartialBytes = 4 - PartialBytes;
+ }
+ if (Size > (OldSize + PartialBytes)) {
+ ExtraSize = (Size - (OldSize + PartialBytes)) & ~(sizeof( ULONG ) - 1);
+ if (ExtraSize != 0) {
+ RtlFillMemoryUlong( (PCHAR)(BusyBlock + 1) + OldSize + PartialBytes,
+ ExtraSize,
+ ALLOC_HEAP_FILL
+ );
+ }
+ }
+ }
+ }
+
+ if (Heap->Flags & HEAP_TAIL_CHECKING_ENABLED) {
+ RtlFillMemory( (PCHAR)(BusyBlock + 1) + Size,
+ CHECK_HEAP_TAIL_SIZE,
+ CHECK_HEAP_TAIL_FILL
+ );
+ }
+
+ //
+ // If amount of change is greater than the size of a free block,
+ // then need to free the extra space. Otherwise, nothing else to
+ // do.
+ //
+
+ if (AllocationIndex != OldAllocationIndex) {
+ FreeFlags = BusyBlock->Flags & ~HEAP_ENTRY_BUSY;
+ if (FreeFlags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ VirtualAllocBlock = CONTAINING_RECORD( BusyBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+
+ if (IS_HEAP_TAGGING_ENABLED()) {
+ VirtualAllocBlock->ExtraStuff.TagIndex =
+ RtlpUpdateTagEntry( Heap,
+ VirtualAllocBlock->ExtraStuff.TagIndex,
+ OldAllocationIndex,
+ AllocationIndex,
+ VirtualReAllocationAction
+ );
+ }
+
+ DeCommitAddress = (PCHAR)VirtualAllocBlock + AllocationSize;
+ DeCommitSize = (OldAllocationIndex << HEAP_GRANULARITY_SHIFT) -
+ AllocationSize;
+ Status = ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&DeCommitAddress,
+ &DeCommitSize,
+ MEM_RELEASE
+ );
+ if (!NT_SUCCESS( Status )) {
+ HeapDebugPrint(( "Unable to release memory at %x for %x bytes - Status == %x\n",
+ DeCommitAddress, DeCommitSize, Status
+ ));
+ HeapDebugBreak( NULL );
+ }
+ else {
+ VirtualAllocBlock->CommitSize -= DeCommitSize;
+ }
+ }
+ else {
+ //
+ // Otherwise, shrink size of this block to new size, and make extra
+ // space at end free.
+ //
+
+ SplitBlock = (PHEAP_FREE_ENTRY)(BusyBlock + AllocationIndex);
+ SplitBlock->Flags = FreeFlags;
+ SplitBlock->PreviousSize = (USHORT)AllocationIndex;
+ SplitBlock->SegmentIndex = BusyBlock->SegmentIndex;
+ FreeSize = BusyBlock->Size - AllocationIndex;
+ BusyBlock->Size = (USHORT)AllocationIndex;
+ BusyBlock->Flags &= ~HEAP_ENTRY_LAST_ENTRY;
+ if (FreeFlags & HEAP_ENTRY_LAST_ENTRY) {
+ SplitBlock->Size = (USHORT)FreeSize;
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ SplitBlock2 = (PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize);
+ if (SplitBlock2->Flags & HEAP_ENTRY_BUSY) {
+ SplitBlock->Size = (USHORT)FreeSize;
+ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ SplitBlock->Flags = SplitBlock2->Flags;
+ RtlpRemoveFreeBlock( Heap, SplitBlock2 );
+ Heap->TotalFreeSize -= SplitBlock2->Size;
+ FreeSize += SplitBlock2->Size;
+ if (FreeSize <= HEAP_MAXIMUM_BLOCK_SIZE) {
+ SplitBlock->Size = (USHORT)FreeSize;
+ if (!(SplitBlock->Flags & HEAP_ENTRY_LAST_ENTRY)) {
+ ((PHEAP_FREE_ENTRY)((PHEAP_ENTRY)SplitBlock + FreeSize))->PreviousSize = (USHORT)FreeSize;
+ }
+ RtlpInsertFreeBlockDirect( Heap, SplitBlock, (USHORT)FreeSize );
+ Heap->TotalFreeSize += FreeSize;
+ }
+ else {
+ RtlpInsertFreeBlock( Heap, SplitBlock, FreeSize );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ else {
+ if ((BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) ||
+ !RtlpGrowBlockInPlace( Heap, Flags, BusyBlock, Size, AllocationIndex )
+ ) {
+ //
+ // Otherwise growing block, so allocate a new block with the bigger
+ // size, copy the contents of the old block to the new block and then
+ // free the old block. Return the address of the new block.
+ //
+
+ if (Flags & HEAP_REALLOC_IN_PLACE_ONLY) {
+#if DBG
+ HeapDebugPrint(( "Failing ReAlloc because cant do it inplace.\n" ));
+#endif
+ BaseAddress = NULL;
+ }
+ else {
+ Flags &= ~HEAP_TAG_MASK;
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ Flags &= ~HEAP_SETTABLE_USER_FLAGS;
+ Flags |= HEAP_SETTABLE_USER_VALUE |
+ ((BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4);
+
+ OldExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ try {
+ if (OldExtraStuff->TagIndex != 0 &&
+ !(OldExtraStuff->TagIndex & HEAP_PSEUDO_TAG_FLAG)
+ ) {
+ Flags |= OldExtraStuff->TagIndex << HEAP_TAG_SHIFT;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ BusyBlock->Flags &= ~HEAP_ENTRY_EXTRA_PRESENT;
+ }
+ }
+ else
+ if (BusyBlock->SmallTagIndex != 0) {
+ Flags |= BusyBlock->SmallTagIndex << HEAP_TAG_SHIFT;
+ }
+
+ NewBaseAddress = RtlAllocateHeap( HeapHandle,
+ Flags & ~HEAP_ZERO_MEMORY,
+ Size
+ );
+ if (NewBaseAddress != NULL) {
+ NewBusyBlock = (PHEAP_ENTRY)NewBaseAddress - 1;
+ if (NewBusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ NewExtraStuff = RtlpGetExtraStuffPointer( NewBusyBlock );
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ OldExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ NewExtraStuff->Settable = OldExtraStuff->Settable;
+ }
+ else {
+ NewExtraStuff->ZeroInit = 0;
+ }
+ }
+
+ RtlMoveMemory( NewBaseAddress, BaseAddress, OldSize );
+ if (Size > OldSize && (Flags & HEAP_ZERO_MEMORY)) {
+ RtlZeroMemory( (PCHAR)NewBaseAddress + OldSize,
+ Size - OldSize
+ );
+ }
+
+ RtlFreeHeap( HeapHandle,
+ Flags,
+ BaseAddress
+ );
+ }
+
+ BaseAddress = NewBaseAddress;
+ }
+ }
+ }
+
+#if ENABLE_HEAP_EVENT_LOGGING
+ if (RtlAreLogging( Heap->EventLogMask )) {
+ RtlLogEvent( RtlpReAllocHeapEventId,
+ Heap->EventLogMask,
+ Heap,
+ Flags,
+ OldBaseAddress,
+ OldSize,
+ Size,
+ BaseAddress
+ );
+ }
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ LockAcquired = FALSE;
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ if (BaseAddress == NULL && Flags & HEAP_GENERATE_EXCEPTIONS) {
+ //
+ // Construct an exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = STATUS_NO_MEMORY;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 1;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.ExceptionInformation[ 0 ] = AllocationSize;
+ RtlRaiseException( &ExceptionRecord );
+ }
+
+ }
+ except( GetExceptionCode() == STATUS_NO_MEMORY ? EXCEPTION_CONTINUE_SEARCH :
+ EXCEPTION_EXECUTE_HANDLER
+ ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ BaseAddress = NULL;
+ };
+ } finally {
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+ }
+
+ return BaseAddress;
+
+}
+
+
+BOOLEAN
+RtlValidateProcessHeaps( VOID )
+{
+ ULONG i, NumberOfHeaps;
+ PVOID Heaps[ 128 ];
+ BOOLEAN Result;
+
+ Result = TRUE;
+ NumberOfHeaps = RtlGetProcessHeaps( 128, Heaps );
+ for (i=0; i<NumberOfHeaps; i++) {
+ if (!RtlValidateHeap( Heaps[i], 0, NULL )) {
+ Result = FALSE;
+ }
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+RtlValidateHeap(
+ PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ BOOLEAN Result;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapValidate( HeapHandle, Flags, BaseAddress )
+ );
+
+ LockAcquired = FALSE;
+ Result = FALSE;
+ try {
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (HEAP_VALIDATE_SIGNATURE( Heap, "RtlValidateHeap" )) {
+ Flags |= Heap->ForceFlags;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+
+ if (BaseAddress == NULL) {
+ Result = RtlpValidateHeap( Heap, TRUE );
+ }
+ else {
+ Result = RtlpValidateHeapEntry( Heap, (PHEAP_ENTRY)BaseAddress - 1, "RtlValidateHeap" );
+ }
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ Result = FALSE;
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+BOOLEAN
+RtlSetUserValueHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN PVOID UserValue
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ BOOLEAN LockAcquired;
+ BOOLEAN Result;
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugSetUserValueHeap( HeapHandle, Flags, BaseAddress, UserValue );
+ }
+
+ Result = FALSE;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+ }
+ else
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ ExtraStuff->Settable = (ULONG)UserValue;
+ Result = TRUE;
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+RtlGetUserInfoHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ OUT PVOID *UserValue OPTIONAL,
+ OUT PULONG UserFlags OPTIONAL
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ BOOLEAN LockAcquired;
+ BOOLEAN Result;
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugGetUserInfoHeap( HeapHandle, Flags, BaseAddress, UserValue, UserFlags );
+ }
+
+ Result = FALSE;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ try {
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+ }
+ else {
+ if (BusyBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( BusyBlock );
+ if (ARGUMENT_PRESENT( UserValue )) {
+ *UserValue = (PVOID)ExtraStuff->Settable;
+ }
+ }
+
+ if (ARGUMENT_PRESENT( UserFlags )) {
+ *UserFlags = (BusyBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS) << 4;
+ }
+
+ Result = TRUE;
+ }
+
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ Result = FALSE;
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+BOOLEAN
+RtlSetUserFlagsHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN ULONG UserFlagsReset,
+ IN ULONG UserFlagsSet
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ BOOLEAN LockAcquired, Result;
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugSetUserFlagsHeap( HeapHandle, Flags, BaseAddress, UserFlagsReset, UserFlagsSet );
+ }
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ try {
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+ }
+ else {
+ BusyBlock->Flags &= ~(UserFlagsReset >> 4);
+ BusyBlock->Flags |= (UserFlagsSet >> 4);
+ Result = TRUE;
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ Result = FALSE;
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+ULONG
+RtlSizeHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ )
+
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_ENTRY BusyBlock;
+ ULONG BusySize;
+ BOOLEAN LockAcquired;
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugSizeHeap( HeapHandle, Flags, BaseAddress );
+ }
+
+ //
+ // No lock is required since nothing is modified and nothing
+ // outside the busy block is read.
+ //
+
+ BusyBlock = (PHEAP_ENTRY)BaseAddress - 1;
+ if (!(BusyBlock->Flags & HEAP_ENTRY_BUSY)) {
+ BusySize = (ULONG)-1;
+ SET_LAST_STATUS( STATUS_INVALID_PARAMETER );
+ } else if (BusyBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ BusySize = RtlpGetSizeOfBigBlock( BusyBlock );
+ } else {
+ BusySize = (BusyBlock->Size << HEAP_GRANULARITY_SHIFT) -
+ BusyBlock->UnusedBytes;
+ }
+ return BusySize;
+}
+
+
+NTSTATUS
+RtlExtendHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Base,
+ IN ULONG Size
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ NTSTATUS Status;
+ PHEAP_SEGMENT Segment;
+ BOOLEAN LockAcquired;
+ UCHAR SegmentIndex, EmptySegmentIndex;
+ ULONG CommitSize;
+ ULONG ReserveSize;
+ ULONG SegmentFlags;
+ PVOID CommittedBase;
+ PVOID UnCommittedBase;
+ MEMORY_BASIC_INFORMATION MemoryInformation;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapExtend( HeapHandle, Flags, Base, Size )
+ );
+
+ Status = NtQueryVirtualMemory( NtCurrentProcess(),
+ Base,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ if (MemoryInformation.State == MEM_FREE) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if (MemoryInformation.BaseAddress != Base) {
+ MemoryInformation.BaseAddress = (PCHAR)MemoryInformation.BaseAddress + PAGE_SIZE;
+ MemoryInformation.RegionSize -= PAGE_SIZE;
+ }
+
+ //
+ // Lock the free list.
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ EmptySegmentIndex = HEAP_MAXIMUM_SEGMENTS;
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment) {
+ if ((ULONG)Base >= (ULONG)Segment &&
+ (ULONG)Base < (ULONG)(Segment->LastValidEntry)
+ ) {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ else
+ if (Segment == NULL && EmptySegmentIndex == HEAP_MAXIMUM_SEGMENTS) {
+ EmptySegmentIndex = SegmentIndex;
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ if (NT_SUCCESS( Status )) {
+ SegmentFlags = HEAP_SEGMENT_USER_ALLOCATED;
+ CommittedBase = MemoryInformation.BaseAddress;
+ if (MemoryInformation.State == MEM_COMMIT) {
+ CommitSize = MemoryInformation.RegionSize;
+ UnCommittedBase = (PCHAR)CommittedBase + CommitSize;
+ Status = NtQueryVirtualMemory( NtCurrentProcess(),
+ UnCommittedBase,
+ MemoryBasicInformation,
+ &MemoryInformation,
+ sizeof( MemoryInformation ),
+ NULL
+ );
+ ReserveSize = CommitSize;
+ if (NT_SUCCESS( Status ) &&
+ MemoryInformation.State == MEM_RESERVE
+ ) {
+ ReserveSize += MemoryInformation.RegionSize;
+ }
+ }
+ else {
+ UnCommittedBase = CommittedBase;
+ ReserveSize = MemoryInformation.RegionSize;
+ }
+
+ if (ReserveSize < PAGE_SIZE ||
+ Size > ReserveSize
+ ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ else {
+ if (UnCommittedBase == CommittedBase) {
+ CommitSize = PAGE_SIZE;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&Segment,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ }
+ }
+
+ if (NT_SUCCESS( Status )) {
+ if (RtlpInitializeHeapSegment( Heap,
+ Segment,
+ EmptySegmentIndex,
+ 0,
+ Segment,
+ (PCHAR)Segment + CommitSize,
+ (PCHAR)Segment + ReserveSize
+ )
+ ) {
+ Status = STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ if (LockAcquired) {
+ LockAcquired = FALSE;
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Status;
+}
+
+
+ULONG
+RtlCompactHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_FREE_ENTRY FreeBlock;
+ PHEAP_SEGMENT Segment;
+ UCHAR SegmentIndex;
+ ULONG LargestFreeSize;
+ BOOLEAN LockAcquired;
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugCompactHeap( HeapHandle, Flags );
+ }
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ LargestFreeSize = 0;
+ try {
+ FreeBlock = RtlpCoalesceHeap( (PHEAP)HeapHandle );
+ if (FreeBlock != NULL) {
+ LargestFreeSize = FreeBlock->Size << HEAP_GRANULARITY_SHIFT;
+ }
+
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment && Segment->LargestUnCommittedRange > LargestFreeSize) {
+ LargestFreeSize = Segment->LargestUnCommittedRange;
+ }
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ SET_LAST_STATUS( GetExceptionCode() );
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return LargestFreeSize;
+}
+
+HEAP RtlpGlobalTagHeap;
+
+PHEAP_TAG_ENTRY
+RtlpAllocateTags(
+ PHEAP Heap,
+ ULONG NumberOfTags
+ )
+{
+ NTSTATUS Status;
+ ULONG TagIndex, ReserveSize, CommitSize;
+ PHEAP_TAG_ENTRY TagEntry;
+ USHORT CreatorBackTraceIndex;
+ USHORT MaximumTagIndex;
+ USHORT TagIndexFlag;
+
+ if (Heap == NULL) {
+ RtlpGlobalTagHeap.Signature = HEAP_SIGNATURE;
+ TagIndexFlag = HEAP_GLOBAL_TAG;
+ Heap = &RtlpGlobalTagHeap;
+ }
+ else {
+ TagIndexFlag = 0;
+ }
+#if i386
+ if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
+ CreatorBackTraceIndex = (USHORT)RtlLogStackBackTrace();
+ }
+ else
+#endif // i386
+ CreatorBackTraceIndex = 0;
+
+ if (Heap->TagEntries == NULL) {
+ MaximumTagIndex = HEAP_MAXIMUM_TAG & ~HEAP_GLOBAL_TAG;
+ ReserveSize = MaximumTagIndex * sizeof( HEAP_TAG_ENTRY );
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &Heap->TagEntries,
+ 0,
+ &ReserveSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+ Heap->MaximumTagIndex = MaximumTagIndex;
+ Heap->NextAvailableTagIndex = 0;
+ NumberOfTags += 1; // Add one for zero tag, as that is always reserved for heap name
+ }
+
+ if (NumberOfTags > (ULONG)(Heap->MaximumTagIndex - Heap->NextAvailableTagIndex)) {
+ return NULL;
+ }
+
+ TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
+ for (TagIndex = Heap->NextAvailableTagIndex;
+ TagIndex < Heap->NextAvailableTagIndex + NumberOfTags;
+ TagIndex++
+ ) {
+ if (((ULONG)TagEntry & (PAGE_SIZE-1)) == 0) {
+ CommitSize = PAGE_SIZE;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &TagEntry,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return NULL;
+ }
+ }
+
+ TagEntry->TagIndex = (USHORT)TagIndex | TagIndexFlag;
+ TagEntry->CreatorBackTraceIndex = CreatorBackTraceIndex;
+ TagEntry += 1;
+ }
+
+ TagEntry = Heap->TagEntries + Heap->NextAvailableTagIndex;
+ Heap->NextAvailableTagIndex += (USHORT)NumberOfTags;
+
+ return TagEntry;
+}
+
+static WCHAR RtlpPseudoTagNameBuffer[ 24 ];
+
+PWSTR
+RtlpGetTagName(
+ PHEAP Heap,
+ USHORT TagIndex
+ )
+{
+ if (TagIndex != 0) {
+ if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
+ TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
+ if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG &&
+ Heap->PseudoTagEntries != NULL
+ ) {
+ if (TagIndex == 0) {
+ swprintf( RtlpPseudoTagNameBuffer, L"Objects>%4u",
+ HEAP_MAXIMUM_FREELISTS << HEAP_GRANULARITY_SHIFT
+ );
+ }
+ else
+ if (TagIndex < HEAP_MAXIMUM_FREELISTS) {
+ swprintf( RtlpPseudoTagNameBuffer, L"Objects=%4u", TagIndex << HEAP_GRANULARITY_SHIFT );
+ }
+ else {
+ swprintf( RtlpPseudoTagNameBuffer, L"VirtualAlloc" );
+ }
+
+ return RtlpPseudoTagNameBuffer;
+ }
+ }
+ else
+ if (TagIndex & HEAP_GLOBAL_TAG) {
+ TagIndex &= ~HEAP_GLOBAL_TAG;
+ if (TagIndex < RtlpGlobalTagHeap.NextAvailableTagIndex &&
+ RtlpGlobalTagHeap.TagEntries != NULL
+ ) {
+ return RtlpGlobalTagHeap.TagEntries[ TagIndex ].TagName;
+ }
+ }
+ else
+ if (TagIndex < Heap->NextAvailableTagIndex &&
+ Heap->TagEntries != NULL
+ ) {
+ return Heap->TagEntries[ TagIndex ].TagName;
+ }
+ }
+
+ return NULL;
+}
+
+
+USHORT
+RtlpUpdateTagEntry(
+ PHEAP Heap,
+ USHORT TagIndex,
+ ULONG OldSize, // Only valid for ReAllocation and Free actions
+ ULONG NewSize, // Only valid for ReAllocation and Allocation actions
+ HEAP_TAG_ACTION Action
+ )
+{
+ PHEAP_TAG_ENTRY TagEntry;
+
+ if (Action >= FreeAction) {
+ if (TagIndex == 0) {
+ return 0;
+ }
+
+ if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
+ TagIndex &= ~HEAP_PSEUDO_TAG_FLAG;
+ if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG &&
+ Heap->PseudoTagEntries != NULL
+ ) {
+ TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
+ TagIndex |= HEAP_PSEUDO_TAG_FLAG;
+ }
+ else {
+ return 0;
+ }
+ }
+ else
+ if (TagIndex & HEAP_GLOBAL_TAG) {
+ TagIndex &= ~HEAP_GLOBAL_TAG;
+ if (TagIndex < RtlpGlobalTagHeap.NextAvailableTagIndex &&
+ RtlpGlobalTagHeap.TagEntries != NULL
+ ) {
+ TagEntry = &RtlpGlobalTagHeap.TagEntries[ TagIndex ];
+ TagIndex |= HEAP_GLOBAL_TAG;
+ }
+ else {
+ return 0;
+ }
+ }
+ else
+ if (TagIndex < Heap->NextAvailableTagIndex &&
+ Heap->TagEntries != NULL
+ ) {
+ TagEntry = &Heap->TagEntries[ TagIndex ];
+ }
+ else {
+ return 0;
+ }
+
+ TagEntry->Frees += 1;
+ TagEntry->Size -= OldSize;
+
+ if (Action >= ReAllocationAction) {
+ if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
+ TagIndex = (USHORT)(NewSize < HEAP_MAXIMUM_FREELISTS ?
+ NewSize :
+ (Action == VirtualReAllocationAction ? HEAP_MAXIMUM_FREELISTS : 0)
+ );
+ TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
+ TagIndex |= HEAP_PSEUDO_TAG_FLAG;
+ }
+
+ TagEntry->Allocs += 1;
+ TagEntry->Size += NewSize;
+ }
+ }
+ else {
+ if (TagIndex != 0 &&
+ TagIndex < Heap->NextAvailableTagIndex &&
+ Heap->TagEntries != NULL
+ ) {
+ TagEntry = &Heap->TagEntries[ TagIndex ];
+ }
+ else
+ if (TagIndex & HEAP_GLOBAL_TAG) {
+ TagIndex &= ~HEAP_GLOBAL_TAG;
+ Heap = &RtlpGlobalTagHeap;
+ if (TagIndex < Heap->NextAvailableTagIndex &&
+ Heap->TagEntries != NULL
+ ) {
+ TagEntry = &Heap->TagEntries[ TagIndex ];
+ TagIndex |= HEAP_GLOBAL_TAG;
+ }
+ else {
+ return 0;
+ }
+ }
+ else
+ if (Heap->PseudoTagEntries != NULL) {
+ TagIndex = (USHORT)(NewSize < HEAP_MAXIMUM_FREELISTS ?
+ NewSize :
+ (Action == VirtualAllocationAction ? HEAP_MAXIMUM_FREELISTS : 0)
+ );
+
+ TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
+ TagIndex |= HEAP_PSEUDO_TAG_FLAG;
+ }
+ else {
+ return 0;
+ }
+
+ TagEntry->Allocs += 1;
+ TagEntry->Size += NewSize;
+ }
+
+ return TagIndex;
+}
+
+VOID
+RtlpDestroyTags(
+ PHEAP Heap
+ )
+{
+ NTSTATUS Status;
+ ULONG RegionSize;
+
+ if (Heap->TagEntries != NULL) {
+ RegionSize = 0;
+ Status = NtFreeVirtualMemory( NtCurrentProcess(),
+ &Heap->TagEntries,
+ &RegionSize,
+ MEM_RELEASE
+ );
+ if (NT_SUCCESS( Status )) {
+ Heap->TagEntries = NULL;
+ }
+ }
+}
+
+ULONG
+RtlCreateTagHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PWSTR TagPrefix OPTIONAL,
+ IN PWSTR TagNames
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ ULONG TagIndex;
+ ULONG NumberOfTags, MaxTagNameLength, TagPrefixLength;
+ PWSTR s, s1, HeapName;
+ PHEAP_TAG_ENTRY TagEntry;
+
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ return 0;
+ }
+
+ LockAcquired = FALSE;
+ if (Heap != NULL) {
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle, 0 );
+
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugCreateTagHeap( HeapHandle, Flags, TagPrefix, TagNames );
+ }
+
+ //
+ // Lock the heap
+ //
+
+ Flags |= Heap->ForceFlags;
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ }
+
+ TagIndex = 0;
+ NumberOfTags = 0;
+ if (*TagNames == L'!') {
+ HeapName = TagNames + 1;
+ while (*TagNames++) {
+ }
+ }
+ else {
+ HeapName = NULL;
+ }
+
+ s = TagNames;
+ while (*s) {
+ while (*s++) {
+ }
+ NumberOfTags += 1;
+ }
+
+ if (NumberOfTags > 0) {
+ TagEntry = RtlpAllocateTags( Heap, NumberOfTags );
+ if (TagEntry != NULL) {
+ MaxTagNameLength = (sizeof( TagEntry->TagName ) / sizeof( WCHAR )) - 1;
+ TagIndex = TagEntry->TagIndex;
+ if (TagIndex == 0) {
+ if (HeapName != NULL ) {
+ wcsncpy( TagEntry->TagName, HeapName, MaxTagNameLength );
+ }
+ TagEntry += 1;
+ TagIndex = TagEntry->TagIndex;
+ }
+ else
+ if (TagIndex == HEAP_GLOBAL_TAG) {
+ wcsncpy( TagEntry->TagName, L"GlobalTags", MaxTagNameLength );
+ TagEntry += 1;
+ TagIndex = TagEntry->TagIndex;
+ }
+
+ if (ARGUMENT_PRESENT( TagPrefix ) && (TagPrefixLength = wcslen( TagPrefix ))) {
+ if (TagPrefixLength >= MaxTagNameLength-4) {
+ TagPrefix = NULL;
+ }
+ else {
+ MaxTagNameLength -= TagPrefixLength;
+ }
+ }
+ else {
+ TagPrefix = NULL;
+ }
+
+ s = TagNames;
+ while (*s) {
+ s1 = TagEntry->TagName;
+ if (ARGUMENT_PRESENT( TagPrefix )) {
+ wcscpy( s1, TagPrefix );
+ s1 += TagPrefixLength;
+ }
+
+ wcsncpy( s1, s, MaxTagNameLength );
+ while (*s++) {
+ }
+ TagEntry += 1;
+ }
+ }
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return TagIndex << HEAP_TAG_SHIFT;
+}
+
+
+PWSTR
+RtlQueryTagHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN USHORT TagIndex,
+ IN BOOLEAN ResetCounters,
+ OUT PRTL_HEAP_TAG_INFO TagInfo OPTIONAL
+ )
+{
+ PHEAP Heap = (PHEAP)HeapHandle;
+ BOOLEAN LockAcquired;
+ PHEAP_TAG_ENTRY TagEntry;
+ PWSTR Result;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN( HeapHandle, NULL );
+
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ return NULL;
+ }
+
+ LockAcquired = FALSE;
+ if (Heap != NULL) {
+
+ //
+ // Validate that HeapAddress points to a HEAP structure.
+ //
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugQueryTagHeap( HeapHandle, Flags, TagIndex, ResetCounters, TagInfo );
+ }
+
+ //
+ // Lock the heap
+ //
+
+ Flags |= Heap->ForceFlags;
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ }
+
+ Result = NULL;
+ if (TagIndex < Heap->NextAvailableTagIndex && Heap->TagEntries != NULL) {
+ TagEntry = Heap->TagEntries + TagIndex;
+ if (ARGUMENT_PRESENT( TagInfo )) {
+ TagInfo->NumberOfAllocations = TagEntry->Allocs;
+ TagInfo->NumberOfFrees = TagEntry->Frees;
+ TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
+ }
+
+ if (ResetCounters) {
+ TagEntry->Allocs = 0;
+ TagEntry->Frees = 0;
+ TagEntry->Size = 0;
+ }
+
+ Result = &TagEntry->TagName[ 0 ];
+ }
+ else
+ if (TagIndex & HEAP_PSEUDO_TAG_FLAG) {
+ TagIndex ^= HEAP_PSEUDO_TAG_FLAG;
+ if (TagIndex < HEAP_NUMBER_OF_PSEUDO_TAG && Heap->PseudoTagEntries != NULL) {
+ TagEntry = (PHEAP_TAG_ENTRY)(Heap->PseudoTagEntries + TagIndex);
+ if (ARGUMENT_PRESENT( TagInfo )) {
+ TagInfo->NumberOfAllocations = TagEntry->Allocs;
+ TagInfo->NumberOfFrees = TagEntry->Frees;
+ TagInfo->BytesAllocated = TagEntry->Size << HEAP_GRANULARITY_SHIFT;
+ }
+
+ if (ResetCounters) {
+ TagEntry->Allocs = 0;
+ TagEntry->Frees = 0;
+ TagEntry->Size = 0;
+ }
+
+ Result = L"";
+ }
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Result;
+}
+
+
+typedef struct _RTL_HEAP_USAGE_INTERNAL {
+ PVOID Base;
+ ULONG ReservedSize;
+ ULONG CommittedSize;
+ PRTL_HEAP_USAGE_ENTRY FreeList;
+ PRTL_HEAP_USAGE_ENTRY LargeEntriesSentinal;
+ ULONG Reserved;
+} RTL_HEAP_USAGE_INTERNAL, *PRTL_HEAP_USAGE_INTERNAL;
+
+
+NTSTATUS
+RtlpAllocateHeapUsageEntry(
+ PRTL_HEAP_USAGE_INTERNAL Buffer,
+ PRTL_HEAP_USAGE_ENTRY *pp
+ )
+{
+ NTSTATUS Status;
+ PRTL_HEAP_USAGE_ENTRY p;
+ PVOID CommitAddress;
+ ULONG PageSize;
+
+ if (Buffer->FreeList == NULL) {
+ if (Buffer->CommittedSize >= Buffer->ReservedSize) {
+ return STATUS_NO_MEMORY;
+ }
+
+ PageSize = PAGE_SIZE;
+ CommitAddress = (PCHAR)Buffer->Base + Buffer->CommittedSize;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &CommitAddress,
+ 0,
+ &PageSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+ Buffer->CommittedSize += PageSize;
+
+ Buffer->FreeList = CommitAddress;
+ p = Buffer->FreeList;
+ while (PageSize != 0) {
+ p->Next = (p+1);
+ p += 1;
+ PageSize -= sizeof( *p );
+ }
+ p -= 1;
+ p->Next = NULL;
+ }
+
+ p = Buffer->FreeList;
+ Buffer->FreeList = p->Next;
+ p->Next = NULL;
+ if (*pp) {
+ (*pp)->Next = p;
+ }
+ *pp = p;
+ return STATUS_SUCCESS;
+}
+
+
+PRTL_HEAP_USAGE_ENTRY
+RtlpFreeHeapUsageEntry(
+ PRTL_HEAP_USAGE_INTERNAL Buffer,
+ PRTL_HEAP_USAGE_ENTRY p
+ )
+{
+ PRTL_HEAP_USAGE_ENTRY pTmp;
+
+ if (p != NULL) {
+ pTmp = p->Next;
+ p->Next = Buffer->FreeList;
+ Buffer->FreeList = p;
+ }
+ else {
+ pTmp = NULL;
+ }
+ return pTmp;
+}
+
+
+NTSTATUS
+RtlUsageHeap(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN OUT PRTL_HEAP_USAGE Usage
+ )
+{
+ NTSTATUS Status;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PRTL_HEAP_USAGE_INTERNAL Buffer;
+ PHEAP_SEGMENT Segment;
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
+ PHEAP_ENTRY CurrentBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ PLIST_ENTRY Head, Next;
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+ ULONG BytesFree;
+ UCHAR SegmentIndex;
+ BOOLEAN LockAcquired;
+ BOOLEAN VirtualAllocBlockSeen;
+ PRTL_HEAP_USAGE_ENTRY pOldEntries, pNewEntries, pNewEntry;
+ PRTL_HEAP_USAGE_ENTRY *ppEntries, *ppAddedEntries, *ppRemovedEntries, *pp;
+ PVOID DataAddress;
+ ULONG DataSize;
+
+ Flags |= Heap->ForceFlags;
+
+ if (DEBUG_HEAP( Flags )) {
+ return RtlDebugUsageHeap( HeapHandle, Flags, Usage );
+ }
+
+ if (Usage->Length != sizeof( RTL_HEAP_USAGE )) {
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+ Usage->BytesAllocated = 0;
+ Usage->BytesCommitted = 0;
+ Usage->BytesReserved = 0;
+ Usage->BytesReservedMaximum = 0;
+ Buffer = (PRTL_HEAP_USAGE_INTERNAL)&Usage->Reserved[ 0 ];
+ if (Buffer->Base == NULL && (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
+ Buffer->ReservedSize = 4 * 1024 * 1024;
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &Buffer->Base,
+ 0,
+ &Buffer->ReservedSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ Buffer->CommittedSize = 0;
+ Buffer->FreeList = NULL;
+ Buffer->LargeEntriesSentinal = NULL;
+ }
+ else
+ if (Buffer->Base != NULL && (Flags & HEAP_USAGE_FREE_BUFFER)) {
+ Buffer->ReservedSize = 0;
+ Status = NtFreeVirtualMemory( NtCurrentProcess(),
+ &Buffer->Base,
+ &Buffer->ReservedSize,
+ MEM_RELEASE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ RtlZeroMemory( Buffer, sizeof( *Buffer ) );
+ }
+
+ Flags |= Heap->ForceFlags;
+
+ //
+ // Lock the heap
+ //
+
+ if (!(Flags & HEAP_NO_SERIALIZE)) {
+ RtlAcquireLockRoutine( Heap->LockVariable );
+ LockAcquired = TRUE;
+ }
+ else {
+ LockAcquired = FALSE;
+ }
+
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment) {
+ Usage->BytesCommitted += (Segment->NumberOfPages -
+ Segment->NumberOfUnCommittedPages) * PAGE_SIZE;
+
+ Usage->BytesReserved += Segment->NumberOfPages * PAGE_SIZE;
+ }
+ else
+ if (Heap->Flags & HEAP_GROWABLE) {
+ Usage->BytesReservedMaximum += Heap->SegmentReserve;
+ }
+ }
+ Usage->BytesReservedMaximum += Usage->BytesReserved;
+ Usage->BytesAllocated = Usage->BytesCommitted - (Heap->TotalFreeSize << HEAP_GRANULARITY_SHIFT);
+
+ Head = &Heap->VirtualAllocdBlocks;
+ Next = Head->Flink;
+ while (Head != Next) {
+ VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
+ Usage->BytesAllocated += VirtualAllocBlock->CommitSize;
+ Usage->BytesCommitted += VirtualAllocBlock->CommitSize;
+ Next = Next->Flink;
+ }
+
+ Status = STATUS_SUCCESS;
+ if (Buffer->Base != NULL && (Flags & HEAP_USAGE_ALLOCATED_BLOCKS)) {
+ pOldEntries = Usage->Entries;
+ ppEntries = &Usage->Entries;
+ *ppEntries = NULL;
+
+ ppAddedEntries = &Usage->AddedEntries;
+ while (*ppAddedEntries = RtlpFreeHeapUsageEntry( Buffer, *ppAddedEntries )) {
+ }
+
+ ppRemovedEntries = &Usage->RemovedEntries;
+ while (*ppRemovedEntries = RtlpFreeHeapUsageEntry( Buffer, *ppRemovedEntries )) {
+ }
+
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment) {
+ if (Segment->BaseAddress == Heap) {
+ CurrentBlock = &Heap->Entry;
+ }
+ else {
+ CurrentBlock = &Segment->Entry;
+ }
+ while (CurrentBlock < Segment->LastValidEntry) {
+ if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
+ DataAddress = (CurrentBlock+1);
+ DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
+ CurrentBlock->UnusedBytes;
+keepLookingAtOldEntries:
+ if (pOldEntries == Buffer->LargeEntriesSentinal) {
+ goto keepLookingAtNewEntries;
+ }
+
+ if (pOldEntries->Address == DataAddress &&
+ pOldEntries->Size == DataSize
+ ) {
+ //
+ // Same block, keep in entries list
+ //
+ *ppEntries = pOldEntries;
+ pOldEntries = pOldEntries->Next;
+ ppEntries = &(*ppEntries)->Next;
+ *ppEntries = NULL;
+ }
+ else
+ if (pOldEntries->Address <= DataAddress) {
+ *ppRemovedEntries = pOldEntries;
+ pOldEntries = pOldEntries->Next;
+ ppRemovedEntries = &(*ppRemovedEntries)->Next;
+ *ppRemovedEntries = NULL;
+ goto keepLookingAtOldEntries;
+ }
+ else {
+keepLookingAtNewEntries:
+ pNewEntry = NULL;
+ Status = RtlpAllocateHeapUsageEntry( Buffer, &pNewEntry );
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+ pNewEntry->Address = DataAddress;
+ pNewEntry->Size = DataSize;
+ if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( CurrentBlock );
+#if i386
+ pNewEntry->AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex;
+#endif // i386
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ pNewEntry->TagIndex = 0;
+ }
+ else {
+ pNewEntry->TagIndex = ExtraStuff->TagIndex;
+ }
+ }
+ else {
+#if i386
+ pNewEntry->AllocatorBackTraceIndex = 0;
+#endif // i386
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ pNewEntry->TagIndex = 0;
+ }
+ else {
+ pNewEntry->TagIndex = CurrentBlock->SmallTagIndex;
+ }
+ }
+
+ Status = RtlpAllocateHeapUsageEntry( Buffer, ppAddedEntries );
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+ **ppAddedEntries = *pNewEntry;
+ ppAddedEntries = &(*ppAddedEntries)->Next;
+ *ppAddedEntries = NULL;
+
+ pNewEntry->Next = NULL;
+ *ppEntries = pNewEntry;
+ ppEntries = &pNewEntry->Next;
+ }
+ }
+
+ if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
+ CurrentBlock += CurrentBlock->Size;
+ if (CurrentBlock < Segment->LastValidEntry) {
+ UnCommittedRange = Segment->UnCommittedRanges;
+ while (UnCommittedRange != NULL && UnCommittedRange->Address != (ULONG)CurrentBlock ) {
+ UnCommittedRange = UnCommittedRange->Next;
+ }
+
+ if (UnCommittedRange == NULL) {
+ CurrentBlock = Segment->LastValidEntry;
+ }
+ else {
+ CurrentBlock = (PHEAP_ENTRY)(UnCommittedRange->Address +
+ UnCommittedRange->Size
+ );
+ }
+ }
+ }
+ else {
+ CurrentBlock += CurrentBlock->Size;
+ }
+ }
+ }
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Head = &Heap->VirtualAllocdBlocks;
+ Next = Head->Flink;
+ VirtualAllocBlockSeen = FALSE;
+ while (Head != Next) {
+ VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
+
+ pNewEntry = NULL;
+ Status = RtlpAllocateHeapUsageEntry( Buffer, &pNewEntry );
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+ VirtualAllocBlockSeen = TRUE;
+
+ pNewEntry->Address = (VirtualAllocBlock + 1);
+ pNewEntry->Size = VirtualAllocBlock->CommitSize - VirtualAllocBlock->BusyBlock.Size;
+#if i386
+ pNewEntry->AllocatorBackTraceIndex = VirtualAllocBlock->ExtraStuff.AllocatorBackTraceIndex;
+#endif // i386
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ pNewEntry->TagIndex = 0;
+ }
+ else {
+ pNewEntry->TagIndex = VirtualAllocBlock->ExtraStuff.TagIndex;
+ }
+
+ pp = ppEntries;
+ while (*pp) {
+ if ((*pp)->Address >= pNewEntry->Address) {
+ break;
+ }
+
+ pp = &(*pp)->Next;
+ }
+ pNewEntry->Next = *pp;
+ *pp = pNewEntry;
+
+ Next = Next->Flink;
+ }
+
+
+ if (NT_SUCCESS( Status )) {
+ pOldEntries = Buffer->LargeEntriesSentinal;
+ Buffer->LargeEntriesSentinal = *ppEntries;
+ while (pOldEntries != NULL) {
+ if (*ppEntries != NULL &&
+ pOldEntries->Address == (*ppEntries)->Address &&
+ pOldEntries->Size == (*ppEntries)->Size
+ ) {
+ ppEntries = &(*ppEntries)->Next;
+ pOldEntries = RtlpFreeHeapUsageEntry( Buffer, pOldEntries );
+ }
+ else
+ if (*ppEntries == NULL ||
+ pOldEntries->Address < (*ppEntries)->Address
+ ) {
+ *ppRemovedEntries = pOldEntries;
+ pOldEntries = pOldEntries->Next;
+ ppRemovedEntries = &(*ppRemovedEntries)->Next;
+ *ppRemovedEntries = NULL;
+ }
+ else {
+ *ppAddedEntries = pOldEntries;
+ pOldEntries = pOldEntries->Next;
+ **ppAddedEntries = **ppEntries;
+ ppAddedEntries = &(*ppAddedEntries)->Next;
+ *ppAddedEntries = NULL;
+ }
+ }
+
+ while (pNewEntry = *ppEntries) {
+ Status = RtlpAllocateHeapUsageEntry( Buffer, ppAddedEntries );
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+ **ppAddedEntries = *pNewEntry;
+ ppAddedEntries = &(*ppAddedEntries)->Next;
+ *ppAddedEntries = NULL;
+ ppEntries = &pNewEntry->Next;
+ }
+
+ if (Usage->AddedEntries != NULL || Usage->RemovedEntries != NULL) {
+ Status = STATUS_MORE_ENTRIES;
+ }
+ }
+ }
+ }
+
+ //
+ // Unlock the heap
+ //
+
+ if (LockAcquired) {
+ RtlReleaseLockRoutine( Heap->LockVariable );
+ }
+
+ return Status;
+} // RtlUsageHeap
+
+
+
+NTSTATUS
+RtlWalkHeap(
+ IN PVOID HeapHandle,
+ IN OUT PRTL_HEAP_WALK_ENTRY Entry
+ )
+{
+ NTSTATUS Status;
+ PHEAP Heap = (PHEAP)HeapHandle;
+ PHEAP_SEGMENT Segment;
+ UCHAR SegmentIndex;
+ PHEAP_ENTRY CurrentBlock;
+ PHEAP_ENTRY_EXTRA ExtraStuff;
+ PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
+ PLIST_ENTRY Next, Head;
+ PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapWalk( HeapHandle, Entry )
+ );
+
+ if (DEBUG_HEAP( Heap->Flags )) {
+ if (!RtlDebugWalkHeap( HeapHandle, Entry )) {
+ return STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ Status = STATUS_SUCCESS;
+ if (Entry->DataAddress == NULL) {
+ SegmentIndex = 0;
+nextSegment:
+ CurrentBlock = NULL;
+ Segment = NULL;
+ while (SegmentIndex < HEAP_MAXIMUM_SEGMENTS &&
+ (Segment = Heap->Segments[ SegmentIndex ]) == NULL
+ ) {
+ SegmentIndex += 1;
+ }
+
+ if (Segment == NULL) {
+ Head = &Heap->VirtualAllocdBlocks;
+ Next = Head->Flink;
+ if (Next == Head) {
+ Status = STATUS_NO_MORE_ENTRIES;
+ }
+ else {
+ VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
+ CurrentBlock = &VirtualAllocBlock->BusyBlock;
+ }
+ }
+ else {
+ Entry->DataAddress = Segment;
+ Entry->DataSize = 0;
+ Entry->OverheadBytes = sizeof( *Segment );
+ Entry->Flags = RTL_HEAP_SEGMENT;
+ Entry->SegmentIndex = SegmentIndex;
+ Entry->Segment.CommittedSize = (Segment->NumberOfPages -
+ Segment->NumberOfUnCommittedPages
+ ) * PAGE_SIZE;
+ Entry->Segment.UnCommittedSize = Segment->NumberOfUnCommittedPages * PAGE_SIZE;
+ Entry->Segment.FirstEntry = (Segment->FirstEntry->Flags & HEAP_ENTRY_BUSY) ?
+ ((PHEAP_ENTRY)Segment->FirstEntry + 1) :
+ (PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Segment->FirstEntry + 1);
+ Entry->Segment.LastEntry = Segment->LastValidEntry;
+ }
+ }
+ else
+ if (Entry->Flags & (RTL_HEAP_SEGMENT | RTL_HEAP_UNCOMMITTED_RANGE)) {
+ if ((SegmentIndex = Entry->SegmentIndex) >= HEAP_MAXIMUM_SEGMENTS) {
+ Status = STATUS_INVALID_ADDRESS;
+ CurrentBlock = NULL;
+ }
+ else {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if (Segment == NULL) {
+ Status = STATUS_INVALID_ADDRESS;
+ CurrentBlock = NULL;
+ }
+ else
+ if (Entry->Flags & RTL_HEAP_SEGMENT) {
+ CurrentBlock = (PHEAP_ENTRY)Segment->FirstEntry;
+ }
+ else {
+ CurrentBlock = (PHEAP_ENTRY)((PCHAR)Entry->DataAddress + Entry->DataSize);
+ if (CurrentBlock >= Segment->LastValidEntry) {
+ SegmentIndex += 1;
+ goto nextSegment;
+ }
+ }
+ }
+ }
+ else {
+ if (Entry->Flags & HEAP_ENTRY_BUSY) {
+ CurrentBlock = ((PHEAP_ENTRY)Entry->DataAddress - 1);
+ if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ Head = &Heap->VirtualAllocdBlocks;
+ VirtualAllocBlock = CONTAINING_RECORD( CurrentBlock, HEAP_VIRTUAL_ALLOC_ENTRY, BusyBlock );
+ Next = VirtualAllocBlock->Entry.Flink;
+ if (Next == Head) {
+ Status = STATUS_NO_MORE_ENTRIES;
+ }
+ else {
+ VirtualAllocBlock = CONTAINING_RECORD( Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry );
+ CurrentBlock = &VirtualAllocBlock->BusyBlock;
+ }
+ }
+ else {
+ Segment = Heap->Segments[ SegmentIndex = CurrentBlock->SegmentIndex ];
+ if (Segment == NULL) {
+ Status = STATUS_INVALID_ADDRESS;
+ CurrentBlock = NULL;
+ }
+ else
+ if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
+findUncommittedRange:
+ CurrentBlock += CurrentBlock->Size;
+ if (CurrentBlock >= Segment->LastValidEntry) {
+ SegmentIndex += 1;
+ goto nextSegment;
+ }
+
+ pp = &Segment->UnCommittedRanges;
+ while ((UnCommittedRange = *pp) && UnCommittedRange->Address != (ULONG)CurrentBlock ) {
+ pp = &UnCommittedRange->Next;
+ }
+
+ if (UnCommittedRange == NULL) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ Entry->DataAddress = (PVOID)UnCommittedRange->Address;
+ Entry->DataSize = UnCommittedRange->Size;
+ Entry->OverheadBytes = 0;
+ Entry->SegmentIndex = SegmentIndex;
+ Entry->Flags = RTL_HEAP_UNCOMMITTED_RANGE;
+ }
+
+ CurrentBlock = NULL;
+ }
+ else {
+ CurrentBlock += CurrentBlock->Size;
+ }
+ }
+ }
+ else {
+ CurrentBlock = (PHEAP_ENTRY)((PHEAP_FREE_ENTRY)Entry->DataAddress - 1);
+ Segment = Heap->Segments[ SegmentIndex = CurrentBlock->SegmentIndex ];
+ if (Segment == NULL) {
+ Status = STATUS_INVALID_ADDRESS;
+ CurrentBlock = NULL;
+ }
+ else
+ if (CurrentBlock->Flags & HEAP_ENTRY_LAST_ENTRY) {
+ goto findUncommittedRange;
+ }
+ else {
+ CurrentBlock += CurrentBlock->Size;
+ }
+ }
+ }
+
+ if (CurrentBlock != NULL) {
+ if (CurrentBlock->Flags & HEAP_ENTRY_BUSY) {
+ Entry->DataAddress = (CurrentBlock+1);
+ if (CurrentBlock->Flags & HEAP_ENTRY_VIRTUAL_ALLOC) {
+ Entry->DataSize = RtlpGetSizeOfBigBlock( CurrentBlock );
+ Entry->OverheadBytes = sizeof( *VirtualAllocBlock ) + CurrentBlock->Size;
+ Entry->SegmentIndex = HEAP_MAXIMUM_SEGMENTS;
+ Entry->Flags = RTL_HEAP_BUSY | HEAP_ENTRY_VIRTUAL_ALLOC;
+ }
+ else {
+ Entry->DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
+ CurrentBlock->UnusedBytes;
+ Entry->OverheadBytes = CurrentBlock->UnusedBytes;
+ Entry->SegmentIndex = CurrentBlock->SegmentIndex;
+ Entry->Flags = RTL_HEAP_BUSY;
+ }
+
+ if (CurrentBlock->Flags & HEAP_ENTRY_EXTRA_PRESENT) {
+ ExtraStuff = RtlpGetExtraStuffPointer( CurrentBlock );
+ Entry->Block.Settable = ExtraStuff->Settable;
+#if i386
+ Entry->Block.AllocatorBackTraceIndex = ExtraStuff->AllocatorBackTraceIndex;
+#endif // i386
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ Entry->Block.TagIndex = 0;
+ }
+ else {
+ Entry->Block.TagIndex = ExtraStuff->TagIndex;
+ }
+
+ Entry->Flags |= RTL_HEAP_SETTABLE_VALUE;
+ }
+ else {
+ if (!IS_HEAP_TAGGING_ENABLED()) {
+ Entry->Block.TagIndex = 0;
+ }
+ else {
+ Entry->Block.TagIndex = CurrentBlock->SmallTagIndex;
+ }
+ }
+
+ Entry->Flags |= CurrentBlock->Flags & HEAP_ENTRY_SETTABLE_FLAGS;
+ }
+ else {
+ Entry->DataAddress = ((PHEAP_FREE_ENTRY)CurrentBlock+1);
+ Entry->DataSize = (CurrentBlock->Size << HEAP_GRANULARITY_SHIFT) -
+ sizeof( HEAP_FREE_ENTRY );
+ Entry->OverheadBytes = sizeof( HEAP_FREE_ENTRY );
+ Entry->SegmentIndex = CurrentBlock->SegmentIndex;
+ Entry->Flags = 0;
+ }
+ }
+
+ return Status;
+}
+
+VOID
+RtlProtectHeap(
+ IN PVOID HeapHandle,
+ IN BOOLEAN MakeReadOnly
+ )
+{
+ PHEAP Heap;
+ UCHAR SegmentIndex;
+ PHEAP_SEGMENT Segment;
+ MEMORY_BASIC_INFORMATION VaInfo;
+ NTSTATUS Status;
+ PVOID Address;
+ PVOID ProtectAddress;
+ ULONG Size;
+ ULONG OldProtect;
+ ULONG NewProtect;
+
+ Heap = (PHEAP)HeapHandle;
+ for (SegmentIndex=0; SegmentIndex<HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
+ Segment = Heap->Segments[ SegmentIndex ];
+ if ( Segment ) {
+ Address = Segment->BaseAddress;
+ while ((ULONG)Address < (ULONG)(Segment->LastValidEntry)) {
+ Status = ZwQueryVirtualMemory( NtCurrentProcess(),
+ Address,
+ MemoryBasicInformation,
+ &VaInfo,
+ sizeof(VaInfo),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ HeapDebugPrint(( "VirtualQuery Failed 0x%08x %x\n", Address, Status ));
+ return;
+ }
+
+ //
+ // Found a commited block. No set it's protection
+ //
+
+ if (VaInfo.State == MEM_COMMIT) {
+ Size = VaInfo.RegionSize;
+ ProtectAddress = Address;
+ if (MakeReadOnly) {
+ NewProtect = PAGE_READONLY;
+ }
+ else {
+ NewProtect = PAGE_READWRITE;
+ }
+ Status = ZwProtectVirtualMemory( NtCurrentProcess(),
+ &ProtectAddress,
+ &Size,
+ NewProtect,
+ &OldProtect
+ );
+ if (!NT_SUCCESS( Status )) {
+ HeapDebugPrint(( "VirtualProtect Failed 0x%08x %x\n", Address, Status ));
+ return;
+ }
+ }
+
+ Address = (PVOID)((ULONG)Address + VaInfo.RegionSize);
+ }
+ }
+ }
+
+ return;
+}
+
+
+BOOLEAN
+RtlpHeapIsLocked(
+ IN PVOID HeapHandle
+ )
+ {
+ PHEAP Heap;
+
+ IF_DEBUG_PAGE_HEAP_THEN_RETURN(
+ HeapHandle,
+ RtlpDebugPageHeapIsLocked( HeapHandle )
+ );
+
+ Heap = (PHEAP)HeapHandle;
+
+ return (( Heap->LockVariable != NULL ) &&
+ ( Heap->LockVariable->Lock.CriticalSection.OwningThread ||
+ Heap->LockVariable->Lock.CriticalSection.LockCount != -1 ));
+ }
diff --git a/private/ntos/rtl/heappage.c b/private/ntos/rtl/heappage.c
new file mode 100644
index 000000000..fe89c4082
--- /dev/null
+++ b/private/ntos/rtl/heappage.c
@@ -0,0 +1,3096 @@
+
+//
+// heappage.c
+//
+// Implementation of NT RtlHeap family of APIs for debugging
+// applications with heap usage bugs. Each allocation returned to
+// the calling app is placed at the end of a virtual page such that
+// the following virtual page is protected (ie, NO_ACCESS).
+// So, when the errant app attempts to reference or modify memory
+// beyond the allocated portion of a heap block, an access violation
+// is immediately caused. This facilitates debugging the app
+// because the access violation occurs at the exact point in the
+// app where the heap corruption or abuse would occur. Note that
+// significantly more memory (pagefile) is required to run an app
+// using this heap implementation as opposed to the retail heap
+// manager.
+//
+// Author: Tom McGuire (tommcg)
+// Date: 01/06/1995
+//
+// Copyright (C) 1994-1996, Microsoft
+//
+
+#include "ntrtlp.h"
+#include "heappage.h" // external interface (hooks) to debug heap manager
+
+int __cdecl sprintf(char *, const char *, ...);
+
+//
+// Remainder of entire file is wrapped with #ifdef DEBUG_PAGE_HEAP so that
+// it will compile away to nothing if DEBUG_PAGE_HEAP is not defined in
+// heappage.h
+//
+
+#ifdef DEBUG_PAGE_HEAP
+
+#if defined(_X86_)
+ #ifndef PAGE_SIZE
+ #define PAGE_SIZE 0x1000
+ #endif
+ #define USER_ALIGNMENT 8
+#elif defined(_MIPS_)
+ #ifndef PAGE_SIZE
+ #define PAGE_SIZE 0x1000
+ #endif
+ #define USER_ALIGNMENT 8
+#elif defined(_PPC_)
+ #ifndef PAGE_SIZE
+ #define PAGE_SIZE 0x1000
+ #endif
+ #define USER_ALIGNMENT 8
+#elif defined(_ALPHA_)
+ #ifndef PAGE_SIZE
+ #define PAGE_SIZE 0x2000
+ #endif
+ #define USER_ALIGNMENT 8
+#else
+ #error // platform not defined
+#endif
+
+
+#define DPH_HEAP_ROOT_SIGNATURE 0xFFEEDDCC
+#define FILL_BYTE 0xEE
+#define HEAD_FILL_SIZE 0x10
+#define RESERVE_SIZE 0x800000
+#define VM_UNIT_SIZE 0x10000
+#define POOL_SIZE 0x4000
+#define INLINE __inline
+#define LOCAL_FUNCTION // static // no coff symbols on static functions
+#define MIN_FREE_LIST_LENGTH 8
+
+
+#define ROUNDUP2( x, n ) ((( x ) + (( n ) - 1 )) & ~(( n ) - 1 ))
+
+#if INTERNAL_DEBUG
+ #define DEBUG_CODE( a ) a
+#else
+ #define DEBUG_CODE( a )
+#endif
+
+#define RETAIL_ASSERT( a ) ( (a) ? TRUE : \
+ RtlpDebugPageHeapAssert( "PAGEHEAP: ASSERTION FAILED: (" #a ")\n" ))
+
+#define DEBUG_ASSERT( a ) DEBUG_CODE( RETAIL_ASSERT( a ))
+
+#define HEAP_HANDLE_FROM_ROOT( HeapRoot ) \
+ ((PVOID)(((PCHAR)(HeapRoot)) - PAGE_SIZE ))
+
+#define IF_GENERATE_EXCEPTION( Flags, Status ) { \
+ if (( Flags ) & HEAP_GENERATE_EXCEPTIONS ) \
+ RtlpDebugPageHeapException((ULONG)(Status)); \
+ }
+
+#define OUT_OF_VM_BREAK( Flags, szText ) { \
+ if (( Flags ) & HEAP_BREAK_WHEN_OUT_OF_VM ) \
+ RtlpDebugPageHeapBreak(( szText )); \
+ }
+
+#define ENQUEUE_HEAD( Node, Head, Tail ) { \
+ (Node)->pNextAlloc = (Head); \
+ if ((Head) == NULL ) \
+ (Tail) = (Node); \
+ (Head) = (Node); \
+ }
+
+#define ENQUEUE_TAIL( Node, Head, Tail ) { \
+ if ((Tail) == NULL ) \
+ (Head) = (Node); \
+ else \
+ (Tail)->pNextAlloc = (Node); \
+ (Tail) = (Node); \
+ }
+
+#define DEQUEUE_NODE( Node, Prev, Head, Tail ) { \
+ PVOID Next = (Node)->pNextAlloc; \
+ if ((Head) == (Node)) \
+ (Head) = Next; \
+ if ((Tail) == (Node)) \
+ (Tail) = (Prev); \
+ if ((Prev) != (NULL)) \
+ (Prev)->pNextAlloc = Next; \
+ }
+
+#define PROTECT_HEAP_STRUCTURES( HeapRoot ) { \
+ if ((HeapRoot)->HeapFlags & HEAP_PROTECTION_ENABLED ) \
+ RtlpDebugPageHeapProtectStructures( (HeapRoot) ); \
+ }
+
+#define UNPROTECT_HEAP_STRUCTURES( HeapRoot ) { \
+ if ((HeapRoot)->HeapFlags & HEAP_PROTECTION_ENABLED ) \
+ RtlpDebugPageHeapUnProtectStructures( (HeapRoot) ); \
+ }
+
+
+BOOLEAN RtlpDebugPageHeap; // exported via extern
+BOOLEAN RtlpDebugPageHeapListHasBeenInitialized;
+RTL_CRITICAL_SECTION RtlpDebugPageHeapListCritSect;
+PDPH_HEAP_ROOT RtlpDebugPageHeapListHead;
+PDPH_HEAP_ROOT RtlpDebugPageHeapListTail;
+ULONG RtlpDebugPageHeapListCount;
+
+#if DPH_CAPTURE_STACK_TRACE
+ PVOID RtlpDebugPageHeapStackTraceBuffer[ DPH_MAX_STACK_LENGTH ];
+#endif
+
+
+//
+// Supporting functions
+//
+
+VOID
+RtlpDebugPageHeapBreak(
+ IN PCH Text
+ )
+ {
+ DbgPrint( Text );
+ DbgBreakPoint();
+ }
+
+
+LOCAL_FUNCTION
+BOOLEAN
+RtlpDebugPageHeapAssert(
+ IN PCH Text
+ )
+ {
+ RtlpDebugPageHeapBreak( Text );
+ return FALSE;
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapEnterCritSect(
+ IN PDPH_HEAP_ROOT HeapRoot,
+ IN ULONG Flags
+ )
+ {
+ if ( Flags & HEAP_NO_SERIALIZE ) {
+
+ if ( ! RtlTryEnterCriticalSection( HeapRoot->HeapCritSect )) {
+
+ if ( HeapRoot->nRemoteLockAcquired == 0 ) {
+
+ //
+ // Another thread owns the CritSect. This is an application
+ // bug since multithreaded access to heap was attempted with
+ // the HEAP_NO_SERIALIZE flag specified.
+ //
+
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Multithreaded access with HEAP_NO_SERIALIZE\n" );
+
+ //
+ // In the interest of allowing the errant app to continue,
+ // we'll force serialization and continue.
+ //
+
+ HeapRoot->HeapFlags &= ~HEAP_NO_SERIALIZE;
+
+ }
+
+ RtlEnterCriticalSection( HeapRoot->HeapCritSect );
+
+ }
+ }
+ else {
+ RtlEnterCriticalSection( HeapRoot->HeapCritSect );
+ }
+ }
+
+
+LOCAL_FUNCTION
+INLINE
+VOID
+RtlpDebugPageHeapLeaveCritSect(
+ IN PDPH_HEAP_ROOT HeapRoot
+ )
+ {
+ RtlLeaveCriticalSection( HeapRoot->HeapCritSect );
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapException(
+ IN ULONG ExceptionCode
+ )
+ {
+ EXCEPTION_RECORD ER;
+
+ ER.ExceptionCode = ExceptionCode;
+ ER.ExceptionFlags = 0;
+ ER.ExceptionRecord = NULL;
+ ER.ExceptionAddress = RtlpDebugPageHeapException;
+ ER.NumberParameters = 0;
+ RtlRaiseException( &ER );
+
+ }
+
+
+LOCAL_FUNCTION
+PVOID
+RtlpDebugPageHeapPointerFromHandle(
+ IN PVOID HeapHandle
+ )
+ {
+ try {
+ if (((PHEAP)(HeapHandle))->ForceFlags & HEAP_FLAG_PAGE_ALLOCS ) {
+
+ PDPH_HEAP_ROOT HeapRoot = (PVOID)(((PCHAR)(HeapHandle)) + PAGE_SIZE );
+
+ if ( HeapRoot->Signature == DPH_HEAP_ROOT_SIGNATURE ) {
+ return HeapRoot;
+ }
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Bad heap handle\n" );
+ return NULL;
+ }
+
+
+LOCAL_FUNCTION
+PCCH
+RtlpDebugPageHeapProtectionText(
+ IN ULONG Access,
+ IN OUT PCHAR Buffer
+ )
+ {
+ switch ( Access ) {
+ case PAGE_NOACCESS: return "PAGE_NOACCESS";
+ case PAGE_READONLY: return "PAGE_READONLY";
+ case PAGE_READWRITE: return "PAGE_READWRITE";
+ case PAGE_WRITECOPY: return "PAGE_WRITECOPY";
+ case PAGE_EXECUTE: return "PAGE_EXECUTE";
+ case PAGE_EXECUTE_READ: return "PAGE_EXECUTE_READ";
+ case PAGE_EXECUTE_READWRITE: return "PAGE_EXECUTE_READWRITE";
+ case PAGE_EXECUTE_WRITECOPY: return "PAGE_EXECUTE_WRITECOPY";
+ case PAGE_GUARD: return "PAGE_GUARD";
+ case 0: return "UNKNOWN";
+ default: sprintf( Buffer, "0x%08X", Access );
+ return Buffer;
+ }
+ }
+
+
+LOCAL_FUNCTION
+BOOLEAN
+RtlpDebugPageHeapRobustProtectVM(
+ IN PVOID VirtualBase,
+ IN ULONG VirtualSize,
+ IN ULONG NewAccess,
+ IN BOOLEAN Recursion
+ )
+ {
+ NTSTATUS Status;
+ ULONG OldAccess = 0;
+
+ Status = ZwProtectVirtualMemory(
+ NtCurrentProcess(),
+ &VirtualBase,
+ &VirtualSize,
+ NewAccess,
+ &OldAccess
+ );
+
+ if ( NT_SUCCESS( Status ))
+ return TRUE;
+
+ if (( Status == STATUS_CONFLICTING_ADDRESSES ) && ( ! Recursion )) {
+
+ //
+ // ZwProtectVirtualMemory will not work spanning adjacent
+ // blocks allocated from separate ZwAllocateVirtualMemory
+ // requests. The status returned in such a case is
+ // STATUS_CONFLICTING_ADDRESSES (0xC0000018). We encounter
+ // this case rarely, so we'll special-case it here by breaking
+ // up request into VM_UNIT_SIZE (64K-aligned) requests.
+ // Note if the first unit succeeds but a subsequent unit fails,
+ // we are left with a partially-changed range of VM, so we'll
+ // likely fail again later on the same VM.
+ //
+
+ ULONG RemainingSize = VirtualSize;
+ ULONG UnitBase = (ULONG) VirtualBase;
+ PVOID UnitBasePtr;
+ ULONG UnitSize;
+ BOOLEAN Success;
+
+ do {
+
+ UnitSize = (( UnitBase + VM_UNIT_SIZE ) & ~( VM_UNIT_SIZE - 1 ))
+ - UnitBase;
+
+ if ( UnitSize > RemainingSize )
+ UnitSize = RemainingSize;
+
+ UnitBasePtr = (PVOID)UnitBase;
+ UnitBase += UnitSize;
+ RemainingSize -= UnitSize;
+
+ Success = RtlpDebugPageHeapRobustProtectVM(
+ UnitBasePtr,
+ UnitSize,
+ NewAccess,
+ TRUE
+ );
+
+ }
+
+ while (( Success ) && ( RemainingSize ));
+
+ return Success;
+ }
+
+ else {
+
+ CHAR OldProtectionText[ 12 ]; // big enough for "0x12345678"
+ CHAR NewProtectionText[ 12 ]; // big enough for "0x12345678"
+
+ DbgPrint(
+ "PAGEHEAP: Failed changing VM %s at %08X size 0x%X\n"
+ " from %s to %s (Status %08X)\n",
+ Recursion ? "fragment" : "protection",
+ VirtualBase,
+ VirtualSize,
+ RtlpDebugPageHeapProtectionText( OldAccess, OldProtectionText ),
+ RtlpDebugPageHeapProtectionText( NewAccess, NewProtectionText ),
+ Status
+ );
+
+ RtlpDebugPageHeapBreak( "" );
+ }
+
+ return FALSE;
+ }
+
+
+LOCAL_FUNCTION
+INLINE
+BOOLEAN
+RtlpDebugPageHeapProtectVM(
+ IN PVOID VirtualBase,
+ IN ULONG VirtualSize,
+ IN ULONG NewAccess
+ )
+ {
+ return RtlpDebugPageHeapRobustProtectVM( VirtualBase, VirtualSize, NewAccess, FALSE );
+ }
+
+
+LOCAL_FUNCTION
+INLINE
+PVOID
+RtlpDebugPageHeapAllocateVM(
+ IN ULONG nSize
+ )
+ {
+ NTSTATUS Status;
+ PVOID pVirtual;
+
+ pVirtual = NULL;
+
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &pVirtual,
+ 0,
+ &nSize,
+ MEM_COMMIT,
+ PAGE_NOACCESS );
+
+ return NT_SUCCESS( Status ) ? pVirtual : NULL;
+ }
+
+
+LOCAL_FUNCTION
+INLINE
+BOOLEAN
+RtlpDebugPageHeapReleaseVM(
+ IN PVOID pVirtual
+ )
+ {
+ ULONG nSize = 0;
+
+ return NT_SUCCESS( ZwFreeVirtualMemory( NtCurrentProcess(),
+ &pVirtual,
+ &nSize,
+ MEM_RELEASE ));
+ }
+
+
+LOCAL_FUNCTION
+PDPH_HEAP_ALLOCATION
+RtlpDebugPageHeapTakeNodeFromUnusedList(
+ IN PDPH_HEAP_ROOT pHeap
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode = pHeap->pUnusedNodeListHead;
+ PDPH_HEAP_ALLOCATION pPrev = NULL;
+
+ //
+ // UnusedNodeList is LIFO with most recent entry at head of list.
+ //
+
+ if ( pNode ) {
+
+ DEQUEUE_NODE( pNode, pPrev, pHeap->pUnusedNodeListHead, pHeap->pUnusedNodeListTail );
+
+ --pHeap->nUnusedNodes;
+
+ }
+
+ return pNode;
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapReturnNodeToUnusedList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode
+ )
+ {
+
+ //
+ // UnusedNodeList is LIFO with most recent entry at head of list.
+ //
+
+ ENQUEUE_HEAD( pNode, pHeap->pUnusedNodeListHead, pHeap->pUnusedNodeListTail );
+
+ ++pHeap->nUnusedNodes;
+
+ }
+
+
+LOCAL_FUNCTION
+PDPH_HEAP_ALLOCATION
+RtlpDebugPageHeapFindBusyMem(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PVOID pUserMem,
+ OUT PDPH_HEAP_ALLOCATION *pPrevAlloc
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode = pHeap->pBusyAllocationListHead;
+ PDPH_HEAP_ALLOCATION pPrev = NULL;
+
+ while ( pNode != NULL ) {
+
+ if ( pNode->pUserAllocation == pUserMem ) {
+
+ if ( pPrevAlloc )
+ *pPrevAlloc = pPrev;
+
+ return pNode;
+ }
+
+ pPrev = pNode;
+ pNode = pNode->pNextAlloc;
+ }
+
+ return NULL;
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapRemoveFromAvailableList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode,
+ IN PDPH_HEAP_ALLOCATION pPrev
+ )
+ {
+
+ DEQUEUE_NODE( pNode, pPrev, pHeap->pAvailableAllocationListHead, pHeap->pAvailableAllocationListTail );
+
+ pHeap->nAvailableAllocations--;
+ pHeap->nAvailableAllocationBytesCommitted -= pNode->nVirtualBlockSize;
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapPlaceOnFreeList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pAlloc
+ )
+ {
+
+ //
+ // FreeAllocationList is stored FIFO to enhance finding
+ // reference-after-freed bugs by keeping previously freed
+ // allocations on the free list as long as possible.
+ //
+
+ pAlloc->pNextAlloc = NULL;
+
+ ENQUEUE_TAIL( pAlloc, pHeap->pFreeAllocationListHead, pHeap->pFreeAllocationListTail );
+
+ pHeap->nFreeAllocations++;
+ pHeap->nFreeAllocationBytesCommitted += pAlloc->nVirtualBlockSize;
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapRemoveFromFreeList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode,
+ IN PDPH_HEAP_ALLOCATION pPrev
+ )
+ {
+
+ DEQUEUE_NODE( pNode, pPrev, pHeap->pFreeAllocationListHead, pHeap->pFreeAllocationListTail );
+
+ pHeap->nFreeAllocations--;
+ pHeap->nFreeAllocationBytesCommitted -= pNode->nVirtualBlockSize;
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ pNode->pStackTrace = NULL;
+
+#endif // DPH_CAPTURE_STACK_TRACE
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapPlaceOnVirtualList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode
+ )
+ {
+
+ //
+ // VirtualStorageList is LIFO so that releasing VM blocks will
+ // occur in exact reverse order.
+ //
+
+ ENQUEUE_HEAD( pNode, pHeap->pVirtualStorageListHead, pHeap->pVirtualStorageListTail );
+
+ pHeap->nVirtualStorageRanges++;
+ pHeap->nVirtualStorageBytes += pNode->nVirtualBlockSize;
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapPlaceOnBusyList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode
+ )
+ {
+
+ //
+ // BusyAllocationList is LIFO to achieve better temporal locality
+ // of reference (older allocations are farther down the list).
+ //
+
+ ENQUEUE_HEAD( pNode, pHeap->pBusyAllocationListHead, pHeap->pBusyAllocationListTail );
+
+ pHeap->nBusyAllocations++;
+ pHeap->nBusyAllocationBytesCommitted += pNode->nVirtualBlockSize;
+ pHeap->nBusyAllocationBytesAccessible += pNode->nVirtualAccessSize;
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapRemoveFromBusyList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode,
+ IN PDPH_HEAP_ALLOCATION pPrev
+ )
+ {
+
+ DEQUEUE_NODE( pNode, pPrev, pHeap->pBusyAllocationListHead, pHeap->pBusyAllocationListTail );
+
+ pHeap->nBusyAllocations--;
+ pHeap->nBusyAllocationBytesCommitted -= pNode->nVirtualBlockSize;
+ pHeap->nBusyAllocationBytesAccessible -= pNode->nVirtualAccessSize;
+
+ }
+
+
+LOCAL_FUNCTION
+PDPH_HEAP_ALLOCATION
+RtlpDebugPageHeapSearchAvailableMemListForBestFit(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN ULONG nSize,
+ OUT PDPH_HEAP_ALLOCATION *pPrevAvailNode
+ )
+ {
+ PDPH_HEAP_ALLOCATION pAvail, pFound, pAvailPrev, pFoundPrev;
+ ULONG nAvail, nFound;
+
+ nFound = 0x7FFFFFFF;
+ pFound = NULL;
+ pFoundPrev = NULL;
+ pAvailPrev = NULL;
+ pAvail = pHeap->pAvailableAllocationListHead;
+
+ while (( pAvail != NULL ) && ( nFound > nSize )) {
+
+ nAvail = pAvail->nVirtualBlockSize;
+
+ if (( nAvail >= nSize ) && ( nAvail < nFound )) {
+ nFound = nAvail;
+ pFound = pAvail;
+ pFoundPrev = pAvailPrev;
+ }
+
+ pAvailPrev = pAvail;
+ pAvail = pAvail->pNextAlloc;
+ }
+
+ *pPrevAvailNode = pFoundPrev;
+ return pFound;
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapCoalesceNodeIntoAvailable(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode
+ )
+ {
+ PDPH_HEAP_ALLOCATION pPrev = NULL;
+ PDPH_HEAP_ALLOCATION pNext = pHeap->pAvailableAllocationListHead;
+ PUCHAR pVirtual = pNode->pVirtualBlock;
+ ULONG nVirtual = pNode->nVirtualBlockSize;
+
+ pHeap->nAvailableAllocationBytesCommitted += nVirtual;
+ pHeap->nAvailableAllocations++;
+
+ //
+ // Walk list to insertion point.
+ //
+
+ while (( pNext ) && ( pNext->pVirtualBlock < pVirtual )) {
+ pPrev = pNext;
+ pNext = pNext->pNextAlloc;
+ }
+
+ if ( pPrev ) {
+
+ if (( pPrev->pVirtualBlock + pPrev->nVirtualBlockSize ) == pVirtual ) {
+
+ //
+ // pPrev and pNode are adjacent, so simply add size of
+ // pNode entry to pPrev entry.
+ //
+
+ pPrev->nVirtualBlockSize += nVirtual;
+
+ RtlpDebugPageHeapReturnNodeToUnusedList( pHeap, pNode );
+
+ pHeap->nAvailableAllocations--;
+
+ pNode = pPrev;
+ pVirtual = pPrev->pVirtualBlock;
+ nVirtual = pPrev->nVirtualBlockSize;
+
+ }
+
+ else {
+
+ //
+ // pPrev and pNode are not adjacent, so insert the pNode
+ // block into the list after pPrev.
+ //
+
+ pNode->pNextAlloc = pPrev->pNextAlloc;
+ pPrev->pNextAlloc = pNode;
+
+ }
+ }
+
+ else {
+
+ //
+ // pNode should be inserted at head of list.
+ //
+
+ pNode->pNextAlloc = pHeap->pAvailableAllocationListHead;
+ pHeap->pAvailableAllocationListHead = pNode;
+
+ }
+
+
+ if ( pNext ) {
+
+ if (( pVirtual + nVirtual ) == pNext->pVirtualBlock ) {
+
+ //
+ // pNode and pNext are adjacent, so simply add size of
+ // pNext entry to pNode entry and remove pNext entry
+ // from the list.
+ //
+
+ pNode->nVirtualBlockSize += pNext->nVirtualBlockSize;
+
+ pNode->pNextAlloc = pNext->pNextAlloc;
+
+ if ( pHeap->pAvailableAllocationListTail == pNext )
+ pHeap->pAvailableAllocationListTail = pNode;
+
+ RtlpDebugPageHeapReturnNodeToUnusedList( pHeap, pNext );
+
+ pHeap->nAvailableAllocations--;
+
+ }
+ }
+
+ else {
+
+ //
+ // pNode is tail of list.
+ //
+
+ pHeap->pAvailableAllocationListTail = pNode;
+
+ }
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapCoalesceFreeIntoAvailable(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN ULONG nLeaveOnFreeList
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode = pHeap->pFreeAllocationListHead;
+ ULONG nFree = pHeap->nFreeAllocations;
+ PDPH_HEAP_ALLOCATION pNext;
+
+ DEBUG_ASSERT( nFree >= nLeaveOnFreeList );
+
+ while (( pNode ) && ( nFree-- > nLeaveOnFreeList )) {
+
+ pNext = pNode->pNextAlloc; // preserve next pointer across shuffling
+
+ RtlpDebugPageHeapRemoveFromFreeList( pHeap, pNode, NULL );
+
+ RtlpDebugPageHeapCoalesceNodeIntoAvailable( pHeap, pNode );
+
+ pNode = pNext;
+
+ }
+
+ DEBUG_ASSERT(( nFree = (volatile ULONG)( pHeap->nFreeAllocations )) >= nLeaveOnFreeList );
+ DEBUG_ASSERT(( pNode != NULL ) || ( nFree == 0 ));
+
+ }
+
+
+LOCAL_FUNCTION
+BOOLEAN
+RtlpDebugPageHeapGrowVirtual(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN ULONG nSize
+ );
+
+
+LOCAL_FUNCTION
+PDPH_HEAP_ALLOCATION
+RtlpDebugPageHeapFindAvailableMem(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN ULONG nSize,
+ OUT PDPH_HEAP_ALLOCATION *pPrevAvailNode,
+ IN BOOLEAN bGrowVirtual
+ )
+ {
+ PDPH_HEAP_ALLOCATION pAvail;
+ ULONG nLeaveOnFreeList;
+
+ //
+ // First search existing AvailableList for a "best-fit" block
+ // (the smallest block that will satisfy the request).
+ //
+
+ pAvail = RtlpDebugPageHeapSearchAvailableMemListForBestFit(
+ pHeap,
+ nSize,
+ pPrevAvailNode
+ );
+
+ while (( pAvail == NULL ) && ( pHeap->nFreeAllocations > MIN_FREE_LIST_LENGTH )) {
+
+ //
+ // Failed to find sufficient memory on AvailableList. Coalesce
+ // 3/4 of the FreeList memory to the AvailableList and try again.
+ // Continue this until we have sufficient memory in AvailableList,
+ // or the FreeList length is reduced to MIN_FREE_LIST_LENGTH entries.
+ // We don't shrink the FreeList length below MIN_FREE_LIST_LENGTH
+ // entries to preserve the most recent MIN_FREE_LIST_LENGTH entries
+ // for reference-after-freed purposes.
+ //
+
+ nLeaveOnFreeList = pHeap->nFreeAllocations / 4;
+
+ if ( nLeaveOnFreeList < MIN_FREE_LIST_LENGTH )
+ nLeaveOnFreeList = MIN_FREE_LIST_LENGTH;
+
+ RtlpDebugPageHeapCoalesceFreeIntoAvailable( pHeap, nLeaveOnFreeList );
+
+ pAvail = RtlpDebugPageHeapSearchAvailableMemListForBestFit(
+ pHeap,
+ nSize,
+ pPrevAvailNode
+ );
+
+ }
+
+
+ if (( pAvail == NULL ) && ( bGrowVirtual )) {
+
+ //
+ // After coalescing FreeList into AvailableList, still don't have
+ // enough memory (large enough block) to satisfy request, so we
+ // need to allocate more VM.
+ //
+
+ if ( RtlpDebugPageHeapGrowVirtual( pHeap, nSize )) {
+
+ pAvail = RtlpDebugPageHeapSearchAvailableMemListForBestFit(
+ pHeap,
+ nSize,
+ pPrevAvailNode
+ );
+
+ if ( pAvail == NULL ) {
+
+ //
+ // Failed to satisfy request with more VM. If remainder
+ // of free list combined with available list is larger
+ // than the request, we might still be able to satisfy
+ // the request by merging all of the free list onto the
+ // available list. Note we lose our MIN_FREE_LIST_LENGTH
+ // reference-after-freed insurance in this case, but it
+ // is a rare case, and we'd prefer to satisfy the allocation.
+ //
+
+ if (( pHeap->nFreeAllocationBytesCommitted +
+ pHeap->nAvailableAllocationBytesCommitted ) >= nSize ) {
+
+ RtlpDebugPageHeapCoalesceFreeIntoAvailable( pHeap, 0 );
+
+ pAvail = RtlpDebugPageHeapSearchAvailableMemListForBestFit(
+ pHeap,
+ nSize,
+ pPrevAvailNode
+ );
+ }
+ }
+ }
+ }
+
+ return pAvail;
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapPlaceOnPoolList(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PDPH_HEAP_ALLOCATION pNode
+ )
+ {
+
+ //
+ // NodePoolList is FIFO.
+ //
+
+ pNode->pNextAlloc = NULL;
+
+ ENQUEUE_TAIL( pNode, pHeap->pNodePoolListHead, pHeap->pNodePoolListTail );
+
+ pHeap->nNodePoolBytes += pNode->nVirtualBlockSize;
+ pHeap->nNodePools += 1;
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapAddNewPool(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN PVOID pVirtual,
+ IN ULONG nSize,
+ IN BOOLEAN bAddToPoolList
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode, pFirst;
+ ULONG n, nCount;
+
+ //
+ // Assume pVirtual points to committed block of nSize bytes.
+ //
+
+ pFirst = pVirtual;
+ nCount = nSize / sizeof( DPH_HEAP_ALLOCATION );
+
+ for ( n = nCount - 1, pNode = pFirst; n > 0; pNode++, n-- )
+ pNode->pNextAlloc = pNode + 1;
+
+ pNode->pNextAlloc = NULL;
+
+ //
+ // Now link this list into the tail of the UnusedNodeList
+ //
+
+ ENQUEUE_TAIL( pFirst, pHeap->pUnusedNodeListHead, pHeap->pUnusedNodeListTail );
+
+ pHeap->pUnusedNodeListTail = pNode;
+
+ pHeap->nUnusedNodes += nCount;
+
+ if ( bAddToPoolList ) {
+
+ //
+ // Now add an entry on the PoolList by taking a node from the
+ // UnusedNodeList, which should be guaranteed to be non-empty
+ // since we just added new nodes to it.
+ //
+
+ pNode = RtlpDebugPageHeapTakeNodeFromUnusedList( pHeap );
+
+ DEBUG_ASSERT( pNode != NULL );
+
+ pNode->pVirtualBlock = pVirtual;
+ pNode->nVirtualBlockSize = nSize;
+
+ RtlpDebugPageHeapPlaceOnPoolList( pHeap, pNode );
+
+ }
+
+ }
+
+
+LOCAL_FUNCTION
+PDPH_HEAP_ALLOCATION
+RtlpDebugPageHeapAllocateNode(
+ IN PDPH_HEAP_ROOT pHeap
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode, pPrev, pReturn;
+ PUCHAR pVirtual;
+ ULONG nVirtual;
+ ULONG nRequest;
+
+ DEBUG_ASSERT( ! pHeap->InsideAllocateNode );
+ DEBUG_CODE( pHeap->InsideAllocateNode = TRUE );
+
+ pReturn = NULL;
+
+ if ( pHeap->pUnusedNodeListHead == NULL ) {
+
+ //
+ // We're out of nodes -- allocate new node pool
+ // from AvailableList. Set bGrowVirtual to FALSE
+ // since growing virtual will require new nodes, causing
+ // recursion. Note that simply calling FindAvailableMem
+ // might return some nodes to the pUnusedNodeList, even if
+ // the call fails, so we'll check that the UnusedNodeList
+ // is still empty before we try to use or allocate more
+ // memory.
+ //
+
+ nRequest = POOL_SIZE;
+
+ pNode = RtlpDebugPageHeapFindAvailableMem(
+ pHeap,
+ nRequest,
+ &pPrev,
+ FALSE
+ );
+
+ if (( pHeap->pUnusedNodeListHead == NULL ) && ( pNode == NULL )) {
+
+ //
+ // Reduce request size to PAGE_SIZE and see if
+ // we can find at least a page on the available
+ // list.
+ //
+
+ nRequest = PAGE_SIZE;
+
+ pNode = RtlpDebugPageHeapFindAvailableMem(
+ pHeap,
+ nRequest,
+ &pPrev,
+ FALSE
+ );
+
+ }
+
+ if ( pHeap->pUnusedNodeListHead == NULL ) {
+
+ if ( pNode == NULL ) {
+
+ //
+ // Insufficient memory on Available list. Try allocating a
+ // new virtual block.
+ //
+
+ nRequest = POOL_SIZE;
+ nVirtual = RESERVE_SIZE;
+ pVirtual = RtlpDebugPageHeapAllocateVM( nVirtual );
+
+ if ( pVirtual == NULL ) {
+
+ //
+ // Unable to allocate full RESERVE_SIZE block,
+ // so reduce request to single VM unit (64K)
+ // and try again.
+ //
+
+ nVirtual = VM_UNIT_SIZE;
+ pVirtual = RtlpDebugPageHeapAllocateVM( nVirtual );
+
+ if ( pVirtual == NULL ) {
+
+ //
+ // Can't allocate any VM.
+ //
+
+ goto EXIT;
+ }
+ }
+ }
+
+ else {
+
+ RtlpDebugPageHeapRemoveFromAvailableList( pHeap, pNode, pPrev );
+
+ pVirtual = pNode->pVirtualBlock;
+ nVirtual = pNode->nVirtualBlockSize;
+
+ }
+
+ //
+ // We now have allocated VM referenced by pVirtual,nVirtual.
+ // Make nRequest portion of VM accessible for new node pool.
+ //
+
+ if ( ! RtlpDebugPageHeapProtectVM( pVirtual, nRequest, PAGE_READWRITE )) {
+
+ if ( pNode == NULL ) {
+ RtlpDebugPageHeapReleaseVM( pVirtual );
+ }
+ else {
+ RtlpDebugPageHeapCoalesceNodeIntoAvailable( pHeap, pNode );
+ }
+
+ goto EXIT;
+ }
+
+ //
+ // Now we have accessible memory for new pool. Add the
+ // new memory to the pool. If the new memory came from
+ // AvailableList versus fresh VM, zero the memory first.
+ //
+
+ if ( pNode != NULL )
+ RtlZeroMemory( pVirtual, nRequest );
+
+ RtlpDebugPageHeapAddNewPool( pHeap, pVirtual, nRequest, TRUE );
+
+ //
+ // If any memory remaining, put it on available list.
+ //
+
+ if ( pNode == NULL ) {
+
+ //
+ // Memory came from new VM -- add appropriate list entries
+ // for new VM and add remainder of VM to free list.
+ //
+
+ pNode = RtlpDebugPageHeapTakeNodeFromUnusedList( pHeap );
+ DEBUG_ASSERT( pNode != NULL );
+ pNode->pVirtualBlock = pVirtual;
+ pNode->nVirtualBlockSize = nVirtual;
+ RtlpDebugPageHeapPlaceOnVirtualList( pHeap, pNode );
+
+ pNode = RtlpDebugPageHeapTakeNodeFromUnusedList( pHeap );
+ DEBUG_ASSERT( pNode != NULL );
+ pNode->pVirtualBlock = pVirtual + nRequest;
+ pNode->nVirtualBlockSize = nVirtual - nRequest;
+
+ RtlpDebugPageHeapCoalesceNodeIntoAvailable( pHeap, pNode );
+
+ }
+
+ else {
+
+ if ( pNode->nVirtualBlockSize > nRequest ) {
+
+ pNode->pVirtualBlock += nRequest;
+ pNode->nVirtualBlockSize -= nRequest;
+
+ RtlpDebugPageHeapCoalesceNodeIntoAvailable( pHeap, pNode );
+ }
+
+ else {
+
+ //
+ // Used up entire available block -- return node to
+ // unused list.
+ //
+
+ RtlpDebugPageHeapReturnNodeToUnusedList( pHeap, pNode );
+
+ }
+ }
+ }
+ }
+
+ pReturn = RtlpDebugPageHeapTakeNodeFromUnusedList( pHeap );
+ DEBUG_ASSERT( pReturn != NULL );
+
+EXIT:
+
+ DEBUG_CODE( pHeap->InsideAllocateNode = FALSE );
+ return pReturn;
+ }
+
+
+LOCAL_FUNCTION
+BOOLEAN
+RtlpDebugPageHeapGrowVirtual(
+ IN PDPH_HEAP_ROOT pHeap,
+ IN ULONG nSize
+ )
+ {
+ PDPH_HEAP_ALLOCATION pVirtualNode;
+ PDPH_HEAP_ALLOCATION pAvailNode;
+ PVOID pVirtual;
+ ULONG nVirtual;
+
+ pVirtualNode = RtlpDebugPageHeapAllocateNode( pHeap );
+
+ if ( pVirtualNode == NULL ) {
+ return FALSE;
+ }
+
+ pAvailNode = RtlpDebugPageHeapAllocateNode( pHeap );
+
+ if ( pAvailNode == NULL ) {
+ RtlpDebugPageHeapReturnNodeToUnusedList( pHeap, pVirtualNode );
+ return FALSE;
+ }
+
+ nSize = ROUNDUP2( nSize, VM_UNIT_SIZE );
+ nVirtual = ( nSize > RESERVE_SIZE ) ? nSize : RESERVE_SIZE;
+ pVirtual = RtlpDebugPageHeapAllocateVM( nVirtual );
+
+ if (( pVirtual == NULL ) && ( nSize < RESERVE_SIZE )) {
+ nVirtual = nSize;
+ pVirtual = RtlpDebugPageHeapAllocateVM( nVirtual );
+ }
+
+ if ( pVirtual == NULL ) {
+ RtlpDebugPageHeapReturnNodeToUnusedList( pHeap, pVirtualNode );
+ RtlpDebugPageHeapReturnNodeToUnusedList( pHeap, pAvailNode );
+ return FALSE;
+ }
+
+ pVirtualNode->pVirtualBlock = pVirtual;
+ pVirtualNode->nVirtualBlockSize = nVirtual;
+ RtlpDebugPageHeapPlaceOnVirtualList( pHeap, pVirtualNode );
+
+ pAvailNode->pVirtualBlock = pVirtual;
+ pAvailNode->nVirtualBlockSize = nVirtual;
+ RtlpDebugPageHeapCoalesceNodeIntoAvailable( pHeap, pAvailNode );
+
+ return TRUE;
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapProtectStructures(
+ IN PDPH_HEAP_ROOT pHeap
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode;
+
+ //
+ // Assume CritSect is owned so we're the only thread twiddling
+ // the protection.
+ //
+
+ DEBUG_ASSERT( pHeap->HeapFlags & HEAP_PROTECTION_ENABLED );
+
+ if ( --pHeap->nUnProtectionReferenceCount == 0 ) {
+
+ pNode = pHeap->pNodePoolListHead;
+
+ while ( pNode != NULL ) {
+
+ RtlpDebugPageHeapProtectVM( pNode->pVirtualBlock,
+ pNode->nVirtualBlockSize,
+ PAGE_READONLY );
+
+ pNode = pNode->pNextAlloc;
+
+ }
+ }
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapUnProtectStructures(
+ IN PDPH_HEAP_ROOT pHeap
+ )
+ {
+ PDPH_HEAP_ALLOCATION pNode;
+
+ DEBUG_ASSERT( pHeap->HeapFlags & HEAP_PROTECTION_ENABLED );
+
+ if ( pHeap->nUnProtectionReferenceCount == 0 ) {
+
+ pNode = pHeap->pNodePoolListHead;
+
+ while ( pNode != NULL ) {
+
+ RtlpDebugPageHeapProtectVM( pNode->pVirtualBlock,
+ pNode->nVirtualBlockSize,
+ PAGE_READWRITE );
+
+ pNode = pNode->pNextAlloc;
+
+ }
+ }
+
+ ++pHeap->nUnProtectionReferenceCount;
+
+ }
+
+
+LOCAL_FUNCTION
+INLINE
+PUCHAR
+RtlpDebugPageHeapScanForFillCorruption(
+ IN PUCHAR Address,
+ IN UCHAR ExpectedValue,
+ IN ULONG Length
+ )
+ {
+ PUCHAR End;
+
+ for ( End = Address + Length; Address < End; Address++ ) {
+ if ( *Address != ExpectedValue )
+ return Address;
+ }
+
+ return NULL;
+ }
+
+
+LOCAL_FUNCTION
+BOOLEAN
+RtlpDebugPageHeapDetectFillCorruption(
+ IN PDPH_HEAP_ALLOCATION pNode
+ )
+ {
+ PUCHAR p;
+
+ p = RtlpDebugPageHeapScanForFillCorruption(
+ pNode->pUserAllocation + pNode->nUserRequestedSize,
+ FILL_BYTE,
+ pNode->nUserActualSize - pNode->nUserRequestedSize );
+
+ if ( p != NULL ) {
+
+ DbgPrint( "PAGEHEAP: Tail fill corruption detected:\n"
+ " Allocation at 0x%08X\n"
+ " Requested size 0x%08X\n"
+ " Allocated size 0x%08X\n"
+ " Corruption at 0x%08X\n",
+ pNode->pUserAllocation,
+ pNode->nUserRequestedSize,
+ pNode->nUserActualSize,
+ p );
+
+ RtlpDebugPageHeapBreak( "" );
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+
+#if INTERNAL_DEBUG
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapVerifyList(
+ IN PDPH_HEAP_ALLOCATION pListHead,
+ IN PDPH_HEAP_ALLOCATION pListTail,
+ IN ULONG nExpectedLength,
+ IN ULONG nExpectedVirtual,
+ IN PCCH pListName
+ )
+ {
+ PDPH_HEAP_ALLOCATION pPrev = NULL;
+ PDPH_HEAP_ALLOCATION pNode = pListHead;
+ PDPH_HEAP_ALLOCATION pTest = pListHead ? pListHead->pNextAlloc : NULL;
+ ULONG nNode = 0;
+ ULONG nSize = 0;
+
+ while ( pNode ) {
+
+ if ( pNode == pTest ) {
+ DbgPrint( "PAGEHEAP: Internal %s list is circular\n", pListName );
+ RtlpDebugPageHeapBreak( "" );
+ return;
+ }
+
+ nNode++;
+ nSize += pNode->nVirtualBlockSize;
+
+ if ( pTest ) {
+ pTest = pTest->pNextAlloc;
+ if ( pTest ) {
+ pTest = pTest->pNextAlloc;
+ }
+ }
+
+ pPrev = pNode;
+ pNode = pNode->pNextAlloc;
+
+ }
+
+ if ( pPrev != pListTail ) {
+ DbgPrint( "PAGEHEAP: Internal %s list has incorrect tail pointer\n", pListName );
+ RtlpDebugPageHeapBreak( "" );
+ }
+
+ if (( nExpectedLength != 0xFFFFFFFF ) && ( nExpectedLength != nNode )) {
+ DbgPrint( "PAGEHEAP: Internal %s list has incorrect length\n", pListName );
+ RtlpDebugPageHeapBreak( "" );
+ }
+
+ if (( nExpectedVirtual != 0xFFFFFFFF ) && ( nExpectedVirtual != nSize )) {
+ DbgPrint( "PAGEHEAP: Internal %s list has incorrect virtual size\n", pListName );
+ RtlpDebugPageHeapBreak( "" );
+ }
+
+ }
+
+
+LOCAL_FUNCTION
+VOID
+RtlpDebugPageHeapVerifyIntegrity(
+ IN PDPH_HEAP_ROOT pHeap
+ )
+ {
+
+ RtlpDebugPageHeapVerifyList(
+ pHeap->pVirtualStorageListHead,
+ pHeap->pVirtualStorageListTail,
+ pHeap->nVirtualStorageRanges,
+ pHeap->nVirtualStorageBytes,
+ "VIRTUAL"
+ );
+
+ RtlpDebugPageHeapVerifyList(
+ pHeap->pBusyAllocationListHead,
+ pHeap->pBusyAllocationListTail,
+ pHeap->nBusyAllocations,
+ pHeap->nBusyAllocationBytesCommitted,
+ "BUSY"
+ );
+
+ RtlpDebugPageHeapVerifyList(
+ pHeap->pFreeAllocationListHead,
+ pHeap->pFreeAllocationListTail,
+ pHeap->nFreeAllocations,
+ pHeap->nFreeAllocationBytesCommitted,
+ "FREE"
+ );
+
+ RtlpDebugPageHeapVerifyList(
+ pHeap->pAvailableAllocationListHead,
+ pHeap->pAvailableAllocationListTail,
+ pHeap->nAvailableAllocations,
+ pHeap->nAvailableAllocationBytesCommitted,
+ "AVAILABLE"
+ );
+
+ RtlpDebugPageHeapVerifyList(
+ pHeap->pUnusedNodeListHead,
+ pHeap->pUnusedNodeListTail,
+ pHeap->nUnusedNodes,
+ 0xFFFFFFFF,
+ "FREENODE"
+ );
+
+ RtlpDebugPageHeapVerifyList(
+ pHeap->pNodePoolListHead,
+ pHeap->pNodePoolListTail,
+ pHeap->nNodePools,
+ pHeap->nNodePoolBytes,
+ "NODEPOOL"
+ );
+
+ }
+
+#endif // INTERNAL_DEBUG
+
+
+#if DPH_CAPTURE_STACK_TRACE
+
+
+VOID
+RtlpDebugPageHeapRemoteThreadLock(
+ IN PVOID HeapBaseAddress
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ LARGE_INTEGER Delay;
+
+ try {
+
+ HeapRoot = HeapBaseAddress;
+
+ if ( HeapRoot->Signature == DPH_HEAP_ROOT_SIGNATURE ) {
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, 0 );
+
+ (volatile ULONG)( HeapRoot->nRemoteLockAcquired ) = 1;
+
+ Delay.QuadPart = -1000000; // 100ms, relative to now
+
+ do {
+ ZwDelayExecution( FALSE, &Delay );
+ }
+ while ((volatile ULONG)( HeapRoot->nRemoteLockAcquired ) == 1 );
+
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ }
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+
+ //
+ // Note that TerminateThread will not free thread's stack --
+ // that will be done by remote caller after thread wait is
+ // satisfied by this termination call.
+ //
+
+ ZwTerminateThread( NtCurrentThread(), 0 );
+ }
+
+//
+// Since RtlpDebugPageHeapRemoteThreadLock is not called from any code in
+// ntdll, the linker will discard it unless we create a reference to it.
+//
+
+PVOID RtlpDebugPageHeapRemoteThreadLockAddress = RtlpDebugPageHeapRemoteThreadLock;
+
+
+LOCAL_FUNCTION
+PDPH_STACK_TRACE_NODE
+RtlpDebugPageHeapNewStackTraceStorage(
+ IN PDPH_HEAP_ROOT HeapRoot,
+ IN ULONG Length
+ )
+ {
+ PDPH_HEAP_ALLOCATION pAvailNode, pPrevNode, pStackNode;
+ PDPH_STACK_TRACE_NODE Return;
+ PUCHAR pVirtual;
+ ULONG nRequest;
+ ULONG Size;
+
+ Size = sizeof( DPH_STACK_TRACE_NODE ) + ( Length * sizeof( PVOID ));
+
+ if ( Size > HeapRoot->nStackTraceStorage ) {
+
+ nRequest = POOL_SIZE;
+
+ pAvailNode = RtlpDebugPageHeapFindAvailableMem(
+ HeapRoot,
+ nRequest,
+ &pPrevNode,
+ TRUE
+ );
+
+ if ( pAvailNode == NULL ) {
+
+ //
+ // Reduce request size to PAGE_SIZE and see if
+ // we can find at least a page on the available
+ // list.
+ //
+
+ nRequest = PAGE_SIZE;
+
+ pAvailNode = RtlpDebugPageHeapFindAvailableMem(
+ HeapRoot,
+ nRequest,
+ &pPrevNode,
+ TRUE
+ );
+
+ }
+
+ if ( pAvailNode == NULL )
+ return NULL;
+
+ pVirtual = pAvailNode->pVirtualBlock;
+
+ if ( ! RtlpDebugPageHeapProtectVM( pVirtual, nRequest, PAGE_READWRITE )) {
+ return NULL;
+ }
+
+ //
+ // pAvailNode (still on avail list) points to block large enough
+ // to satisfy request, but it might be large enough to split
+ // into two blocks -- one for request, remainder leave on
+ // avail list.
+ //
+
+ if ( pAvailNode->nVirtualBlockSize > nRequest ) {
+
+ //
+ // Adjust pVirtualBlock and nVirtualBlock size of existing
+ // node in avail list. The node will still be in correct
+ // address space order on the avail list. This saves having
+ // to remove and then re-add node to avail list. Note since
+ // we're changing sizes directly, we need to adjust the
+ // avail list counters manually.
+ //
+ // Note: since we're leaving at least one page on the
+ // available list, we are guaranteed that AllocateNode
+ // will not fail.
+ //
+
+ pAvailNode->pVirtualBlock += nRequest;
+ pAvailNode->nVirtualBlockSize -= nRequest;
+ HeapRoot->nAvailableAllocationBytesCommitted -= nRequest;
+
+ pStackNode = RtlpDebugPageHeapAllocateNode( HeapRoot );
+
+ DEBUG_ASSERT( pStackNode != NULL );
+
+ pStackNode->pVirtualBlock = pVirtual;
+ pStackNode->nVirtualBlockSize = nRequest;
+
+ }
+
+ else {
+
+ //
+ // Entire avail block is needed, so simply remove it from avail list.
+ //
+
+ RtlpDebugPageHeapRemoveFromAvailableList( HeapRoot, pAvailNode, pPrevNode );
+
+ pStackNode = pAvailNode;
+
+ }
+
+ HeapRoot->nStackTraceBytesWasted += HeapRoot->nStackTraceStorage;
+ HeapRoot->nStackTraceBytesCommitted += nRequest;
+
+ //
+ // Note: we're wasting the remaining HeapRoot->nStackTraceStorage
+ // bytes here.
+ //
+
+ HeapRoot->pStackTraceStorage = pVirtual;
+ HeapRoot->nStackTraceStorage = nRequest;
+
+ RtlpDebugPageHeapPlaceOnPoolList( HeapRoot, pStackNode );
+
+ }
+
+ Return = (PVOID) HeapRoot->pStackTraceStorage;
+
+ HeapRoot->pStackTraceStorage += Size;
+ HeapRoot->nStackTraceStorage -= Size;
+ HeapRoot->nStackTraceBNodes++;
+
+ return Return;
+ }
+
+
+LOCAL_FUNCTION
+PDPH_STACK_TRACE_NODE
+RtlpDebugPageHeapFindOrAddStackTrace(
+ IN PDPH_HEAP_ROOT HeapRoot,
+ IN ULONG HashValue,
+ IN ULONG Length,
+ IN PVOID* Address
+ )
+ {
+ PDPH_STACK_TRACE_NODE Node;
+ PDPH_STACK_TRACE_NODE NewNode;
+ ULONG Depth;
+
+ Node = HeapRoot->pStackTraceRoot; // assume non-NULL
+ NewNode = NULL;
+ Depth = 0;
+
+ for (;;) {
+
+ Depth++;
+
+ if ( Node->Hash > HashValue ) { // go left
+ if ( Node->Left ) {
+ Node = Node->Left;
+ }
+ else {
+
+ NewNode = RtlpDebugPageHeapNewStackTraceStorage(
+ HeapRoot,
+ Length
+ );
+
+ Node->Left = NewNode;
+ break;
+ }
+ }
+
+ else if ( Node->Hash < HashValue ) { // go right
+ if ( Node->Right ) {
+ Node = Node->Right;
+ }
+ else {
+
+ NewNode = RtlpDebugPageHeapNewStackTraceStorage(
+ HeapRoot,
+ Length
+ );
+
+ Node->Right = NewNode;
+ break;
+ }
+ }
+ else { // ( Node->Hash == HashValue ), verify matching data or rehash
+
+ if (( Node->Length == Length ) &&
+ ( RtlCompareMemory( Node->Address, Address, Length ) == Length )) {
+
+ //
+ // Complete match, return this Node.
+ //
+
+ return Node;
+ }
+
+ else {
+
+ //
+ // Not a match, increment hash value by one and search again
+ // (slow linear-rehashing, but don't expect many collisions).
+ //
+
+ HashValue++;
+ Node = HeapRoot->pStackTraceRoot;
+ Depth = 0;
+
+ HeapRoot->nStackTraceBHashCollisions++;
+
+ }
+ }
+ }
+
+ if ( NewNode != NULL ) {
+
+ NewNode->Left = NULL;
+ NewNode->Right = NULL;
+ NewNode->Hash = HashValue;
+ NewNode->Length = Length;
+ NewNode->BusyCount = 0;
+ NewNode->BusyBytes = 0;
+
+ RtlCopyMemory( NewNode->Address, Address, Length * sizeof( PVOID ));
+
+ if ( ++Depth > HeapRoot->nStackTraceBDepth ) {
+ HeapRoot->nStackTraceBDepth = Depth;
+ }
+ }
+
+ return NewNode;
+ }
+
+
+#if (( i386 ) && ( FPO ))
+#pragma optimize( "y", off ) // disable FPO for consistent stack traces
+#endif
+
+LOCAL_FUNCTION
+UCHAR
+RtlpDebugPageHeapCaptureStackTrace(
+ IN UCHAR FramesToSkip,
+ IN UCHAR FramesToCapture,
+ OUT PVOID* TraceBuffer,
+ OUT PULONG HashValue
+ )
+ {
+ UCHAR FramesCaptured;
+
+ RtlZeroMemory( TraceBuffer, FramesToCapture * sizeof( PVOID ));
+
+ *HashValue = 0;
+
+ try {
+
+ FramesCaptured = (UCHAR) RtlCaptureStackBackTrace(
+ (UCHAR)( FramesToSkip + 1 ),
+ FramesToCapture,
+ TraceBuffer,
+ HashValue
+ );
+
+ //
+ // Sometimes the final frame is NULL: if so, we'll strip it
+ // for smaller storage.
+ //
+
+ if (( FramesCaptured ) && ( ! TraceBuffer[ FramesCaptured - 1 ] )) {
+ --FramesCaptured;
+ }
+
+ }
+
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+
+ FramesCaptured = 0;
+
+ while (( FramesCaptured < FramesToCapture ) &&
+ ( TraceBuffer[ FramesCaptured ] != NULL )) {
+
+ FramesCaptured++;
+
+ }
+ }
+
+ return FramesCaptured;
+
+ }
+
+
+LOCAL_FUNCTION
+PDPH_STACK_TRACE_NODE
+RtlpDebugPageHeapCaptureAndStoreStackTrace(
+ IN PDPH_HEAP_ROOT HeapRoot,
+ IN UCHAR FramesToSkip
+ )
+ {
+ ULONG HashValue;
+ ULONG FramesCaptured;
+
+ FramesCaptured = RtlpDebugPageHeapCaptureStackTrace(
+ (UCHAR)( FramesToSkip + 1 ),
+ (UCHAR)( DPH_MAX_STACK_LENGTH ),
+ RtlpDebugPageHeapStackTraceBuffer,
+ &HashValue
+ );
+
+ if ( FramesCaptured ) {
+
+ return RtlpDebugPageHeapFindOrAddStackTrace(
+ HeapRoot,
+ HashValue,
+ FramesCaptured,
+ RtlpDebugPageHeapStackTraceBuffer
+ );
+ }
+
+ return NULL;
+
+ }
+
+
+#if (( i386 ) && ( FPO ))
+#pragma optimize( "", on ) // restore original optimizations
+#endif
+
+#endif // DPH_CAPTURE_STACK_TRACE
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+//
+// Here's where the exported interface functions are defined.
+//
+
+#if (( DPH_CAPTURE_STACK_TRACE ) && ( i386 ) && ( FPO ))
+#pragma optimize( "y", off ) // disable FPO for consistent stack traces
+#endif
+
+
+PVOID
+RtlpDebugPageHeapCreate(
+ IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN ULONG ReserveSize OPTIONAL,
+ IN ULONG CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL
+ )
+ {
+ SYSTEM_BASIC_INFORMATION SystemInfo;
+ PDPH_HEAP_ALLOCATION Node;
+ PDPH_HEAP_ROOT HeapRoot;
+ PVOID HeapHandle;
+ PUCHAR pVirtual;
+ ULONG nVirtual;
+ ULONG Size;
+ NTSTATUS Status;
+
+ //
+ // We don't handle heaps where HeapBase is already allocated
+ // from user or where Lock is provided by user.
+ //
+
+ DEBUG_ASSERT( HeapBase == NULL );
+ DEBUG_ASSERT( Lock == NULL );
+
+ if (( HeapBase != NULL ) || ( Lock != NULL ))
+ return NULL;
+
+ //
+ // Note that we simply ignore ReserveSize, CommitSize, and
+ // Parameters as we always have a growable heap with our
+ // own thresholds, etc.
+ //
+
+ ZwQuerySystemInformation( SystemBasicInformation,
+ &SystemInfo,
+ sizeof( SystemInfo ),
+ NULL );
+
+ RETAIL_ASSERT( SystemInfo.PageSize == PAGE_SIZE );
+ RETAIL_ASSERT( SystemInfo.AllocationGranularity == VM_UNIT_SIZE );
+ DEBUG_ASSERT(( PAGE_SIZE + POOL_SIZE + PAGE_SIZE ) < VM_UNIT_SIZE );
+
+ nVirtual = RESERVE_SIZE;
+ pVirtual = RtlpDebugPageHeapAllocateVM( nVirtual );
+
+ if ( pVirtual == NULL ) {
+
+ nVirtual = VM_UNIT_SIZE;
+ pVirtual = RtlpDebugPageHeapAllocateVM( nVirtual );
+
+ if ( pVirtual == NULL ) {
+ OUT_OF_VM_BREAK( Flags, "PAGEHEAP: Insufficient memory to create heap\n" );
+ IF_GENERATE_EXCEPTION( Flags, STATUS_NO_MEMORY );
+ return NULL;
+ }
+ }
+
+ if ( ! RtlpDebugPageHeapProtectVM( pVirtual, PAGE_SIZE + POOL_SIZE + PAGE_SIZE, PAGE_READWRITE )) {
+ RtlpDebugPageHeapReleaseVM( pVirtual );
+ IF_GENERATE_EXCEPTION( Flags, STATUS_NO_MEMORY );
+ return NULL;
+ }
+
+ //
+ // Out of our initial allocation, the initial page is the fake
+ // retail HEAP structure. The second page begins our DPH_HEAP_ROOT
+ // structure followed by (POOL_SIZE-sizeof(DPH_HEAP_ROOT)) bytes for
+ // the initial pool. The next page contains out CRIT_SECT
+ // variable, which must always be READWRITE. Beyond that, the
+ // remainder of the virtual allocation is placed on the available
+ // list.
+ //
+ // |_____|___________________|_____|__ _ _ _ _ _ _ _ _ _ _ _ _ __|
+ //
+ // ^pVirtual
+ //
+ // ^FakeRetailHEAP
+ //
+ // ^HeapRoot
+ //
+ // ^InitialNodePool
+ //
+ // ^CRITICAL_SECTION
+ //
+ // ^AvailableSpace
+ //
+ //
+ //
+ // Our DPH_HEAP_ROOT structure starts at the page following the
+ // fake retail HEAP structure pointed to by the "heap handle".
+ // For the fake HEAP structure, we'll fill it with 0xEEEEEEEE
+ // except for the Heap->Flags and Heap->ForceFlags fields,
+ // which we must set to include our HEAP_FLAG_PAGE_ALLOCS flag,
+ // and then we'll make the whole page read-only.
+ //
+
+ RtlFillMemory( pVirtual, PAGE_SIZE, FILL_BYTE );
+
+ ((PHEAP)pVirtual)->Flags = Flags | HEAP_FLAG_PAGE_ALLOCS;
+ ((PHEAP)pVirtual)->ForceFlags = Flags | HEAP_FLAG_PAGE_ALLOCS;
+
+ if ( ! RtlpDebugPageHeapProtectVM( pVirtual, PAGE_SIZE, PAGE_READONLY )) {
+ RtlpDebugPageHeapReleaseVM( pVirtual );
+ IF_GENERATE_EXCEPTION( Flags, STATUS_NO_MEMORY );
+ return NULL;
+ }
+
+ HeapRoot = (PDPH_HEAP_ROOT)( pVirtual + PAGE_SIZE );
+
+ HeapRoot->Signature = DPH_HEAP_ROOT_SIGNATURE;
+ HeapRoot->HeapFlags = Flags;
+ HeapRoot->HeapCritSect = (PVOID)((PCHAR)HeapRoot + POOL_SIZE );
+
+ RtlInitializeCriticalSection( HeapRoot->HeapCritSect );
+
+ //
+ // On the page that contains our DPH_HEAP_ROOT structure, use
+ // the remaining memory beyond the DPH_HEAP_ROOT structure as
+ // pool for allocating heap nodes.
+ //
+
+ RtlpDebugPageHeapAddNewPool( HeapRoot,
+ HeapRoot + 1,
+ POOL_SIZE - sizeof( DPH_HEAP_ROOT ),
+ FALSE
+ );
+
+ //
+ // Make initial PoolList entry by taking a node from the
+ // UnusedNodeList, which should be guaranteed to be non-empty
+ // since we just added new nodes to it.
+ //
+
+ Node = RtlpDebugPageHeapAllocateNode( HeapRoot );
+ DEBUG_ASSERT( Node != NULL );
+ Node->pVirtualBlock = (PVOID)HeapRoot;
+ Node->nVirtualBlockSize = POOL_SIZE;
+ RtlpDebugPageHeapPlaceOnPoolList( HeapRoot, Node );
+
+ //
+ // Make VirtualStorageList entry for initial VM allocation
+ //
+
+ Node = RtlpDebugPageHeapAllocateNode( HeapRoot );
+ DEBUG_ASSERT( Node != NULL );
+ Node->pVirtualBlock = pVirtual;
+ Node->nVirtualBlockSize = nVirtual;
+ RtlpDebugPageHeapPlaceOnVirtualList( HeapRoot, Node );
+
+ //
+ // Make AvailableList entry containing remainder of initial VM
+ // and add to (create) the AvailableList.
+ //
+
+ Node = RtlpDebugPageHeapAllocateNode( HeapRoot );
+ DEBUG_ASSERT( Node != NULL );
+ Node->pVirtualBlock = pVirtual + ( PAGE_SIZE + POOL_SIZE + PAGE_SIZE );
+ Node->nVirtualBlockSize = nVirtual - ( PAGE_SIZE + POOL_SIZE + PAGE_SIZE );
+ RtlpDebugPageHeapCoalesceNodeIntoAvailable( HeapRoot, Node );
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ HeapRoot->pStackTraceRoot = RtlpDebugPageHeapNewStackTraceStorage( HeapRoot, 0 );
+ DEBUG_ASSERT( HeapRoot->pStackTraceRoot != NULL );
+ HeapRoot->pStackTraceRoot->Left = NULL;
+ HeapRoot->pStackTraceRoot->Right = NULL;
+ HeapRoot->pStackTraceRoot->Hash = 0;
+ HeapRoot->pStackTraceRoot->BusyCount = 0;
+ HeapRoot->pStackTraceRoot->BusyBytes = 0;
+ HeapRoot->pStackTraceCreator = RtlpDebugPageHeapCaptureAndStoreStackTrace( HeapRoot, 1 );
+
+#endif // DPH_CAPTURE_STACK_TRACE
+
+ //
+ // Initialize heap internal structure protection.
+ //
+
+ HeapRoot->nUnProtectionReferenceCount = 1; // initialize
+ PROTECT_HEAP_STRUCTURES( HeapRoot ); // now protected
+
+ //
+ // If this is the first heap creation in this process, then we
+ // need to initialize the process heap list critical section.
+ //
+
+ if ( ! RtlpDebugPageHeapListHasBeenInitialized ) {
+ RtlpDebugPageHeapListHasBeenInitialized = TRUE;
+ RtlInitializeCriticalSection( &RtlpDebugPageHeapListCritSect );
+ }
+
+ //
+ // Add this heap entry to the process heap linked list.
+ //
+
+ RtlEnterCriticalSection( &RtlpDebugPageHeapListCritSect );
+
+ if ( RtlpDebugPageHeapListHead == NULL ) {
+ RtlpDebugPageHeapListHead = HeapRoot;
+ RtlpDebugPageHeapListTail = HeapRoot;
+ }
+ else {
+ HeapRoot->pPrevHeapRoot = RtlpDebugPageHeapListTail;
+ RtlpDebugPageHeapListTail->pNextHeapRoot = HeapRoot;
+ RtlpDebugPageHeapListTail = HeapRoot;
+ }
+
+ RtlpDebugPageHeapListCount++;
+
+ RtlLeaveCriticalSection( &RtlpDebugPageHeapListCritSect );
+
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+
+ DbgPrint( "PAGEHEAP: process 0x%X created debug heap %08X\n",
+ NtCurrentTeb()->ClientId.UniqueProcess,
+ HEAP_HANDLE_FROM_ROOT( HeapRoot ));
+
+ return HEAP_HANDLE_FROM_ROOT( HeapRoot ); // same as pVirtual
+ }
+
+
+PVOID
+RtlpDebugPageHeapAllocate(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION pAvailNode;
+ PDPH_HEAP_ALLOCATION pPrevAvailNode;
+ PDPH_HEAP_ALLOCATION pBusyNode;
+ ULONG nBytesAllocate;
+ ULONG nBytesAccess;
+ ULONG nActual;
+ PVOID pVirtual;
+ PVOID pReturn;
+ PUCHAR pBlockHeader;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return NULL;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ //
+ // Acquire the heap CritSect and unprotect the structures
+ //
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ pReturn = NULL;
+
+ //
+ // Validate requested size so we don't overflow
+ // while rounding up size computations. We do this
+ // after we've acquired the critsect so we can still
+ // catch serialization problems.
+ //
+
+ if ( Size > 0x7FFF0000 ) {
+ OUT_OF_VM_BREAK( Flags, "PAGEHEAP: Invalid allocation size\n" );
+ goto EXIT;
+ }
+
+ //
+ // Determine number of pages needed for READWRITE portion
+ // of allocation and add an extra page for the NO_ACCESS
+ // memory beyond the READWRITE page(s).
+ //
+
+ nBytesAccess = ROUNDUP2( Size, PAGE_SIZE );
+ nBytesAllocate = nBytesAccess + PAGE_SIZE;
+
+ //
+ // RtlpDebugPageHeapFindAvailableMem will first attempt to satisfy
+ // the request from memory on the Available list. If that fails,
+ // it will coalesce some of the Free list memory into the Available
+ // list and try again. If that still fails, new VM is allocated and
+ // added to the Available list. If that fails, the function will
+ // finally give up and return NULL.
+ //
+
+ pAvailNode = RtlpDebugPageHeapFindAvailableMem(
+ HeapRoot,
+ nBytesAllocate,
+ &pPrevAvailNode,
+ TRUE
+ );
+
+ if ( pAvailNode == NULL ) {
+ OUT_OF_VM_BREAK( Flags, "PAGEHEAP: Unable to allocate virtual memory\n" );
+ goto EXIT;
+ }
+
+ //
+ // Now can't call AllocateNode until pAvailNode is
+ // adjusted and/or removed from Avail list since AllocateNode
+ // might adjust the Avail list.
+ //
+
+ pVirtual = pAvailNode->pVirtualBlock;
+
+ if ( nBytesAccess > 0 ) {
+ if ( ! RtlpDebugPageHeapProtectVM( pVirtual, nBytesAccess, PAGE_READWRITE )) {
+ goto EXIT;
+ }
+ }
+
+ //
+ // pAvailNode (still on avail list) points to block large enough
+ // to satisfy request, but it might be large enough to split
+ // into two blocks -- one for request, remainder leave on
+ // avail list.
+ //
+
+ if ( pAvailNode->nVirtualBlockSize > nBytesAllocate ) {
+
+ //
+ // Adjust pVirtualBlock and nVirtualBlock size of existing
+ // node in avail list. The node will still be in correct
+ // address space order on the avail list. This saves having
+ // to remove and then re-add node to avail list. Note since
+ // we're changing sizes directly, we need to adjust the
+ // avail and busy list counters manually.
+ //
+ // Note: since we're leaving at least one page on the
+ // available list, we are guaranteed that AllocateNode
+ // will not fail.
+ //
+
+ pAvailNode->pVirtualBlock += nBytesAllocate;
+ pAvailNode->nVirtualBlockSize -= nBytesAllocate;
+ HeapRoot->nAvailableAllocationBytesCommitted -= nBytesAllocate;
+
+ pBusyNode = RtlpDebugPageHeapAllocateNode( HeapRoot );
+
+ DEBUG_ASSERT( pBusyNode != NULL );
+
+ pBusyNode->pVirtualBlock = pVirtual;
+ pBusyNode->nVirtualBlockSize = nBytesAllocate;
+
+ }
+
+ else {
+
+ //
+ // Entire avail block is needed, so simply remove it from avail list.
+ //
+
+ RtlpDebugPageHeapRemoveFromAvailableList( HeapRoot, pAvailNode, pPrevAvailNode );
+
+ pBusyNode = pAvailNode;
+
+ }
+
+ //
+ // Now pBusyNode points to our committed virtual block.
+ //
+
+ if ( HeapRoot->HeapFlags & HEAP_NO_ALIGNMENT )
+ nActual = Size;
+ else
+ nActual = ROUNDUP2( Size, USER_ALIGNMENT );
+
+ pBusyNode->nVirtualAccessSize = nBytesAccess;
+ pBusyNode->nUserRequestedSize = Size;
+ pBusyNode->nUserActualSize = nActual;
+ pBusyNode->pUserAllocation = pBusyNode->pVirtualBlock
+ + pBusyNode->nVirtualAccessSize
+ - nActual;
+ pBusyNode->UserValue = NULL;
+ pBusyNode->UserFlags = Flags & HEAP_SETTABLE_USER_FLAGS;
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ //
+ // RtlpDebugPageHeapAllocate gets called from RtlDebugAllocateHeap,
+ // which gets called from RtlAllocateHeapSlowly, which gets called
+ // from RtlAllocateHeap. To keep from wasting lots of stack trace
+ // storage, we'll skip the bottom 3 entries, leaving RtlAllocateHeap
+ // as the first recorded entry.
+ //
+
+ pBusyNode->pStackTrace = RtlpDebugPageHeapCaptureAndStoreStackTrace( HeapRoot, 3 );
+
+ if ( pBusyNode->pStackTrace ) {
+ pBusyNode->pStackTrace->BusyCount += 1;
+ pBusyNode->pStackTrace->BusyBytes += pBusyNode->nUserRequestedSize;
+ }
+
+#endif
+
+ RtlpDebugPageHeapPlaceOnBusyList( HeapRoot, pBusyNode );
+
+ pReturn = pBusyNode->pUserAllocation;
+
+ //
+ // For requests the specify HEAP_ZERO_MEMORY, we'll fill the
+ // user-requested portion of the block with zeros, but the
+ // 16 bytes (HEAD_FILL_SIZE) before the block and the odd
+ // alignment bytes beyond the requested size up to the end of
+ // the page are filled with 0xEEEEEEEE. For requests that
+ // don't specify HEAP_ZERO_MEMORY, we fill the whole request
+ // including the 16 bytes before the block and the alignment
+ // bytes beyond the block with 0xEEEEEEEE.
+ //
+
+ pBlockHeader = pBusyNode->pUserAllocation - HEAD_FILL_SIZE;
+
+ if ( pBlockHeader < pBusyNode->pVirtualBlock )
+ pBlockHeader = pBusyNode->pVirtualBlock;
+
+ if ( Flags & HEAP_ZERO_MEMORY ) {
+
+ RtlFillMemory( pBlockHeader,
+ pBusyNode->pUserAllocation - pBlockHeader,
+ FILL_BYTE );
+
+ RtlZeroMemory( pBusyNode->pUserAllocation, Size );
+
+ RtlFillMemory( pBusyNode->pUserAllocation + Size,
+ nActual - Size,
+ FILL_BYTE );
+ }
+ else {
+
+ RtlFillMemory( pBlockHeader,
+ pBusyNode->pUserAllocation + nActual - pBlockHeader,
+ FILL_BYTE );
+
+ }
+
+EXIT:
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ if ( pReturn == NULL ) {
+ IF_GENERATE_EXCEPTION( Flags, STATUS_NO_MEMORY );
+ }
+
+ return pReturn;
+
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapFree(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address
+ )
+ {
+
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION Node, Prev;
+ BOOLEAN Success;
+ PCH p;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return FALSE;
+
+ if ( Address == NULL )
+ return TRUE; // for C++ apps that delete NULL
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Success = FALSE;
+
+ Node = RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, &Prev );
+
+ if ( Node == NULL ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Attempt to reference block which is not allocated\n" );
+ goto EXIT;
+ }
+
+ //
+ // If tail was allocated, make sure filler not overwritten
+ //
+
+ if ( Node->nUserActualSize > Node->nUserRequestedSize ) {
+ RtlpDebugPageHeapDetectFillCorruption( Node );
+ }
+
+ if ( Node->nVirtualAccessSize > 0 ) {
+ RtlpDebugPageHeapProtectVM( Node->pVirtualBlock,
+ Node->nVirtualAccessSize,
+ PAGE_NOACCESS );
+ }
+
+ RtlpDebugPageHeapRemoveFromBusyList( HeapRoot, Node, Prev );
+
+ RtlpDebugPageHeapPlaceOnFreeList( HeapRoot, Node );
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ //
+ // RtlpDebugPageHeapFree gets called from RtlDebugFreeHeap, which
+ // gets called from RtlFreeHeapSlowly, which gets called from
+ // RtlFreeHeap. To keep from wasting lots of stack trace storage,
+ // we'll skip the bottom 3 entries, leaving RtlFreeHeap as the
+ // first recorded entry.
+ //
+
+ if ( Node->pStackTrace ) {
+ if ( Node->pStackTrace->BusyCount > 0 ) {
+ Node->pStackTrace->BusyCount -= 1;
+ Node->pStackTrace->BusyBytes -= Node->nUserRequestedSize;
+ }
+ }
+
+ Node->pStackTrace = RtlpDebugPageHeapCaptureAndStoreStackTrace( HeapRoot, 3 );
+
+#endif
+
+ Success = TRUE;
+
+EXIT:
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ if ( ! Success ) {
+ IF_GENERATE_EXCEPTION( Flags, STATUS_ACCESS_VIOLATION );
+ }
+
+ return Success;
+ }
+
+
+PVOID
+RtlpDebugPageHeapReAllocate(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ IN ULONG Size
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION OldNode, OldPrev, NewNode;
+ PVOID NewAddress;
+ PUCHAR p;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return NULL;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ NewAddress = NULL;
+
+ //
+ // Check Flags for non-moveable reallocation and fail it
+ // unconditionally. Apps that specify this flag should be
+ // prepared to deal with failure anyway.
+ //
+
+ if ( Flags & HEAP_REALLOC_IN_PLACE_ONLY ) {
+ goto EXIT;
+ }
+
+ //
+ // Validate requested size so we don't overflow
+ // while rounding up size computations. We do this
+ // after we've acquired the critsect so we can still
+ // catch serialization problems.
+ //
+
+ if ( Size > 0x7FFF0000 ) {
+ OUT_OF_VM_BREAK( Flags, "PAGEHEAP: Invalid allocation size\n" );
+ goto EXIT;
+ }
+
+ OldNode = RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, &OldPrev );
+
+ if ( OldNode == NULL ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Attempt to reference block which is not allocated\n" );
+ goto EXIT;
+ }
+
+ //
+ // If tail was allocated, make sure filler not overwritten
+ //
+
+ if ( OldNode->nUserActualSize > OldNode->nUserRequestedSize ) {
+ RtlpDebugPageHeapDetectFillCorruption( OldNode );
+ }
+
+ //
+ // Before allocating a new block, remove the old block from
+ // the busy list. When we allocate the new block, the busy
+ // list pointers will change, possibly leaving our acquired
+ // Prev pointer invalid.
+ //
+
+ RtlpDebugPageHeapRemoveFromBusyList( HeapRoot, OldNode, OldPrev );
+
+ //
+ // Allocate new memory for new requested size. Use try/except
+ // to trap exception if Flags caused out-of-memory exception.
+ //
+
+ try {
+ NewAddress = RtlpDebugPageHeapAllocate( HeapHandle, Flags, Size );
+ }
+ except( EXCEPTION_EXECUTE_HANDLER ) {
+ }
+
+ if ( NewAddress ) {
+
+ RtlCopyMemory(
+ NewAddress,
+ Address,
+ ( OldNode->nUserActualSize < Size ) ?
+ OldNode->nUserActualSize : Size
+ );
+
+ NewNode = RtlpDebugPageHeapFindBusyMem( HeapRoot, NewAddress, NULL );
+
+ DEBUG_ASSERT( NewNode != NULL );
+
+ NewNode->UserValue = OldNode->UserValue;
+ NewNode->UserFlags = ( Flags & HEAP_SETTABLE_USER_FLAGS ) ?
+ ( Flags & HEAP_SETTABLE_USER_FLAGS ) :
+ OldNode->UserFlags;
+
+ if ( OldNode->nVirtualAccessSize > 0 ) {
+ RtlpDebugPageHeapProtectVM( OldNode->pVirtualBlock,
+ OldNode->nVirtualAccessSize,
+ PAGE_NOACCESS );
+ }
+
+ RtlpDebugPageHeapPlaceOnFreeList( HeapRoot, OldNode );
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ //
+ // RtlpDebugPageHeapReAllocate gets called from RtlDebugReAllocateHeap,
+ // which gets called from RtlReAllocateHeap. To keep from wasting
+ // lots of stack trace storage, we'll skip the bottom 2 entries,
+ // leaving RtlReAllocateHeap as the first recorded entry in the
+ // freed stack trace.
+ //
+
+ if ( OldNode->pStackTrace ) {
+ if ( OldNode->pStackTrace->BusyCount > 0 ) {
+ OldNode->pStackTrace->BusyCount -= 1;
+ OldNode->pStackTrace->BusyBytes -= OldNode->nUserRequestedSize;
+ }
+ }
+
+ OldNode->pStackTrace = RtlpDebugPageHeapCaptureAndStoreStackTrace( HeapRoot, 2 );
+
+#endif
+ }
+
+ else {
+
+ //
+ // Failed to allocate a new block. Return old block to busy list.
+ //
+
+ RtlpDebugPageHeapPlaceOnBusyList( HeapRoot, OldNode );
+
+ }
+
+EXIT:
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ if ( NewAddress == NULL ) {
+ IF_GENERATE_EXCEPTION( Flags, STATUS_NO_MEMORY );
+ }
+
+ return NewAddress;
+ }
+
+
+#if (( DPH_CAPTURE_STACK_TRACE ) && ( i386 ) && ( FPO ))
+#pragma optimize( "", on ) // restore original optimizations
+#endif
+
+
+PVOID
+RtlpDebugPageHeapDestroy(
+ IN PVOID HeapHandle
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ROOT PrevHeapRoot;
+ PDPH_HEAP_ROOT NextHeapRoot;
+ PDPH_HEAP_ALLOCATION Node;
+ PDPH_HEAP_ALLOCATION Next;
+ ULONG Flags;
+ PUCHAR p;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return NULL;
+
+ Flags = HeapRoot->HeapFlags | HEAP_NO_SERIALIZE;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ //
+ // Walk all busy allocations and check for tail fill corruption
+ //
+
+ Node = HeapRoot->pBusyAllocationListHead;
+
+ while ( Node ) {
+
+ if ( Node->nUserActualSize > Node->nUserRequestedSize ) {
+ RtlpDebugPageHeapDetectFillCorruption( Node );
+ }
+
+ Node = Node->pNextAlloc;
+ }
+
+ //
+ // Remove this heap entry from the process heap linked list.
+ //
+
+ RtlEnterCriticalSection( &RtlpDebugPageHeapListCritSect );
+
+ if ( HeapRoot->pPrevHeapRoot ) {
+ HeapRoot->pPrevHeapRoot->pNextHeapRoot = HeapRoot->pNextHeapRoot;
+ }
+ else {
+ RtlpDebugPageHeapListHead = HeapRoot->pNextHeapRoot;
+ }
+
+ if ( HeapRoot->pNextHeapRoot ) {
+ HeapRoot->pNextHeapRoot->pPrevHeapRoot = HeapRoot->pPrevHeapRoot;
+ }
+ else {
+ RtlpDebugPageHeapListTail = HeapRoot->pPrevHeapRoot;
+ }
+
+ RtlpDebugPageHeapListCount--;
+
+ RtlLeaveCriticalSection( &RtlpDebugPageHeapListCritSect );
+
+
+ //
+ // Must release critical section before deleting it; otherwise,
+ // checked build Teb->CountOfOwnedCriticalSections gets out of sync.
+ //
+
+ RtlLeaveCriticalSection( HeapRoot->HeapCritSect );
+ RtlDeleteCriticalSection( HeapRoot->HeapCritSect );
+
+ //
+ // This is weird. A virtual block might contain storage for
+ // one of the nodes necessary to walk this list. In fact,
+ // we're guaranteed that the root node contains at least one
+ // virtual alloc node.
+ //
+ // Each time we alloc new VM, we make that the head of the
+ // of the VM list, like a LIFO structure. I think we're ok
+ // because no VM list node should be on a subsequently alloc'd
+ // VM -- only a VM list entry might be on its own memory (as
+ // is the case for the root node). We read pNode->pNextAlloc
+ // before releasing the VM in case pNode existed on that VM.
+ // I think this is safe -- as long as the VM list is LIFO and
+ // we don't do any list reorganization.
+ //
+
+ Node = HeapRoot->pVirtualStorageListHead;
+
+ while ( Node ) {
+ Next = Node->pNextAlloc;
+ if ( ! RtlpDebugPageHeapReleaseVM( Node->pVirtualBlock )) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Unable to release virtual memory\n" );
+ }
+ Node = Next;
+ }
+
+ //
+ // That's it. All the VM, including the root node, should now
+ // be released. RtlDestroyHeap always returns NULL.
+ //
+
+ return NULL;
+
+ }
+
+
+ULONG
+RtlpDebugPageHeapSize(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION Node;
+ ULONG Size;
+
+ Size = 0xFFFFFFFF;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL ) {
+ return Size;
+ }
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Node = RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, NULL );
+
+ if ( Node == NULL ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Attempt to reference block which is not allocated\n" );
+ }
+ else {
+ Size = Node->nUserRequestedSize;
+ }
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ if ( Size == 0xFFFFFFFF ) {
+ IF_GENERATE_EXCEPTION( Flags, STATUS_ACCESS_VIOLATION );
+ }
+
+ return Size;
+ }
+
+
+ULONG
+RtlpDebugPageHeapGetProcessHeaps(
+ ULONG NumberOfHeaps,
+ PVOID *ProcessHeaps
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ ULONG Count;
+
+ //
+ // Although we'd expect GetProcessHeaps never to be called
+ // before at least the very first heap creation, we should
+ // still be safe and initialize the critical section if
+ // necessary.
+ //
+
+ if ( ! RtlpDebugPageHeapListHasBeenInitialized ) {
+ RtlpDebugPageHeapListHasBeenInitialized = TRUE;
+ RtlInitializeCriticalSection( &RtlpDebugPageHeapListCritSect );
+ }
+
+ RtlEnterCriticalSection( &RtlpDebugPageHeapListCritSect );
+
+ if ( RtlpDebugPageHeapListCount <= NumberOfHeaps ) {
+
+ for ( HeapRoot = RtlpDebugPageHeapListHead, Count = 0;
+ HeapRoot != NULL;
+ HeapRoot = HeapRoot->pNextHeapRoot, Count++ ) {
+
+ *ProcessHeaps++ = HEAP_HANDLE_FROM_ROOT( HeapRoot );
+ }
+
+ if ( Count != RtlpDebugPageHeapListCount ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: BUG: process heap list count wrong\n" );
+ }
+
+ }
+ else {
+
+ //
+ // User's buffer is too small. Return number of entries
+ // necessary for subsequent call to succeed. Buffer
+ // remains untouched.
+ //
+
+ Count = RtlpDebugPageHeapListCount;
+
+ }
+
+ RtlLeaveCriticalSection( &RtlpDebugPageHeapListCritSect );
+
+ return Count;
+ }
+
+
+ULONG
+RtlpDebugPageHeapCompact(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return 0;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+
+ //
+ // Don't do anything, but we did want to acquire the critsect
+ // in case this was called with HEAP_NO_SERIALIZE while another
+ // thread is in the heap code.
+ //
+
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return 0;
+
+ }
+
+
+
+BOOLEAN
+RtlpDebugPageHeapValidate(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION Node;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return FALSE;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ DEBUG_CODE( RtlpDebugPageHeapVerifyIntegrity( HeapRoot ));
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Node = Address ? RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, NULL ) : NULL;
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return ( Address ? ( Node != NULL ) : TRUE );
+ }
+
+
+NTSTATUS
+RtlpDebugPageHeapWalk(
+ IN PVOID HeapHandle,
+ IN OUT PRTL_HEAP_WALK_ENTRY Entry
+ )
+ {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Not implemented\n" );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapLock(
+ IN PVOID HeapHandle
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return FALSE;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, HeapRoot->HeapFlags );
+
+ return TRUE;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapUnlock(
+ IN PVOID HeapHandle
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return FALSE;
+
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return TRUE;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapSetUserValue(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ IN PVOID UserValue
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION Node;
+ BOOLEAN Success;
+
+ Success = FALSE;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return Success;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Node = RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, NULL );
+
+ if ( Node == NULL ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Attempt to reference block which is not allocated\n" );
+ }
+ else {
+ Node->UserValue = UserValue;
+ Success = TRUE;
+ }
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return Success;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapGetUserInfo(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ OUT PVOID* UserValue,
+ OUT PULONG UserFlags
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION Node;
+ BOOLEAN Success;
+
+ Success = FALSE;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return Success;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Node = RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, NULL );
+
+ if ( Node == NULL ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Attempt to reference block which is not allocated\n" );
+ }
+ else {
+ if ( UserValue != NULL )
+ *UserValue = Node->UserValue;
+ if ( UserFlags != NULL )
+ *UserFlags = Node->UserFlags;
+ Success = TRUE;
+ }
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return Success;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapSetUserFlags(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ IN ULONG UserFlagsReset,
+ IN ULONG UserFlagsSet
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+ PDPH_HEAP_ALLOCATION Node;
+ BOOLEAN Success;
+
+ Success = FALSE;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return Success;
+
+ Flags |= HeapRoot->HeapFlags;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, Flags );
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Node = RtlpDebugPageHeapFindBusyMem( HeapRoot, Address, NULL );
+
+ if ( Node == NULL ) {
+ RtlpDebugPageHeapBreak( "PAGEHEAP: Attempt to reference block which is not allocated\n" );
+ }
+ else {
+ Node->UserFlags &= ~( UserFlagsReset );
+ Node->UserFlags |= UserFlagsSet;
+ Success = TRUE;
+ }
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return Success;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapSerialize(
+ IN PVOID HeapHandle
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return FALSE;
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, 0 );
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ HeapRoot->HeapFlags &= ~HEAP_NO_SERIALIZE;
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return TRUE;
+ }
+
+
+NTSTATUS
+RtlpDebugPageHeapExtend(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Base,
+ IN ULONG Size
+ )
+ {
+ return STATUS_SUCCESS;
+ }
+
+
+NTSTATUS
+RtlpDebugPageHeapZero(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ )
+ {
+ return STATUS_SUCCESS;
+ }
+
+
+NTSTATUS
+RtlpDebugPageHeapUsage(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN OUT PRTL_HEAP_USAGE Usage
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+
+ //
+ // Partial implementation since this information is kind of meaningless.
+ //
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return STATUS_INVALID_PARAMETER;
+
+ if ( Usage->Length != sizeof( RTL_HEAP_USAGE ))
+ return STATUS_INFO_LENGTH_MISMATCH;
+
+ memset( Usage, 0, sizeof( RTL_HEAP_USAGE ));
+ Usage->Length = sizeof( RTL_HEAP_USAGE );
+
+ RtlpDebugPageHeapEnterCritSect( HeapRoot, 0 );
+ UNPROTECT_HEAP_STRUCTURES( HeapRoot );
+
+ Usage->BytesAllocated = HeapRoot->nBusyAllocationBytesAccessible;
+ Usage->BytesCommitted = HeapRoot->nVirtualStorageBytes;
+ Usage->BytesReserved = HeapRoot->nVirtualStorageBytes;
+ Usage->BytesReservedMaximum = HeapRoot->nVirtualStorageBytes;
+
+ PROTECT_HEAP_STRUCTURES( HeapRoot );
+ RtlpDebugPageHeapLeaveCritSect( HeapRoot );
+
+ return STATUS_SUCCESS;
+ }
+
+
+BOOLEAN
+RtlpDebugPageHeapIsLocked(
+ IN PVOID HeapHandle
+ )
+ {
+ PDPH_HEAP_ROOT HeapRoot;
+
+ HeapRoot = RtlpDebugPageHeapPointerFromHandle( HeapHandle );
+ if ( HeapRoot == NULL )
+ return FALSE;
+
+ if ( RtlTryEnterCriticalSection( HeapRoot->HeapCritSect )) {
+ RtlLeaveCriticalSection( HeapRoot->HeapCritSect );
+ return FALSE;
+ }
+ else {
+ return TRUE;
+ }
+ }
+
+
+#endif // DEBUG_PAGE_HEAP
+
+
+
+
diff --git a/private/ntos/rtl/heappage.h b/private/ntos/rtl/heappage.h
new file mode 100644
index 000000000..3e50cc769
--- /dev/null
+++ b/private/ntos/rtl/heappage.h
@@ -0,0 +1,229 @@
+
+//
+// heappage.h
+//
+
+#ifndef _HEAP_PAGE_H_
+#define _HEAP_PAGE_H_
+
+//
+// #defining DEBUG_PAGE_HEAP will cause the page heap manager
+// to be compiled. Only #define this flag if NOT kernel mode.
+// Probably want to define this just for checked-build (DBG).
+//
+
+#ifndef NTOS_KERNEL_RUNTIME
+ #if DBG
+ #define DEBUG_PAGE_HEAP 1
+ #endif
+#endif
+
+#include "heappagi.h"
+
+#ifndef DEBUG_PAGE_HEAP
+
+//
+// These macro-based hooks should be defined to nothing so they
+// simply "go away" during compile if the debug heap manager is
+// not desired (retail builds).
+//
+
+#define IF_DEBUG_PAGE_HEAP_THEN_RETURN( Handle, ReturnThis )
+#define IF_DEBUG_PAGE_HEAP_THEN_CALL( Handle, CallThis )
+#define IF_DEBUG_PAGE_HEAP_THEN_BREAK( Handle, Text, ReturnThis )
+
+#define HEAP_FLAG_PAGE_ALLOCS 0
+
+#else // DEBUG_PAGE_HEAP
+
+//
+// The following definitions and prototypes are the external interface
+// for hooking the debug heap manager in the retail heap manager.
+//
+
+#define HEAP_FLAG_PAGE_ALLOCS 0x01000000
+
+#define HEAP_PROTECTION_ENABLED 0x02000000
+#define HEAP_BREAK_WHEN_OUT_OF_VM 0x04000000
+#define HEAP_NO_ALIGNMENT 0x08000000
+
+
+#define IS_DEBUG_PAGE_HEAP_HANDLE( HeapHandle ) \
+ (((PHEAP)(HeapHandle))->ForceFlags & HEAP_FLAG_PAGE_ALLOCS )
+
+
+#define IF_DEBUG_PAGE_HEAP_THEN_RETURN( Handle, ReturnThis ) \
+ { \
+ if ( IS_DEBUG_PAGE_HEAP_HANDLE( Handle )) \
+ { \
+ return ReturnThis; \
+ } \
+ }
+
+
+#define IF_DEBUG_PAGE_HEAP_THEN_CALL( Handle, CallThis ) \
+ { \
+ if ( IS_DEBUG_PAGE_HEAP_HANDLE( Handle )) \
+ { \
+ CallThis; \
+ return; \
+ } \
+ }
+
+
+#define IF_DEBUG_PAGE_HEAP_THEN_BREAK( Handle, Text, ReturnThis ) \
+ { \
+ if ( IS_DEBUG_PAGE_HEAP_HANDLE( Handle )) \
+ { \
+ RtlpDebugPageHeapBreak( Text ); \
+ return ReturnThis; \
+ } \
+ }
+
+
+PVOID
+RtlpDebugPageHeapCreate(
+ IN ULONG Flags,
+ IN PVOID HeapBase,
+ IN ULONG ReserveSize,
+ IN ULONG CommitSize,
+ IN PVOID Lock,
+ IN PRTL_HEAP_PARAMETERS Parameters
+ );
+
+PVOID
+RtlpDebugPageHeapAllocate(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ );
+
+BOOLEAN
+RtlpDebugPageHeapFree(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address
+ );
+
+PVOID
+RtlpDebugPageHeapReAllocate(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ IN ULONG Size
+ );
+
+PVOID
+RtlpDebugPageHeapDestroy(
+ IN PVOID HeapHandle
+ );
+
+ULONG
+RtlpDebugPageHeapSize(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address
+ );
+
+ULONG
+RtlpDebugPageHeapGetProcessHeaps(
+ ULONG NumberOfHeaps,
+ PVOID *ProcessHeaps
+ );
+
+ULONG
+RtlpDebugPageHeapCompact(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ );
+
+BOOLEAN
+RtlpDebugPageHeapValidate(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address
+ );
+
+NTSTATUS
+RtlpDebugPageHeapWalk(
+ IN PVOID HeapHandle,
+ IN OUT PRTL_HEAP_WALK_ENTRY Entry
+ );
+
+BOOLEAN
+RtlpDebugPageHeapLock(
+ IN PVOID HeapHandle
+ );
+
+BOOLEAN
+RtlpDebugPageHeapUnlock(
+ IN PVOID HeapHandle
+ );
+
+BOOLEAN
+RtlpDebugPageHeapSetUserValue(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ IN PVOID UserValue
+ );
+
+BOOLEAN
+RtlpDebugPageHeapGetUserInfo(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ OUT PVOID* UserValue,
+ OUT PULONG UserFlags
+ );
+
+BOOLEAN
+RtlpDebugPageHeapSetUserFlags(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Address,
+ IN ULONG UserFlagsReset,
+ IN ULONG UserFlagsSet
+ );
+
+BOOLEAN
+RtlpDebugPageHeapSerialize(
+ IN PVOID HeapHandle
+ );
+
+NTSTATUS
+RtlpDebugPageHeapExtend(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID Base,
+ IN ULONG Size
+ );
+
+NTSTATUS
+RtlpDebugPageHeapZero(
+ IN PVOID HeapHandle,
+ IN ULONG Flags
+ );
+
+NTSTATUS
+RtlpDebugPageHeapUsage(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN OUT PRTL_HEAP_USAGE Usage
+ );
+
+BOOLEAN
+RtlpDebugPageHeapIsLocked(
+ IN PVOID HeapHandle
+ );
+
+VOID
+RtlpDebugPageHeapBreak(
+ PCH Text
+ );
+
+
+#endif // DEBUG_PAGE_HEAP
+
+#endif // _HEAP_PAGE_H_
+
diff --git a/private/ntos/rtl/heappagi.h b/private/ntos/rtl/heappagi.h
new file mode 100644
index 000000000..f00b255ee
--- /dev/null
+++ b/private/ntos/rtl/heappagi.h
@@ -0,0 +1,225 @@
+
+//
+// heappagi.h
+//
+// The following definitions are internal to the debug heap manager,
+// but are placed in this include file so that debugger extensions
+// can reference the same structure definitions. The following
+// definitions are not intended to be referenced externally except
+// by debugger extensions.
+//
+
+#ifndef _HEAP_PAGE_I_
+#define _HEAP_PAGE_I_
+
+#ifdef DEBUG_PAGE_HEAP
+
+#include "heap.h"
+
+#define DPH_INTERNAL_DEBUG 0 // change to 0 or #undef for production code
+
+#define DPH_MAX_STACK_LENGTH 20
+
+#if defined(_X86_) // RtlCaptureStackBackTrace() only implemented on x86
+ #if DBG // RtlCaptureStackBackTrace() only consistent with no FPO
+ #define DPH_CAPTURE_STACK_TRACE 1
+ #endif // DBG
+#endif // _X86_
+
+
+#if DPH_CAPTURE_STACK_TRACE
+
+typedef struct _DPH_STACK_TRACE_NODE DPH_STACK_TRACE_NODE, *PDPH_STACK_TRACE_NODE;
+
+struct _DPH_STACK_TRACE_NODE {
+
+ PDPH_STACK_TRACE_NODE Left; // B-tree on Hash
+ PDPH_STACK_TRACE_NODE Right; // B-tree on Hash
+
+ ULONG Hash; // simple sum of PVOIDs in stack trace
+ ULONG Length; // number of PVOIDs in stack trace
+
+ ULONG BusyCount; // number of busy allocations
+ ULONG BusyBytes; // total user size of busy allocations
+
+ PVOID Address[ 0 ]; // variable length array of addresses
+ };
+
+#endif // DPH_CAPTURE_STACK_TRACE
+
+
+typedef struct _DPH_HEAP_ALLOCATION DPH_HEAP_ALLOCATION, *PDPH_HEAP_ALLOCATION;
+
+struct _DPH_HEAP_ALLOCATION {
+
+ //
+ // Singly linked list of allocations (pNextAlloc must be
+ // first member in structure).
+ //
+
+ PDPH_HEAP_ALLOCATION pNextAlloc;
+
+ //
+ // | PAGE_READWRITE | PAGE_NOACCESS |
+ // |____________________|___||_________________________|
+ //
+ // ^pVirtualBlock ^pUserAllocation
+ //
+ // |---------------- nVirtualBlockSize ----------------|
+ //
+ // |---nVirtualAccessSize----|
+ //
+ // |---| nUserRequestedSize
+ //
+ // |----| nUserActualSize
+ //
+
+ PUCHAR pVirtualBlock;
+ ULONG nVirtualBlockSize;
+
+ ULONG nVirtualAccessSize;
+ PUCHAR pUserAllocation;
+ ULONG nUserRequestedSize;
+ ULONG nUserActualSize;
+ PVOID UserValue;
+ ULONG UserFlags;
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ PDPH_STACK_TRACE_NODE pStackTrace;
+
+#endif
+
+ };
+
+
+typedef struct _DPH_HEAP_ROOT DPH_HEAP_ROOT, *PDPH_HEAP_ROOT;
+
+struct _DPH_HEAP_ROOT {
+
+ //
+ // Maintain a signature (DPH_HEAP_ROOT_SIGNATURE) as the
+ // first value in the heap root structure.
+ //
+
+ ULONG Signature;
+ ULONG HeapFlags;
+
+ //
+ // Access to this heap is synchronized with a critical section.
+ //
+
+ PRTL_CRITICAL_SECTION HeapCritSect;
+ ULONG nRemoteLockAcquired;
+
+ //
+ // The "VirtualStorage" list only uses the pVirtualBlock,
+ // nVirtualBlockSize, and nVirtualAccessSize fields of the
+ // HEAP_ALLOCATION structure. This is the list of virtual
+ // allocation entries that all the heap allocations are
+ // taken from.
+ //
+
+ PDPH_HEAP_ALLOCATION pVirtualStorageListHead;
+ PDPH_HEAP_ALLOCATION pVirtualStorageListTail;
+ ULONG nVirtualStorageRanges;
+ ULONG nVirtualStorageBytes;
+
+ //
+ // The "Busy" list is the list of active heap allocations.
+ // It is stored in LIFO order to improve temporal locality
+ // for linear searches since most initial heap allocations
+ // tend to remain permanent throughout a process's lifetime.
+ //
+
+ PDPH_HEAP_ALLOCATION pBusyAllocationListHead;
+ PDPH_HEAP_ALLOCATION pBusyAllocationListTail;
+ ULONG nBusyAllocations;
+ ULONG nBusyAllocationBytesCommitted;
+
+ //
+ // The "Free" list is the list of freed heap allocations, stored
+ // in FIFO order to increase the length of time a freed block
+ // remains on the freed list without being used to satisfy an
+ // allocation request. This increases the odds of catching
+ // a reference-after-freed bug in an app.
+ //
+
+ PDPH_HEAP_ALLOCATION pFreeAllocationListHead;
+ PDPH_HEAP_ALLOCATION pFreeAllocationListTail;
+ ULONG nFreeAllocations;
+ ULONG nFreeAllocationBytesCommitted;
+
+ //
+ // The "Available" list is stored in address-sorted order to facilitate
+ // coalescing. When an allocation request cannot be satisfied from the
+ // "Available" list, it is attempted from the free list. If it cannot
+ // be satisfied from the free list, the free list is coalesced into the
+ // available list. If the request still cannot be satisfied from the
+ // coalesced available list, new VM is added to the available list.
+ //
+
+ PDPH_HEAP_ALLOCATION pAvailableAllocationListHead;
+ PDPH_HEAP_ALLOCATION pAvailableAllocationListTail;
+ ULONG nAvailableAllocations;
+ ULONG nAvailableAllocationBytesCommitted;
+
+ //
+ // The "UnusedNode" list is simply a list of available node
+ // entries to place "Busy", "Free", or "Virtual" entries.
+ // When freed nodes get coalesced into a single free node,
+ // the other "unused" node goes on this list. When a new
+ // node is needed (like an allocation not satisfied from the
+ // free list), the node comes from this list if it's not empty.
+ //
+
+ PDPH_HEAP_ALLOCATION pUnusedNodeListHead;
+ PDPH_HEAP_ALLOCATION pUnusedNodeListTail;
+ ULONG nUnusedNodes;
+
+ ULONG nBusyAllocationBytesAccessible;
+
+ //
+ // Node pools need to be tracked so they can be protected
+ // from app scribbling on them.
+ //
+
+ PDPH_HEAP_ALLOCATION pNodePoolListHead;
+ PDPH_HEAP_ALLOCATION pNodePoolListTail;
+ ULONG nNodePools;
+ ULONG nNodePoolBytes;
+
+ //
+ // Doubly linked list of DPH heaps in process is tracked through this.
+ //
+
+ PDPH_HEAP_ROOT pNextHeapRoot;
+ PDPH_HEAP_ROOT pPrevHeapRoot;
+
+ ULONG nUnProtectionReferenceCount;
+ ULONG InsideAllocateNode; // only for debugging
+
+#if DPH_CAPTURE_STACK_TRACE
+
+ PUCHAR pStackTraceStorage;
+ ULONG nStackTraceStorage;
+
+ PDPH_STACK_TRACE_NODE pStackTraceRoot; // B-tree root
+ PDPH_STACK_TRACE_NODE pStackTraceCreator;
+
+ ULONG nStackTraceBytesCommitted;
+ ULONG nStackTraceBytesWasted;
+
+ ULONG nStackTraceBNodes;
+ ULONG nStackTraceBDepth;
+ ULONG nStackTraceBHashCollisions;
+
+#endif // DPH_CAPTURE_STACK_TRACE
+
+ };
+
+
+#endif // DEBUG_PAGE_HEAP
+
+#endif // _HEAP_PAGE_I_
+
diff --git a/private/ntos/rtl/heappriv.h b/private/ntos/rtl/heappriv.h
new file mode 100644
index 000000000..4f224ca99
--- /dev/null
+++ b/private/ntos/rtl/heappriv.h
@@ -0,0 +1,513 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ heappriv.h
+
+Abstract:
+
+ Private include file used by heap allocator (heap.c, heapdll.c and heapdbg.c)
+
+Author:
+
+ Steve Wood (stevewo) 25-Oct-1994
+
+Revision History:
+
+--*/
+
+#ifndef _RTL_HEAP_PRIVATE_
+#define _RTL_HEAP_PRIVATE_
+
+#include "heappage.h"
+
+#if DBG
+#define HEAPASSERT(exp) if (!(exp)) RtlAssert( #exp, __FILE__, __LINE__, NULL )
+#else
+#define HEAPASSERT(exp)
+#endif
+
+#ifdef NTOS_KERNEL_RUNTIME
+
+#define SET_LAST_STATUS( S )
+#define HEAP_DEBUG_FLAGS 0
+
+//
+// Kernel mode heap uses the kernel resource package for locking
+//
+#define RtlInitializeLockRoutine(L) ExInitializeResource((PERESOURCE)(L))
+#define RtlAcquireLockRoutine(L) ExAcquireResourceExclusive((PERESOURCE)(L), TRUE)
+#define RtlReleaseLockRoutine(L) ExReleaseResource((PERESOURCE)(L))
+#define RtlDeleteLockRoutine(L) ExDeleteResource((PERESOURCE)(L))
+#define RtlOkayToLockRoutine(L) ExOkayToLockRoutine((PERESOURCE)(L))
+
+#else
+
+#define HEAP_DEBUG_FLAGS (HEAP_VALIDATE_PARAMETERS_ENABLED | \
+ HEAP_VALIDATE_ALL_ENABLED | \
+ HEAP_CREATE_ENABLE_TRACING | \
+ HEAP_FLAG_PAGE_ALLOCS)
+
+#define DEBUG_HEAP( F ) ((F & HEAP_DEBUG_FLAGS) && !(F & HEAP_SKIP_VALIDATION_CHECKS))
+#define SET_LAST_STATUS( S ) NtCurrentTeb()->LastErrorValue = RtlNtStatusToDosError( NtCurrentTeb()->LastStatusValue = (ULONG)(S) )
+
+BOOLEAN
+RtlpValidateHeapHeaders(
+ IN PHEAP Heap,
+ IN BOOLEAN Recompute
+ );
+
+//
+// User mode heap uses the critical section package for locking
+//
+#define RtlInitializeLockRoutine(L) RtlInitializeCriticalSection((PRTL_CRITICAL_SECTION)(L))
+#define RtlAcquireLockRoutine(L) RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)(L))
+#define RtlReleaseLockRoutine(L) RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)(L))
+#define RtlDeleteLockRoutine(L) RtlDeleteCriticalSection((PRTL_CRITICAL_SECTION)(L))
+#define RtlOkayToLockRoutine(L) NtdllOkayToLockRoutine((PVOID)(L))
+
+#endif // NTOS_KERNEL_RUNTIME
+
+#ifdef THEAP
+#include "stdio.h"
+#include "string.h"
+#define DPRINTF printf
+#else
+#define DPRINTF DbgPrint
+#endif
+
+#if DBG && !defined(NTOS_KERNEL_RUNTIME)
+#define ENABLE_HEAP_EVENT_LOGGING 1
+#else
+#define ENABLE_HEAP_EVENT_LOGGING 0
+#endif
+
+#if ENABLE_HEAP_EVENT_LOGGING
+PRTL_EVENT_ID_INFO RtlpCreateHeapEventId;
+PRTL_EVENT_ID_INFO RtlpDestroyHeapEventId;
+PRTL_EVENT_ID_INFO RtlpAllocHeapEventId;
+PRTL_EVENT_ID_INFO RtlpFreeHeapEventId;
+PRTL_EVENT_ID_INFO RtlpReAllocHeapEventId;
+#endif // ENABLE_HEAP_EVENT_LOGGING
+
+UCHAR CheckHeapFillPattern[ CHECK_HEAP_TAIL_SIZE ];
+
+#if !defined(NTOS_KERNEL_RUNTIME)
+VOID
+RtlpBreakPointHeap( PVOID BadAddress );
+
+#define HeapDebugPrint( _x_ ) { \
+ PLIST_ENTRY _Module; \
+ PLDR_DATA_TABLE_ENTRY _Entry; \
+ \
+ _Module = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink; \
+ _Entry = CONTAINING_RECORD(_Module, \
+ LDR_DATA_TABLE_ENTRY, \
+ InLoadOrderLinks); \
+ DPRINTF("HEAP[%wZ]: ", \
+ &_Entry->BaseDllName); \
+ DPRINTF _x_; \
+ }
+
+#define HeapDebugBreak( _x_ ) RtlpBreakPointHeap( (_x_) )
+
+VOID
+RtlpAddHeapToProcessList(
+ IN PHEAP Heap
+ );
+
+VOID
+RtlpRemoveHeapFromProcessList(
+ IN PHEAP Heap
+ );
+
+#if DBG
+#define HeapInternalTrace( _h_, _x_ ) if (_h_->TraceBuffer) RtlTraceEvent _x_
+#else
+#define HeapInternalTrace( _h_, _x_ )
+#endif // DBG
+PRTL_TRACE_BUFFER
+RtlpHeapCreateTraceBuffer(
+ IN PHEAP Heap
+ );
+
+#define HeapTrace( _h_, _x_ ) if (RtlpHeapCreateTraceBuffer( _h_ )) RtlTraceEvent _x_
+#else
+#define HeapDebugPrint KdPrint
+#define HeapDebugBreak( _x_ ) if (KdDebuggerEnabled) DbgBreakPoint()
+#define HeapTrace( _h_, _x_ )
+#define HeapInternalTrace( _h_, _x_ )
+#endif // !defined(NTOS_KERNEL_RUNTIME)
+
+#define HEAP_TRACE_ALLOC 0
+#define HEAP_TRACE_REALLOC 1
+#define HEAP_TRACE_FREE 2
+#define HEAP_TRACE_SIZE 3
+#define HEAP_TRACE_GET_INFO 4
+#define HEAP_TRACE_SET_VALUE 5
+#define HEAP_TRACE_SET_FLAGS 6
+#if DBG
+#define HEAP_TRACE_COMMIT_MEMORY 7
+#define HEAP_TRACE_COMMIT_INSERT 8
+#define HEAP_TRACE_COMMIT_NEW_ENTRY 9
+#define HEAP_TRACE_INSERT_FREE_BLOCK 10
+#define HEAP_TRACE_UNLINK_FREE_BLOCK 11
+#define HEAP_TRACE_COALESCE_FREE_BLOCKS 12
+#define HEAP_TRACE_EXTEND_HEAP 13
+#define HEAP_TRACE_MAX_EVENT (HEAP_TRACE_EXTEND_HEAP+1)
+#else
+#define HEAP_TRACE_MAX_EVENT (HEAP_TRACE_SET_FLAGS+1)
+#endif // DBG
+
+BOOLEAN
+RtlpInitializeHeapSegment(
+ IN PHEAP Heap,
+ IN PHEAP_SEGMENT Segment,
+ IN UCHAR SegmentIndex,
+ IN ULONG Flags,
+ IN PVOID BaseAddress,
+ IN PVOID UnCommittedAddress,
+ IN PVOID CommitLimitAddress
+ );
+
+PHEAP_FREE_ENTRY
+RtlpCoalesceFreeBlocks(
+ IN PHEAP Heap,
+ IN PHEAP_FREE_ENTRY FreeBlock,
+ IN OUT PULONG FreeSize,
+ IN BOOLEAN RemoveFromFreeList
+ );
+
+PHEAP_FREE_ENTRY
+RtlpCoalesceHeap(
+ IN PHEAP Heap
+ );
+
+VOID
+RtlpUpdateHeapListIndex(
+ USHORT OldIndex,
+ USHORT NewIndex
+ );
+
+VOID
+RtlpDeCommitFreeBlock(
+ IN PHEAP Heap,
+ IN PHEAP_FREE_ENTRY FreeBlock,
+ IN ULONG FreeSize
+ );
+
+VOID
+RtlpInsertFreeBlock(
+ IN PHEAP Heap,
+ IN PHEAP_FREE_ENTRY FreeBlock,
+ IN ULONG FreeSize
+ );
+
+PHEAP_FREE_ENTRY
+RtlpFindAndCommitPages(
+ IN PHEAP Heap,
+ IN PHEAP_SEGMENT Segment,
+ IN OUT PULONG Size,
+ IN PVOID AddressWanted OPTIONAL
+ );
+
+PVOID
+RtlAllocateHeapSlowly(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN ULONG Size
+ );
+
+BOOLEAN
+RtlFreeHeapSlowly(
+ IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID BaseAddress
+ );
+
+//
+// Macro for setting a bit in the freelist vector to indicate entries are present.
+//
+#define SET_FREELIST_BIT( H, FB ) \
+{ \
+ ULONG _Index_; \
+ ULONG _Bit_; \
+ \
+ HEAPASSERT((FB)->Size < HEAP_MAXIMUM_FREELISTS); \
+ _Index_ = (FB)->Size >> 3; \
+ _Bit_ = (1 << ((FB)->Size & 7)); \
+ \
+ HEAPASSERT(((H)->u.FreeListsInUseBytes[ _Index_ ] & _Bit_) == 0); \
+ (H)->u.FreeListsInUseBytes[ _Index_ ] |= _Bit_; \
+}
+
+//
+// Macro for clearing a bit in the freelist vector to indicate entries are not present.
+//
+#define CLEAR_FREELIST_BIT( H, FB ) \
+{ \
+ ULONG _Index_; \
+ ULONG _Bit_; \
+ \
+ HEAPASSERT((FB)->Size < HEAP_MAXIMUM_FREELISTS); \
+ _Index_ = (FB)->Size >> 3; \
+ _Bit_ = (1 << ((FB)->Size & 7)); \
+ \
+ HEAPASSERT((H)->u.FreeListsInUseBytes[ _Index_ ] & _Bit_); \
+ HEAPASSERT(IsListEmpty(&(H)->FreeLists[ (FB)->Size ])); \
+ (H)->u.FreeListsInUseBytes[ _Index_ ] ^= _Bit_; \
+}
+
+#define RtlpInsertFreeBlockDirect( H, FB, SIZE ) \
+ { \
+ PLIST_ENTRY _HEAD, _NEXT; \
+ PHEAP_FREE_ENTRY _FB1; \
+ \
+ HEAPASSERT((FB)->Size == (SIZE)); \
+ (FB)->Flags &= ~(HEAP_ENTRY_FILL_PATTERN | \
+ HEAP_ENTRY_EXTRA_PRESENT | \
+ HEAP_ENTRY_BUSY \
+ ); \
+ if ((H)->Flags & HEAP_FREE_CHECKING_ENABLED) { \
+ RtlFillMemoryUlong( (PCHAR)((FB) + 1), \
+ ((ULONG)(SIZE) << HEAP_GRANULARITY_SHIFT) - \
+ sizeof( *(FB) ), \
+ FREE_HEAP_FILL \
+ ); \
+ (FB)->Flags |= HEAP_ENTRY_FILL_PATTERN; \
+ } \
+ \
+ if ((SIZE) < HEAP_MAXIMUM_FREELISTS) { \
+ _HEAD = &(H)->FreeLists[ (SIZE) ]; \
+ if (IsListEmpty(_HEAD)) { \
+ SET_FREELIST_BIT( H, FB ); \
+ } \
+ } else { \
+ _HEAD = &(H)->FreeLists[ 0 ]; \
+ _NEXT = _HEAD->Flink; \
+ while (_HEAD != _NEXT) { \
+ _FB1 = CONTAINING_RECORD( _NEXT, HEAP_FREE_ENTRY, FreeList ); \
+ if ((SIZE) <= _FB1->Size) { \
+ break; \
+ } \
+ else { \
+ _NEXT = _NEXT->Flink; \
+ } \
+ } \
+ \
+ _HEAD = _NEXT; \
+ } \
+ \
+ HeapInternalTrace( (H), ((H)->TraceBuffer, HEAP_TRACE_INSERT_FREE_BLOCK, 3, (FB), *(PULONG)(FB), *((PULONG)(FB)+1)) ); \
+ InsertTailList( _HEAD, &(FB)->FreeList ); \
+ }
+
+//
+// This version of RtlpInsertFreeBlockDirect does no filling.
+//
+#define RtlpFastInsertFreeBlockDirect( H, FB, SIZE ) \
+{ \
+ if ((SIZE) < HEAP_MAXIMUM_FREELISTS) { \
+ RtlpFastInsertDedicatedFreeBlockDirect( H, FB, SIZE ); \
+ } else { \
+ RtlpFastInsertNonDedicatedFreeBlockDirect( H, FB, SIZE ); \
+ } \
+}
+
+//
+// This version of RtlpInsertFreeBlockDirect only works for dedicated free lists
+// and doesn't do any filling.
+//
+#define RtlpFastInsertDedicatedFreeBlockDirect( H, FB, SIZE ) \
+{ \
+ PLIST_ENTRY _HEAD; \
+ \
+ HEAPASSERT((FB)->Size == (SIZE)); \
+ if (!((FB)->Flags & HEAP_ENTRY_LAST_ENTRY)) { \
+ HEAPASSERT(((PHEAP_ENTRY)(FB) + (SIZE))->PreviousSize == (SIZE)); \
+ } \
+ (FB)->Flags &= HEAP_ENTRY_LAST_ENTRY; \
+ \
+ _HEAD = &(H)->FreeLists[ (SIZE) ]; \
+ if (IsListEmpty(_HEAD)) { \
+ SET_FREELIST_BIT( H, FB ); \
+ } \
+ InsertTailList( _HEAD, &(FB)->FreeList ); \
+}
+
+//
+// This version of RtlpInsertFreeBlockDirect only works for nondedicated free lists
+// and doesn't do any filling.
+//
+#define RtlpFastInsertNonDedicatedFreeBlockDirect( H, FB, SIZE ) \
+{ \
+ PLIST_ENTRY _HEAD, _NEXT; \
+ PHEAP_FREE_ENTRY _FB1; \
+ \
+ HEAPASSERT((FB)->Size == (SIZE)); \
+ if (!((FB)->Flags & HEAP_ENTRY_LAST_ENTRY)) { \
+ HEAPASSERT(((PHEAP_ENTRY)(FB) + (SIZE))->PreviousSize == (SIZE)); \
+ } \
+ (FB)->Flags &= (HEAP_ENTRY_LAST_ENTRY); \
+ \
+ _HEAD = &(H)->FreeLists[ 0 ]; \
+ _NEXT = _HEAD->Flink; \
+ while (_HEAD != _NEXT) { \
+ _FB1 = CONTAINING_RECORD( _NEXT, HEAP_FREE_ENTRY, FreeList ); \
+ if ((SIZE) <= _FB1->Size) { \
+ break; \
+ } else { \
+ _NEXT = _NEXT->Flink; \
+ } \
+ } \
+ \
+ InsertTailList( _NEXT, &(FB)->FreeList ); \
+}
+
+#define RtlpRemoveFreeBlock( H, FB ) \
+ { \
+ HeapInternalTrace( (H), ((H)->TraceBuffer, HEAP_TRACE_UNLINK_FREE_BLOCK, 3, (FB), *(PULONG)(FB), *((PULONG)(FB)+1)) ); \
+ \
+ RtlpFastRemoveFreeBlock( H, FB ) \
+ if ((FB)->Flags & HEAP_ENTRY_FILL_PATTERN) { \
+ ULONG cb, cbEqual; \
+ PVOID p; \
+ \
+ cb = ((FB)->Size-2) << HEAP_GRANULARITY_SHIFT; \
+ if ((FB)->Flags & HEAP_ENTRY_EXTRA_PRESENT && \
+ cb > sizeof( HEAP_FREE_ENTRY_EXTRA ) \
+ ) { \
+ cb -= sizeof( HEAP_FREE_ENTRY_EXTRA ); \
+ } \
+ cbEqual = RtlCompareMemoryUlong( (PCHAR)((FB) + 1), cb, FREE_HEAP_FILL ); \
+ if (cbEqual != cb) { \
+ HeapDebugPrint( ( "HEAP: Free Heap block %lx modified at %lx after it was freed\n", \
+ (FB), \
+ (PCHAR)((FB) + 1) + cbEqual \
+ ) ); \
+ HeapDebugBreak( (FB) ); \
+ } \
+ } \
+ }
+
+//
+// This version of RtlpRemoveFreeBlock does no filling.
+//
+#define RtlpFastRemoveFreeBlock( H, FB ) \
+{ \
+ PLIST_ENTRY _EX_Blink; \
+ PLIST_ENTRY _EX_Flink; \
+ \
+ _EX_Flink = (FB)->FreeList.Flink; \
+ _EX_Blink = (FB)->FreeList.Blink; \
+ _EX_Blink->Flink = _EX_Flink; \
+ _EX_Flink->Blink = _EX_Blink; \
+ if ((_EX_Flink == _EX_Blink) && \
+ ((FB)->Size < HEAP_MAXIMUM_FREELISTS)) { \
+ CLEAR_FREELIST_BIT( H, FB ); \
+ } \
+}
+
+//
+// This version of RtlpRemoveFreeBlock only works for dedicated free lists
+// (where we know that (FB)->Mask != 0) and doesn't do any filling.
+//
+#define RtlpFastRemoveDedicatedFreeBlock( H, FB ) \
+{ \
+ PLIST_ENTRY _EX_Blink; \
+ PLIST_ENTRY _EX_Flink; \
+ \
+ _EX_Flink = (FB)->FreeList.Flink; \
+ _EX_Blink = (FB)->FreeList.Blink; \
+ _EX_Blink->Flink = _EX_Flink; \
+ _EX_Flink->Blink = _EX_Blink; \
+ if (_EX_Flink == _EX_Blink) { \
+ CLEAR_FREELIST_BIT( H, FB ); \
+ } \
+}
+
+//
+// This version of RtlpRemoveFreeBlock only works for dedicated free lists
+// (where we know that (FB)->Mask == 0) and doesn't do any filling.
+//
+#define RtlpFastRemoveNonDedicatedFreeBlock( H, FB ) \
+ RemoveEntryList(&(FB)->FreeList)
+
+#if DBG
+#define IS_HEAP_TAGGING_ENABLED() (TRUE)
+#else
+#define IS_HEAP_TAGGING_ENABLED() (NtGlobalFlag & FLG_HEAP_ENABLE_TAGGING)
+#endif
+
+PWSTR
+RtlpGetTagName(
+ PHEAP Heap,
+ USHORT TagIndex
+ );
+
+typedef enum _HEAP_TAG_ACTION { // ORDER IS IMPORTANT HERE...SEE RtlpUpdateTagEntry sources
+ AllocationAction,
+ VirtualAllocationAction,
+ FreeAction,
+ VirtualFreeAction,
+ ReAllocationAction,
+ VirtualReAllocationAction
+} HEAP_TAG_ACTION;
+
+USHORT
+RtlpUpdateTagEntry(
+ PHEAP Heap,
+ USHORT TagIndex,
+ ULONG OldSize, // Only valid for ReAllocation and Free actions
+ ULONG NewSize, // Only valid for ReAllocation and Allocation actions
+ HEAP_TAG_ACTION Action
+ );
+
+VOID
+RtlpDestroyTags(
+ PHEAP Heap
+ );
+
+ULONG
+RtlpGetSizeOfBigBlock(
+ IN PHEAP_ENTRY BusyBlock
+ );
+
+PHEAP_ENTRY_EXTRA
+RtlpGetExtraStuffPointer(
+ PHEAP_ENTRY BusyBlock
+ );
+
+BOOLEAN
+RtlpCheckBusyBlockTail(
+ IN PHEAP_ENTRY BusyBlock
+ );
+
+#if !defined(NTOS_KERNEL_RUNTIME)
+BOOLEAN
+RtlpCheckHeapSignature(
+ IN PHEAP Heap,
+ IN PCHAR Caller
+ );
+
+#define HEAP_VALIDATE_SIGNATURE( _h_, _r_ ) \
+ (BOOLEAN)(((_h_)->Signature == HEAP_SIGNATURE) ? TRUE : RtlpCheckHeapSignature( (_h_), (_r_)))
+
+
+BOOLEAN
+RtlpValidateHeapEntry(
+ IN PHEAP Heap,
+ IN PHEAP_ENTRY BusyBlock,
+ IN PCHAR Reason
+ );
+
+BOOLEAN
+RtlpValidateHeap(
+ IN PHEAP Heap,
+ IN BOOLEAN AlwaysValidate
+ );
+#endif // !defined(NTOS_KERNEL_RUNTIME)
+
+#endif // _RTL_HEAP_PRIVATE_
diff --git a/private/ntos/rtl/i386/context.c b/private/ntos/rtl/i386/context.c
new file mode 100644
index 000000000..44ab2b358
--- /dev/null
+++ b/private/ntos/rtl/i386/context.c
@@ -0,0 +1,263 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ context.c
+
+Abstract:
+
+ This module implements user-mode callable context manipulation routines.
+ The interfaces exported from this module are portable, but they must
+ be re-implemented for each architecture.
+
+Author:
+
+ Mark Lucovsky (markl) 20-Jun-1989
+
+Revision History:
+
+ Bryan Willman (bryanwi) 8-Mar-90
+
+ Ported to the 80386
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlInitializeContext)
+#pragma alloc_text(PAGE,RtlRemoteCall)
+#endif
+
+
+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 context buffer to be initialized by this routine.
+
+ 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.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ Context->Eax = 0L;
+ Context->Ebx = 1L;
+ Context->Ecx = 2L;
+ Context->Edx = 3L;
+ Context->Esi = 4L;
+ Context->Edi = 5L;
+ Context->Ebp = 0L;
+
+ Context->SegGs = 0;
+ Context->SegFs = KGDT_R3_TEB;
+ Context->SegEs = KGDT_R3_DATA;
+ Context->SegDs = KGDT_R3_DATA;
+ Context->SegSs = KGDT_R3_DATA;
+ Context->SegCs = KGDT_R3_CODE;
+
+ Context->EFlags = 0x200L; // force interrupts on, clear all else.
+
+ //
+ // Even though these are optional, they are used as is, since NULL
+ // is what these would have been initialized to anyway
+ //
+
+ Context->Esp = (ULONG) InitialSp;
+ Context->Eip = (ULONG) InitialPc;
+
+ //
+ // add code to check alignment and raise exception...
+ //
+
+ Context->ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS;
+
+ //
+ // Set the initial context of the thread in a machine specific way.
+ // ie, pass the initial parameter to the start address
+ //
+
+ Context->Esp -= sizeof(Parameter);
+ ZwWriteVirtualMemory(Process,
+ (PVOID)Context->Esp,
+ (PVOID)&Parameter,
+ sizeof(Parameter),
+ NULL);
+ Context->Esp -= sizeof(Parameter); // Reserve room for ret address
+
+
+}
+
+
+
+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, using
+ NtGetContext and NtSetContext. Parameters are passed to the
+ target procedure via its stack.
+
+Arguments:
+
+ Process - Handle of the target process
+
+ Thread - Handle of the target thread within that process
+
+ CallSite - Address of the procedure to call in the target process.
+
+ ArgumentCount - Number of 32 bit parameters to pass to the target
+ procedure.
+
+ Arguments - Pointer to the array of 32 bit parameters to pass.
+
+ PassContext - TRUE if an additional parameter is to be passed that
+ points to a context record.
+
+ AlreadySuspended - TRUE if the target thread is already in a suspended
+ or waiting state.
+
+Return Value:
+
+ Status - Status value
+
+--*/
+
+{
+ NTSTATUS Status;
+ CONTEXT Context;
+ ULONG NewSp;
+ ULONG ArgumentsCopy[5];
+
+ RTL_PAGED_CODE();
+
+ if (ArgumentCount > 4)
+ return STATUS_INVALID_PARAMETER;
+
+ //
+ // If necessary, suspend the guy before with we mess with his stack.
+ //
+ if (!AlreadySuspended) {
+ Status = NtSuspendThread( Thread, NULL );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+ }
+
+
+ //
+ // Get the context record for the target thread.
+ //
+
+ Context.ContextFlags = CONTEXT_FULL;
+ Status = NtGetContextThread( Thread, &Context );
+ if (!NT_SUCCESS( Status )) {
+ if (!AlreadySuspended) {
+ NtResumeThread( Thread, NULL );
+ }
+ return( Status );
+ }
+
+
+ //
+ // Pass all parameters on the stack, regardless of whether a
+ // a context record is passed.
+ //
+
+ //
+ // Put Context Record on stack first, so it is above other args.
+ //
+ NewSp = Context.Esp;
+ if (PassContext) {
+ NewSp -= sizeof( CONTEXT );
+ Status = NtWriteVirtualMemory( Process,
+ (PVOID)NewSp,
+ &Context,
+ sizeof( CONTEXT ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (!AlreadySuspended) {
+ NtResumeThread( Thread, NULL );
+ }
+ return( Status );
+ }
+ ArgumentsCopy[0] = NewSp; // pass pointer to context
+ RtlMoveMemory(&(ArgumentsCopy[1]),Arguments,ArgumentCount*sizeof( ULONG ));
+ ArgumentCount++;
+ }
+ else {
+ RtlMoveMemory(ArgumentsCopy,Arguments,ArgumentCount*sizeof( ULONG ));
+ }
+
+ //
+ // Copy the arguments onto the target stack
+ //
+ if (ArgumentCount) {
+ NewSp -= ArgumentCount * sizeof( ULONG );
+ Status = NtWriteVirtualMemory( Process,
+ (PVOID)NewSp,
+ ArgumentsCopy,
+ ArgumentCount * sizeof( ULONG ),
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (!AlreadySuspended) {
+ NtResumeThread( Thread, NULL );
+ }
+ return( Status );
+ }
+ }
+
+ //
+ // Set the address of the target code into Eip, the new target stack
+ // into Esp, and reload context to make it happen.
+ //
+ Context.Esp = NewSp;
+ Context.Eip = (ULONG)CallSite;
+ Status = NtSetContextThread( Thread, &Context );
+ if (!AlreadySuspended) {
+ NtResumeThread( Thread, NULL );
+ }
+
+ return( Status );
+}
diff --git a/private/ntos/rtl/i386/debug2.asm b/private/ntos/rtl/i386/debug2.asm
new file mode 100644
index 000000000..fcf84484b
--- /dev/null
+++ b/private/ntos/rtl/i386/debug2.asm
@@ -0,0 +1,65 @@
+ title "Debug Support Functions"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; debug.s
+;
+; Abstract:
+;
+; This module implements functions to support debugging NT.
+;
+; Author:
+;
+; Steven R. Wood (stevewo) 3-Aug-1989
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+; 11 April 90 (and before) bryanwi
+; Ported to 386, 386 specific support added.
+;
+; 2 Aug. 90 (tomp)
+; Added _DbgUnLoadImageSymbols routine.
+;
+;--
+.386p
+
+
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+_TEXT SEGMENT PUBLIC DWORD 'CODE'
+ASSUME DS:FLAT, ES:FLAT, FS:NOTHING, GS:NOTHING, SS:NOTHING
+
+cPublicProc _DbgBreakPoint ,0
+cPublicFpo 0,0
+ int 3
+ stdRET _DbgBreakPoint
+stdENDP _DbgBreakPoint
+
+cPublicProc _DbgUserBreakPoint ,0
+cPublicFpo 0,0
+ int 3
+ stdRET _DbgUserBreakPoint
+stdENDP _DbgUserBreakPoint
+
+cPublicProc _DbgBreakPointWithStatus,1
+cPublicFpo 1,0
+ mov eax,[esp+4]
+ public _RtlpBreakWithStatusInstruction@0
+_RtlpBreakWithStatusInstruction@0:
+ int 3
+ stdRET _DbgBreakPointWithStatus
+stdENDP _DbgBreakPointWithStatus
+
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/debug3.c b/private/ntos/rtl/i386/debug3.c
new file mode 100644
index 000000000..90944833d
--- /dev/null
+++ b/private/ntos/rtl/i386/debug3.c
@@ -0,0 +1,121 @@
+//++
+//
+// Copyright (c) 1990 Microsoft Corporation
+//
+// Module Name:
+//
+// debug3.c
+//
+// Abstract:
+//
+// This module implements architecture specific functions to support debugging NT.
+//
+// Author:
+//
+// Steven R. Wood (stevewo) 3-Aug-1989
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "stdarg.h"
+#include "stdio.h"
+#include "string.h"
+#include "ntrtlp.h"
+
+//
+// Prototype for local procedure
+//
+
+NTSTATUS
+DebugService(
+ ULONG ServiceClass,
+ PVOID Arg1,
+ PVOID Arg2
+ );
+
+VOID _fptrap() {};
+
+NTSTATUS
+DebugPrint(
+ IN PSTRING Output
+ )
+{
+ return DebugService( BREAKPOINT_PRINT, Output, 0 );
+}
+
+
+ULONG
+DebugPrompt(
+ IN PSTRING Output,
+ IN PSTRING Input
+ )
+{
+ return DebugService( BREAKPOINT_PROMPT, Output, Input );
+}
+
+VOID
+DebugLoadImageSymbols(
+ IN PSTRING FileName,
+ IN PKD_SYMBOLS_INFO SymbolInfo
+ )
+{
+ DebugService( BREAKPOINT_LOAD_SYMBOLS, FileName, SymbolInfo );
+}
+
+
+VOID
+DebugUnLoadImageSymbols(
+ IN PSTRING FileName,
+ IN PKD_SYMBOLS_INFO SymbolInfo
+ )
+{
+ DebugService( BREAKPOINT_UNLOAD_SYMBOLS, FileName, SymbolInfo );
+}
+
+NTSTATUS
+DebugService(
+ ULONG ServiceClass,
+ PVOID Arg1,
+ PVOID Arg2
+ )
+
+//++
+//
+// Routine Description:
+//
+// Allocate an ExceptionRecord, fill in data to allow exception
+// dispatch code to do the right thing with the service, and
+// call RtlRaiseException (NOT ExRaiseException!!!).
+//
+// Arguments:
+// ServiceClass - which call is to be performed
+// Arg1 - generic first argument
+// Arg2 - generic second argument
+//
+// Returns:
+// Whatever the exception returns in eax
+//
+//--
+
+{
+ NTSTATUS RetValue;
+
+ _asm {
+ mov eax, ServiceClass
+ mov ecx, Arg1
+ mov edx, Arg2
+
+ int 2dh ; Raise exception
+ int 3 ; DO NOT REMOVE (See KiDebugService)
+
+ mov RetValue, eax
+
+ }
+
+ return RetValue;
+}
diff --git a/private/ntos/rtl/i386/divlarge.c b/private/ntos/rtl/i386/divlarge.c
new file mode 100644
index 000000000..12cf1dce0
--- /dev/null
+++ b/private/ntos/rtl/i386/divlarge.c
@@ -0,0 +1,124 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ largediv.c
+
+Abstract:
+
+ This module implements the NT runtime library large integer divide
+ routines.
+
+ N.B. These routines use a one bit at a time algorithm and is slow.
+ They should be used only when absolutely necessary.
+
+Author:
+
+ David N. Cutler 10-Aug-1992
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+LARGE_INTEGER
+RtlLargeIntegerDivide (
+ IN LARGE_INTEGER Dividend,
+ IN LARGE_INTEGER Divisor,
+ OUT PLARGE_INTEGER Remainder OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine divides an unsigned 64-bit dividend by an unsigned 64-bit
+ divisor and returns a 64-bit quotient, and optionally a 64-bit remainder.
+
+Arguments:
+
+ Dividend - Supplies the 64-bit dividend for the divide operation.
+
+ Divisor - Supplies the 64-bit divisor for the divide operation.
+
+ Remainder - Supplies an optional pointer to a variable which receives
+ the remainder
+
+Return Value:
+
+ The 64-bit quotient is returned as the function value.
+
+--*/
+
+{
+
+ ULONG Index = 64;
+ LARGE_INTEGER Partial = {0, 0};
+ LARGE_INTEGER Quotient;
+
+#ifndef BLDR_KERNEL_RUNTIME
+ //
+ // Check for divide by zero
+ //
+
+ if (!(Divisor.LowPart | Divisor.HighPart)) {
+ RtlRaiseStatus (STATUS_INTEGER_DIVIDE_BY_ZERO);
+ }
+#endif
+
+ //
+ // Loop through the dividend bits and compute the quotient and remainder.
+ //
+
+ Quotient = Dividend;
+ do {
+
+ //
+ // Shift the next dividend bit into the parital remainder and shift
+ // the partial quotient (dividend) left one bit.
+ //
+
+ Partial.HighPart = (Partial.HighPart << 1) | (Partial.LowPart >> 31);
+ Partial.LowPart = (Partial.LowPart << 1) | ((ULONG)Quotient.HighPart >> 31);
+ Quotient.HighPart = (Quotient.HighPart << 1) | (Quotient.LowPart >> 31);
+ Quotient.LowPart <<= 1;
+
+ //
+ // If the partial remainder is greater than or equal to the divisor,
+ // then subtract the divisor from the partial remainder and insert a
+ // one bit into the quotient.
+ //
+
+ if (((ULONG)Partial.HighPart > (ULONG)Divisor.HighPart) ||
+ ((Partial.HighPart == Divisor.HighPart) &&
+ (Partial.LowPart >= Divisor.LowPart))) {
+
+ Quotient.LowPart |= 1;
+ Partial.HighPart -= Divisor.HighPart;
+ if (Partial.LowPart < Divisor.LowPart) {
+ Partial.HighPart -= 1;
+ }
+
+ Partial.LowPart -= Divisor.LowPart;
+ }
+
+ Index -= 1;
+ } while (Index > 0);
+
+ //
+ // If the remainder is requested, then return the 64-bit remainder.
+ //
+
+ if (ARGUMENT_PRESENT(Remainder)) {
+ *Remainder = Partial;
+ }
+
+ //
+ // Return the 64-bit quotient.
+ //
+
+ return Quotient;
+}
diff --git a/private/ntos/rtl/i386/exdsptch.c b/private/ntos/rtl/i386/exdsptch.c
new file mode 100644
index 000000000..2efec6aca
--- /dev/null
+++ b/private/ntos/rtl/i386/exdsptch.c
@@ -0,0 +1,535 @@
+/*++
+
+Copyright (c) 1989 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) 13-Aug-1989
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ 10 april 90 bryanwi
+
+ Port to the 386.
+
+--*/
+
+#include "ntrtlp.h"
+
+
+//
+// Dispatcher context structure definition.
+//
+
+typedef struct _DISPATCHER_CONTEXT {
+ PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
+ } DISPATCHER_CONTEXT;
+
+//
+// Execute handler for exception function prototype.
+//
+
+EXCEPTION_DISPOSITION
+RtlpExecuteHandlerForException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PVOID DispatcherContext,
+ IN PEXCEPTION_ROUTINE ExceptionRoutine
+ );
+
+//
+// Execute handler for unwind function prototype.
+//
+
+EXCEPTION_DISPOSITION
+RtlpExecuteHandlerForUnwind (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PVOID EstablisherFrame,
+ IN OUT PCONTEXT ContextRecord,
+ IN OUT PVOID DispatcherContext,
+ IN PEXCEPTION_ROUTINE ExceptionRoutine
+ );
+
+
+
+BOOLEAN
+RtlDispatchException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to dispatch an exception to a call 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.
+
+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.
+
+--*/
+
+{
+
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
+ PEXCEPTION_REGISTRATION_RECORD NestedRegistration;
+ ULONG HighAddress;
+ ULONG HighLimit;
+ ULONG LowLimit;
+ EXCEPTION_RECORD ExceptionRecord1;
+
+ //
+ // Get current stack limits.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+
+ //
+ // 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 handler the exception.
+ //
+
+ RegistrationPointer = RtlpGetRegistrationHead();
+ NestedRegistration = 0;
+ while (RegistrationPointer != EXCEPTION_CHAIN_END) {
+
+ //
+ // If the call frame is not within the specified stack limits or the
+ // call frame is unaligned, then set the stack invalid flag in the
+ // exception record and return FALSE. Else check to determine if the
+ // frame has an exception handler.
+ //
+
+ HighAddress = (ULONG)RegistrationPointer +
+ sizeof(EXCEPTION_REGISTRATION_RECORD);
+
+ if ( ((ULONG)RegistrationPointer < LowLimit) ||
+ (HighAddress > HighLimit) ||
+ (((ULONG)RegistrationPointer & 0x3) != 0) ) {
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_STACK_INVALID;
+ return FALSE;
+ } else {
+
+#if !defined(WX86_i386)
+
+ ULONG Index;
+ //
+ // 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.
+ //
+
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ Index = RtlpLogExceptionHandler(
+ ExceptionRecord,
+ ContextRecord,
+ 0,
+ (PULONG)RegistrationPointer,
+ 4 * sizeof(ULONG));
+ // can't use sizeof(EXCEPTION_REGISTRATION_RECORD
+ // because we need the 2 dwords above it.
+ }
+#endif
+
+ Disposition = RtlpExecuteHandlerForException(
+ ExceptionRecord,
+ (PVOID)RegistrationPointer,
+ ContextRecord,
+ (PVOID)&DispatcherContext,
+ (PEXCEPTION_ROUTINE)RegistrationPointer->Handler);
+
+#if !defined(WX86_i386)
+ if (NtGlobalFlag & FLG_ENABLE_EXCEPTION_LOGGING) {
+ RtlpLogLastExceptionDisposition(Index, Disposition);
+ }
+#endif
+
+
+ //
+ // If the current scan is within a nested context and the frame
+ // just examined is the end of the context region, then clear
+ // the nested context frame and the nested exception in the
+ // exception flags.
+ //
+
+ if (NestedRegistration == RegistrationPointer) {
+ ExceptionRecord->ExceptionFlags &= (~EXCEPTION_NESTED_CALL);
+ NestedRegistration = 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
+ // TRUE.
+ //
+
+ case ExceptionContinueExecution :
+ if ((ExceptionRecord->ExceptionFlags &
+ EXCEPTION_NONCONTINUABLE) != 0) {
+ ExceptionRecord1.ExceptionCode =
+ STATUS_NONCONTINUABLE_EXCEPTION;
+ ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ ExceptionRecord1.ExceptionRecord = ExceptionRecord;
+ ExceptionRecord1.NumberParameters = 0;
+ RtlRaiseException(&ExceptionRecord1);
+ } 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
+ // nested exception in the exception flags.
+ //
+
+ case ExceptionNestedException :
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_NESTED_CALL;
+ if (DispatcherContext.RegistrationPointer > NestedRegistration) {
+ NestedRegistration = DispatcherContext.RegistrationPointer;
+ }
+ break;
+
+ //
+ // All other disposition values are invalid. Raise
+ // invalid disposition exception.
+ //
+
+ default :
+ ExceptionRecord1.ExceptionCode = STATUS_INVALID_DISPOSITION;
+ ExceptionRecord1.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ ExceptionRecord1.ExceptionRecord = ExceptionRecord;
+ ExceptionRecord1.NumberParameters = 0;
+ RtlRaiseException(&ExceptionRecord1);
+ break;
+ }
+
+ //
+ // If chain goes in wrong direction or loops, report an
+ // invalid exception stack, otherwise go on to the next one.
+ //
+
+ RegistrationPointer = RegistrationPointer->Next;
+ }
+ }
+ return FALSE;
+}
+
+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
+ walk through the procedure call frames is then performed to find the target
+ of the unwind operation.
+
+ N.B. The captured context passed to unwinding handlers will not be
+ a completely accurate context set for the 386. This is because
+ there isn't a standard stack frame in which registers are stored.
+
+ Only the integer registers are affected. The segement and
+ control registers (ebp, esp) will have correct values for
+ the flat 32 bit environment.
+
+ N.B. If you change the number of arguments, make sure you change the
+ adjustment of ESP after the call to RtlpCaptureContext (for
+ STDCALL calling convention)
+
+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.
+
+--*/
+
+{
+ PCONTEXT ContextRecord;
+ CONTEXT ContextRecord1;
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
+ PEXCEPTION_REGISTRATION_RECORD PriorPointer;
+ ULONG HighAddress;
+ ULONG HighLimit;
+ ULONG LowLimit;
+ EXCEPTION_RECORD ExceptionRecord1;
+ EXCEPTION_RECORD ExceptionRecord2;
+
+ //
+ // Get current stack limits.
+ //
+
+ RtlpGetStackLimits(&LowLimit, &HighLimit);
+
+ //
+ // 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.ExceptionFlags = 0;
+ ExceptionRecord1.ExceptionRecord = NULL;
+ ExceptionRecord1.ExceptionAddress = RtlpGetReturnAddress();
+ ExceptionRecord1.NumberParameters = 0;
+ }
+
+ //
+ // If the target frame of the unwind is specified, then set EXCEPTION_UNWINDING
+ // flag in the exception flags. Otherwise set both EXCEPTION_EXIT_UNWIND and
+ // EXCEPTION_UNWINDING flags in the exception flags.
+ //
+
+ if (ARGUMENT_PRESENT(TargetFrame) == TRUE) {
+ ExceptionRecord->ExceptionFlags |= EXCEPTION_UNWINDING;
+ } else {
+ ExceptionRecord->ExceptionFlags |= (EXCEPTION_UNWINDING |
+ EXCEPTION_EXIT_UNWIND);
+ }
+
+ //
+ // Capture the context.
+ //
+
+ ContextRecord = &ContextRecord1;
+ ContextRecord1.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS;
+ RtlpCaptureContext(ContextRecord);
+
+#ifdef STD_CALL
+ //
+ // Adjust captured context to pop our arguments off the stack
+ //
+ ContextRecord->Esp += sizeof(TargetFrame) +
+ sizeof(TargetIp) +
+ sizeof(ExceptionRecord) +
+ sizeof(ReturnValue);
+#endif
+ ContextRecord->Eax = (ULONG)ReturnValue;
+
+ //
+ // Scan backward through the call frame hierarchy, calling exception
+ // handlers as they are encountered, until the target frame of the unwind
+ // is reached.
+ //
+
+ RegistrationPointer = RtlpGetRegistrationHead();
+ while (RegistrationPointer != EXCEPTION_CHAIN_END) {
+
+ //
+ // If this is the target of the unwind, then continue execution
+ // by calling the continue system service.
+ //
+
+ if ((ULONG)RegistrationPointer == (ULONG)TargetFrame) {
+ ZwContinue(ContextRecord, FALSE);
+
+ //
+ // If the target frame is lower in the stack than the current frame,
+ // then raise STATUS_INVALID_UNWIND exception.
+ //
+
+ } else if ( (ARGUMENT_PRESENT(TargetFrame) == TRUE) &&
+ ((ULONG)TargetFrame < (ULONG)RegistrationPointer) ) {
+ ExceptionRecord2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET;
+ ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ ExceptionRecord2.ExceptionRecord = ExceptionRecord;
+ ExceptionRecord2.NumberParameters = 0;
+ RtlRaiseException(&ExceptionRecord2);
+ }
+
+ //
+ // If the call frame is not within the specified stack limits or the
+ // call frame is unaligned, then raise the exception STATUS_BAD_STACK.
+ // Else restore the state from the specified frame to the context
+ // record.
+ //
+
+ HighAddress = (ULONG)RegistrationPointer +
+ sizeof(EXCEPTION_REGISTRATION_RECORD);
+
+ if ( ((ULONG)RegistrationPointer < LowLimit) ||
+ (HighAddress > HighLimit) ||
+ (((ULONG)RegistrationPointer & 0x3) != 0) ) {
+ ExceptionRecord2.ExceptionCode = STATUS_BAD_STACK;
+ ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ ExceptionRecord2.ExceptionRecord = ExceptionRecord;
+ ExceptionRecord2.NumberParameters = 0;
+ RtlRaiseException(&ExceptionRecord2);
+ } else {
+
+ //
+ // 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 collided unwind is encountered.
+ //
+
+ Disposition = RtlpExecuteHandlerForUnwind(
+ ExceptionRecord,
+ (PVOID)RegistrationPointer,
+ ContextRecord,
+ (PVOID)&DispatcherContext,
+ RegistrationPointer->Handler);
+
+ //
+ // Case on the handler disposition.
+ //
+
+ switch (Disposition) {
+
+ //
+ // The disposition is to continue the search. Get next
+ // frame address and continue the search.
+ //
+
+ case ExceptionContinueSearch :
+ break;
+
+ //
+ // The disposition is colided unwind. Maximize the target
+ // of the unwind and change the context record pointer.
+ //
+
+ case ExceptionCollidedUnwind :
+
+ //
+ // Pick up the registration pointer that was active at
+ // the time of the unwind, and simply continue.
+ //
+
+ RegistrationPointer = DispatcherContext.RegistrationPointer;
+ break;
+
+
+ //
+ // All other disposition values are invalid. Raise
+ // invalid disposition exception.
+ //
+
+ default :
+ ExceptionRecord2.ExceptionCode = STATUS_INVALID_DISPOSITION;
+ ExceptionRecord2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ ExceptionRecord2.ExceptionRecord = ExceptionRecord;
+ ExceptionRecord2.NumberParameters = 0;
+ RtlRaiseException(&ExceptionRecord2);
+ break;
+ }
+
+ //
+ // Step to next registration record
+ //
+
+ PriorPointer = RegistrationPointer;
+ RegistrationPointer = RegistrationPointer->Next;
+
+ //
+ // Unlink the unwind handler, since it's been called.
+ //
+
+ RtlpUnlinkHandler(PriorPointer);
+
+ //
+ // If chain goes in wrong direction or loops, raise an
+ // exception.
+ //
+
+ }
+ }
+
+ if (TargetFrame == EXCEPTION_CHAIN_END) {
+
+ //
+ // Caller simply wants to unwind all exception records.
+ // This differs from an exit_unwind in that no "exit" is desired.
+ // Do a normal continue, since we've effectively found the
+ // "target" the caller wanted.
+ //
+
+ ZwContinue(ContextRecord, FALSE);
+
+ } else {
+
+ //
+ // Either (1) a real exit unwind was performed, or (2) the
+ // specified TargetFrame is not present in the exception handler
+ // list. In either case, give debugger and subsystem a chance
+ // to see the unwind.
+ //
+
+ ZwRaiseException(ExceptionRecord, ContextRecord, FALSE);
+
+ }
+ return;
+}
diff --git a/private/ntos/rtl/i386/exsup.asm b/private/ntos/rtl/i386/exsup.asm
new file mode 100644
index 000000000..cfa02a6fb
--- /dev/null
+++ b/private/ntos/rtl/i386/exsup.asm
@@ -0,0 +1,15 @@
+ title "Public Constant (__except_list) Definition"
+
+.386p
+.xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+include mac386.inc
+.list
+
+COMM __setjmpexused:dword
+
+public __except_list
+__except_list equ 0
+
+END
diff --git a/private/ntos/rtl/i386/forceres.asm b/private/ntos/rtl/i386/forceres.asm
new file mode 100644
index 000000000..cd941803a
--- /dev/null
+++ b/private/ntos/rtl/i386/forceres.asm
@@ -0,0 +1,23 @@
+.386p
+
+; forceres.asm - generate publics to force resolution of symbols we
+; don't want to deal with yet.
+;
+
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ public __fltused
+
+foo proc near ; make the assembler shut up
+
+__fltused:
+ int 3
+ ret
+
+foo endp
+
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/halvprnt.c b/private/ntos/rtl/i386/halvprnt.c
new file mode 100644
index 000000000..9179241c9
--- /dev/null
+++ b/private/ntos/rtl/i386/halvprnt.c
@@ -0,0 +1,407 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ halvprnt.c
+
+Author:
+
+ John Vert (jvert) 13-Aug-1991
+ based on TomP's video.c
+
+Abstract:
+
+ Video support routines.
+
+ The vprintf function here outputs to the console via HalDisplayString.
+ All the global variables have been made local, so multiple processors
+ can execute it simultaneously. If this is the case, it relies on
+ HalDisplayString to sort the output to avoid interleaving the text from
+ the processors.
+
+History:
+
+--*/
+
+#include <ntos.h>
+
+typedef unsigned char BYTE, *PBYTE;
+
+
+//
+// Internal routines
+//
+
+static
+int
+xatoi(char c);
+
+static
+int
+fields(
+ char *cp,
+ int *zerofill,
+ int *fieldwidth
+ );
+
+static
+VOID
+putx(
+ ULONG x,
+ int digits,
+ int zerofill,
+ int *fieldwidth
+ );
+
+static
+VOID
+puti(
+ LONG i,
+ int digits,
+ int zerofill,
+ int *fieldwidth
+ );
+
+static
+VOID
+putu(
+ ULONG u,
+ int digits,
+ int zerofill,
+ int *fieldwidth
+ );
+
+static
+VOID
+putc(
+ CHAR c
+ );
+
+
+/*++
+
+Name
+
+ vprintf - DbgPrint function on standard video
+
+ Currently handles
+
+
+ %i, %li - signed short, signed long (same as d)
+ %d, %ld - signed short, signed long
+ %u, %lu - unsigned short, unsigned long
+ %c, %s, %.*s - character, string
+ %Z - PSTRING data type
+ %x, %lx - unsigned print in hex, unsigned long print in hex
+ %X, %lX, %X, %X, %X, %X - same as %x and %lx
+ field widths
+ leading 0 fills
+
+ Does not do yet:
+
+ No floating point.
+
+
+--*/
+void
+vprintf(PCHAR cp,USHORT a1)
+{
+ ULONG cb;
+ USHORT b,c;
+ PBYTE ap;
+ PCHAR s;
+ PSTRING str;
+ ULONG Flags;
+ int zerofill, fieldwidth;
+
+ //
+ // Cast a pointer to the first word on the stack
+ //
+
+ ap = (PBYTE)&a1;
+
+
+ // Save flags in automatic variable on stack, turn off ints.
+
+ _asm {
+ pushfd
+ pop Flags
+ cli
+ }
+
+ //
+ // Process the argements using the descriptor string
+ //
+
+ while (b = *cp++) {
+ if (b == '%') {
+ cp += fields(cp, &zerofill, &fieldwidth);
+ c = *cp++;
+
+ switch (c) {
+ case '.':
+ if (*cp != '*' || cp[1] != 's') {
+ putc((char)b);
+ putc((char)c);
+ break;
+ }
+ cp += 2;
+ cb = *((ULONG *)ap);
+ ap += sizeof( ULONG );
+ s = *((PCHAR *)ap);
+ ap += sizeof( PCHAR );
+ if (s == NULL) {
+ s = "(null)";
+ cb = 6;
+ }
+ if (cb > 0xFFF) {
+ s = "(overflow)";
+ cb = 10;
+ }
+
+ while (cb--) {
+ if (*s) {
+ putc(*s++);
+ } else {
+ putc(' ');
+ }
+ }
+ break;
+
+ case 'i':
+ case 'd':
+ puti((long)*((int *)ap), 1, zerofill, &fieldwidth);
+ ap += sizeof(int);
+ break;
+
+ case 'S':
+ str = *((PSTRING *)ap);
+ ap += sizeof (STRING *);
+ b = str->Length;
+ s = str->Buffer;
+ if (s == NULL)
+ s = "(null)";
+ while (b--)
+ putc(*s++);
+ break;
+
+ case 's':
+ s = *((PCHAR *)ap);
+ ap += sizeof( PCHAR );
+ if (s == NULL)
+ s = "(null)";
+ while (*s)
+ putc(*s++);
+ break;
+
+ case 'c':
+ putc(*((char *)ap));
+ ap += sizeof(int);
+ break;
+
+
+ //
+ // If we cannot find the status value in the table, print it in
+ // hex.
+ //
+ case 'C':
+ case 'B':
+ //
+ // Should search bugcodes.h to display bug code
+ // symbolically. For now just show as hex
+ //
+
+ case 'X':
+ case 'x':
+ putx((ULONG)*((USHORT *)ap), 1, zerofill, &fieldwidth);
+ ap += sizeof(int);
+ break;
+
+ case 'u':
+ putu((ULONG)*((USHORT *)ap), 1, zerofill, &fieldwidth);
+ ap += sizeof(int);
+ break;
+
+ case 'l':
+ c = *cp++;
+
+ switch(c) {
+ case 'u':
+ putu(*((ULONG *)ap), 1, zerofill, &fieldwidth);
+ ap += sizeof(long);
+ break;
+
+ case 'C':
+ case 'B':
+ //
+ // Should search bugcodes.h to display bug code
+ // symbolically. For now just show as hex
+ //
+
+ case 'X':
+ case 'x':
+ putx(*((ULONG *)ap), 1, zerofill, &fieldwidth);
+ ap += sizeof(long);
+ break;
+
+ case 'i':
+ case 'd':
+ puti(*((ULONG *)ap), 1, zerofill, &fieldwidth);
+ ap += sizeof(long);
+ break;
+ } // inner switch
+ break;
+
+ default :
+ putc((char)b);
+ putc((char)c);
+ } // outer switch
+ } // if
+ else
+ putc((char)b);
+ } // while
+
+ // Restore flags from automatic variable on stack
+
+ _asm {
+ push Flags
+ popfd
+ }
+ return;
+}
+
+
+//
+// Fields computation
+//
+
+static int fields(char *cp, int *zerofill, int *fieldwidth)
+{
+ int incval = 0;
+
+ *zerofill = 0;
+ *fieldwidth = 0;
+
+ if (*cp == '0') {
+ *zerofill = 1;
+ cp++;
+ incval++;
+ }
+
+ while ((*cp >= '0') && (*cp <= '9')) {
+ *fieldwidth = (*fieldwidth * 10) + xatoi(*cp);
+ cp++;
+ incval++;
+ }
+ return incval;
+}
+
+//
+// Write a hex short to display
+//
+
+static void putx(ULONG x, int digits, int zerofill, int *fieldwidth)
+{
+ ULONG j;
+
+ if (x/16)
+ putx(x/16, digits+1, zerofill, fieldwidth);
+
+ if (*fieldwidth > digits) {
+ while (*fieldwidth > digits) {
+ if (zerofill)
+ putc('0');
+ else
+ putc(' ');
+ *fieldwidth--;
+ }
+ }
+ *fieldwidth = 0;
+
+
+ if((j=x%16) > 9)
+ putc((char)(j+'A'- 10));
+ else
+ putc((char)(j+'0'));
+
+}
+
+
+//
+// Write a short integer to display
+//
+
+static void puti(long i, int digits, int zerofill, int *fieldwidth)
+{
+ if (i<0) {
+ i = -i;
+ putc((char)'-');
+ }
+
+ if (i/10)
+ puti(i/10, digits+1, zerofill, fieldwidth);
+
+ if (*fieldwidth > digits) {
+ while (*fieldwidth > digits) {
+ if (zerofill)
+ putc('0');
+ else
+ putc(' ');
+ *fieldwidth--;
+ }
+ }
+ *fieldwidth = 0;
+
+ putc((char)((i%10)+'0'));
+}
+
+
+//
+// Write an unsigned short to display
+//
+
+static void putu(ULONG u, int digits, int zerofill, int *fieldwidth)
+{
+ if (u/10)
+ putu(u/10, digits+1, zerofill, fieldwidth);
+
+ if (*fieldwidth > digits) {
+ while (*fieldwidth > digits) {
+ if (zerofill)
+ putc('0');
+ else
+ putc(' ');
+ *fieldwidth--;
+ }
+ }
+ *fieldwidth = 0;
+
+ putc((char)((u%10)+'0'));
+}
+
+//
+// Write a character to display
+//
+
+VOID putc(
+ CHAR c
+ )
+{
+ static UCHAR OneCharacter[2];
+
+ OneCharacter[1] = '\0';
+ OneCharacter[0] = c;
+ HalDisplayString(OneCharacter);
+}
+
+
+//
+// Return the integer value of numeral represented by ascii char
+//
+
+int xatoi(char c)
+{
+ return c - '0';
+}
diff --git a/private/ntos/rtl/i386/ioaccess.asm b/private/ntos/rtl/i386/ioaccess.asm
new file mode 100644
index 000000000..17dbd1e36
--- /dev/null
+++ b/private/ntos/rtl/i386/ioaccess.asm
@@ -0,0 +1,423 @@
+ title "ioaccess"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; ioaccess.asm
+;
+; Abstract:
+;
+; Procedures to correctly touch I/O registers.
+;
+; Author:
+;
+; Bryan Willman (bryanwi) 16 May 1990
+;
+; Environment:
+;
+; User or Kernel, although privledge (IOPL) may be required.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+;++
+;
+; I/O memory space read and write functions.
+;
+; These have to be actual functions on the 386, because we need
+; to use assembler, but cannot return a value if we inline it.
+;
+; This set of functions manipulates I/O registers in MEMORY space.
+; (Uses x86 mov instructions)
+;
+;--
+
+
+
+;++
+;
+; UCHAR
+; READ_REGISTER_UCHAR(
+; PUCHAR Register
+; )
+;
+; Memory space references will use lock prefix to force real access,
+; flush through posted write buffers, and so forth.
+;
+; Arguments:
+; (esp+4) = Register
+;
+; Returns:
+; Value in register.
+;
+;--
+cPublicProc _READ_REGISTER_UCHAR ,1
+cPublicFpo 1,0
+
+ mov edx,[esp+4] ; (edx) = Register
+ mov al,[edx] ; (al) = byte, lock forces real access
+ stdRET _READ_REGISTER_UCHAR
+
+stdENDP _READ_REGISTER_UCHAR
+
+
+
+;++
+;
+; USHORT
+; READ_REGISTER_USHORT(
+; PUSHORT Register
+; )
+;
+; Memory space references will use lock prefix to force real access,
+; flush through posted write buffers, and so forth.
+;
+; Arguments:
+; (esp+4) = Register
+;
+; Returns:
+; Value in register.
+;
+;--
+cPublicProc _READ_REGISTER_USHORT ,1
+cPublicFpo 1,0
+
+ mov edx,[esp+4] ; (edx) = Register
+ mov ax,[edx] ; (ax) = word, lock forces real access
+ stdRET _READ_REGISTER_USHORT
+
+stdENDP _READ_REGISTER_USHORT
+
+
+
+;++
+;
+; ULONG
+; READ_REGISTER_ULONG(
+; PULONG Register
+; )
+;
+; Memory space references will use lock prefix to force real access,
+; flush through posted write buffers, and so forth.
+;
+; Arguments:
+; (esp+4) = Register
+;
+; Returns:
+; Value in register.
+;
+;--
+cPublicProc _READ_REGISTER_ULONG ,1
+cPublicFpo 1,0
+
+ mov edx,[esp+4] ; (edx) = Register
+ mov eax,[edx] ; (eax) = dword, lock forces real access
+ stdRET _READ_REGISTER_ULONG
+
+stdENDP _READ_REGISTER_ULONG
+
+
+;++
+;
+; VOID
+; READ_REGISTER_BUFFER_UCHAR(
+; PUCHAR Register,
+; PUCHAR Buffer,
+; ULONG Count
+; )
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Buffer address
+; (esp+12) = Count
+;
+;--
+cPublicProc _READ_REGISTER_BUFFER_UCHAR ,3
+cPublicFpo 3,0
+
+ mov eax, esi
+ mov edx, edi ; Save esi, edi
+
+ mov ecx,[esp+12] ; (ecx) = transfer count
+ mov esi,[esp+4] ; (edx) = Register
+ mov edi,[esp+8] ; (edi) = buffer
+ rep movsb
+
+ mov edi, edx
+ mov esi, eax
+
+ stdRET _READ_REGISTER_BUFFER_UCHAR
+
+stdENDP _READ_REGISTER_BUFFER_UCHAR
+
+
+;++
+;
+; VOID
+; READ_REGISTER_BUFFER_USHORT(
+; PUSHORT Register,
+; PUSHORT Buffer,
+; ULONG Count
+; )
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Buffer address
+; (esp+12) = Count
+;
+;--
+cPublicProc _READ_REGISTER_BUFFER_USHORT ,3
+cPublicFpo 3,0
+
+ mov eax, esi
+ mov edx, edi ; Save esi, edi
+
+ mov ecx,[esp+12] ; (ecx) = transfer count
+ mov esi,[esp+4] ; (edx) = Register
+ mov edi,[esp+8] ; (edi) = buffer
+ rep movsw
+
+ mov edi, edx
+ mov esi, eax
+ stdRET _READ_REGISTER_BUFFER_USHORT
+
+stdENDP _READ_REGISTER_BUFFER_USHORT
+
+
+;++
+;
+; VOID
+; READ_REGISTER_BUFFER_ULONG(
+; PULONG Register,
+; PULONG Buffer,
+; ULONG Count
+; )
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Buffer address
+; (esp+12) = Count
+;
+;--
+cPublicProc _READ_REGISTER_BUFFER_ULONG ,3
+cPublicFpo 3,0
+
+ mov eax, esi
+ mov edx, edi ; Save esi, edi
+
+ mov ecx,[esp+12] ; (ecx) = transfer count
+ mov esi,[esp+4] ; (edx) = Register
+ mov edi,[esp+8] ; (edi) = buffer
+ rep movsd
+
+ mov edi, edx
+ mov esi, eax
+ stdRET _READ_REGISTER_BUFFER_ULONG
+
+stdENDP _READ_REGISTER_BUFFER_ULONG
+
+
+
+;++
+;
+; VOID
+; WRITE_REGISTER_UCHAR(
+; PUCHAR Register,
+; UCHAR Value
+; )
+;
+; Memory space references will use lock prefix to force real access,
+; flush through posted write buffers, and so forth.
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Value
+;
+;--
+cPublicProc _WRITE_REGISTER_UCHAR ,2
+cPublicFpo 2,0
+
+ mov edx,[esp+4] ; (edx) = Register
+ mov al,[esp+8] ; (al) = Value
+ mov [edx],al ; do write
+ lock or [esp+4],edx ; flush processors posted-write buffers
+ stdRET _WRITE_REGISTER_UCHAR
+
+stdENDP _WRITE_REGISTER_UCHAR
+
+
+
+;++
+;
+; VOID
+; WRITE_REGISTER_USHORT(
+; PUSHORT Register,
+; USHORT Value
+; )
+;
+; Memory space references will use lock prefix to force real access,
+; flush through posted write buffers, and so forth.
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Value
+;
+;--
+cPublicProc _WRITE_REGISTER_USHORT ,2
+cPublicFpo 2,0
+
+ mov edx,[esp+4] ; (edx) = Register
+ mov eax,[esp+8] ; (ax) = Value
+ mov [edx],ax ; do write
+ lock or [esp+4],edx ; flush processors posted-write buffers
+ stdRET _WRITE_REGISTER_USHORT
+
+stdENDP _WRITE_REGISTER_USHORT
+
+
+
+;++
+;
+; VOID
+; WRITE_REGISTER_ULONG(
+; PULONG Register,
+; ULONG Value
+; )
+;
+; Memory space references will use lock prefix to force real access,
+; flush through posted write buffers, and so forth.
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Value
+;
+;--
+cPublicProc _WRITE_REGISTER_ULONG ,2
+cPublicFpo 2,0
+
+ mov edx,[esp+4] ; (edx) = Register
+ mov eax,[esp+8] ; (eax) = Value
+ mov [edx],eax ; do write
+ lock or [esp+4],edx ; flush processors posted-write buffers
+ stdRET _WRITE_REGISTER_ULONG
+
+stdENDP _WRITE_REGISTER_ULONG
+
+
+;++
+;
+; VOID
+; WRITE_REGISTER_BUFFER_UCHAR(
+; PUCHAR Register,
+; PUCHAR Buffer,
+; ULONG Count
+; )
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Buffer address
+; (esp+12) = Count
+;
+;--
+cPublicProc _WRITE_REGISTER_BUFFER_UCHAR ,3
+cPublicFpo 3,0
+
+ mov eax, esi
+ mov edx, edi ; Save esi, edi
+
+ mov ecx,[esp+12] ; (ecx) = transfer count
+ mov esi,[esp+8] ; (edi) = buffer
+ mov edi,[esp+4] ; (edx) = Register
+ rep movsb
+ lock or [esp+4],ecx ; flush processors posted-write buffers
+
+ mov edi, edx
+ mov esi, eax
+
+ stdRET _WRITE_REGISTER_BUFFER_UCHAR
+
+stdENDP _WRITE_REGISTER_BUFFER_UCHAR
+
+
+;++
+;
+; VOID
+; WRITE_REGISTER_BUFFER_USHORT(
+; PUSHORT Register,
+; PUSHORT Buffer,
+; ULONG Count
+; )
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Buffer address
+; (esp+12) = Count
+;
+;--
+cPublicProc _WRITE_REGISTER_BUFFER_USHORT ,3
+cPublicFpo 3,0
+
+ mov eax, esi
+ mov edx, edi ; Save esi, edi
+
+ mov ecx,[esp+12] ; (ecx) = transfer count
+ mov esi,[esp+8] ; (edi) = buffer
+ mov edi,[esp+4] ; (edx) = Register
+ rep movsw
+ lock or [esp+4],ecx ; flush processors posted-write buffers
+
+ mov edi, edx
+ mov esi, eax
+ stdRET _WRITE_REGISTER_BUFFER_USHORT
+
+stdENDP _WRITE_REGISTER_BUFFER_USHORT
+
+
+;++
+;
+; VOID
+; WRITE_REGISTER_BUFFER_ULONG(
+; PULONG Register,
+; PULONG Buffer,
+; ULONG Count
+; )
+;
+; Arguments:
+; (esp+4) = Register
+; (esp+8) = Buffer address
+; (esp+12) = Count
+;
+;--
+cPublicProc _WRITE_REGISTER_BUFFER_ULONG ,3
+cPublicFpo 0, 3
+
+;FPO ( 0, 3, 0, 0, 0, 0 )
+
+ mov eax, esi
+ mov edx, edi ; Save esi, edi
+
+ mov ecx,[esp+12] ; (ecx) = transfer count
+ mov esi,[esp+8] ; (edi) = buffer
+ mov edi,[esp+4] ; (edx) = Register
+ rep movsd
+ lock or [esp+4],ecx ; flush processors posted-write buffers
+
+ mov edi, edx
+ mov esi, eax
+ stdRET _WRITE_REGISTER_BUFFER_ULONG
+
+stdENDP _WRITE_REGISTER_BUFFER_ULONG
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/largeint.asm b/private/ntos/rtl/i386/largeint.asm
new file mode 100644
index 000000000..d376cbd7e
--- /dev/null
+++ b/private/ntos/rtl/i386/largeint.asm
@@ -0,0 +1,855 @@
+ TITLE "Large Integer Arithmetic"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; largeint.s
+;
+; Abstract:
+;
+; This module implements routines for performing extended integer
+; arithmtic.
+;
+; Author:
+;
+; David N. Cutler (davec) 24-Aug-1989
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+IFNDEF BLDR_KERNEL_RUNTIME
+ EXTRNP _RtlRaiseStatus, 1
+ENDIF
+
+
+_TEXT$00 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "RtlLargeIntegerAdd"
+;++
+;
+; 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:
+;
+; (TOS+4) = Addend1 - first addend value
+; (TOS+12) = Addend2 - second addend value
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+cPublicProc _RtlLargeIntegerAdd ,4
+cPublicFpo 4,0
+
+ mov eax,[esp]+4 ; (eax)=add1.low
+ add eax,[esp]+12 ; (eax)=sum.low
+ mov edx,[esp]+8 ; (edx)=add1.hi
+ adc edx,[esp]+16 ; (edx)=sum.hi
+ stdRET _RtlLargeIntegerAdd
+
+stdENDP _RtlLargeIntegerAdd
+
+
+ page
+ subttl "Enlarged 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:
+;
+; (TOS+4) = Factor1
+; (TOS+8) = Factor2
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+cPublicProc _RtlEnlargedIntegerMultiply ,2
+cPublicFpo 2,0
+
+ mov eax,[esp]+4 ; (eax) = factor1
+ imul dword ptr [esp]+8 ; (edx:eax) = signed result
+ stdRET _RtlEnlargedIntegerMultiply
+
+stdENDP _RtlEnlargedIntegerMultiply
+
+
+ page
+ subttl "Enlarged Unsigned Integer Multiply"
+;++
+;
+; LARGE_INTEGER
+; RtlEnlargedUnsignedMultiply (
+; IN ULONG Multiplicand,
+; IN ULONG Multiplier
+; )
+;
+; Routine Description:
+;
+; This function multiplies an un signed integer by an unsigned integer and
+; returns a signed large integer result.
+;
+; Arguments:
+;
+; (TOS+4) = Factor1
+; (TOS+8) = Factor2
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+cPublicProc _RtlEnlargedUnsignedMultiply ,2
+cPublicFpo 2,0
+
+ mov eax,[esp]+4 ; (eax) = factor1
+ mul dword ptr [esp]+8 ; (edx:eax) = unsigned result
+ stdRET _RtlEnlargedUnsignedMultiply
+
+stdENDP _RtlEnlargedUnsignedMultiply
+
+ page
+ subttl "Enlarged Unsigned Integer 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.
+;
+; Arguments:
+;
+; Dividend - Supplies the dividend value.
+;
+; Divisor - Supplies the divisor value.
+;
+; Remainder - Supplies an optional pointer to a variable that
+; receives the remainder.
+;
+; Return Value:
+;
+; The unsigned long integer quotient is returned as the function value.
+;
+;--
+
+cPublicProc _RtlEnlargedUnsignedDivide,4
+cPublicFpo 4,0
+
+ mov eax, [esp+4] ; (eax) = Dividend.LowPart
+ mov edx, [esp+8] ; (edx) = Dividend.HighPart
+ mov ecx, [esp+16] ; (ecx) = pRemainder
+ div dword ptr [esp+12] ; divide by Divisor
+
+ or ecx, ecx ; return remainder?
+ jnz short @f
+
+ stdRET _RtlEnlargedUnsignedDivide ; (eax) = Quotient
+
+align 4
+@@: mov [ecx], edx ; save remainder
+ stdRET _RtlEnlargedUnsignedDivide ; (eax) = Quotient
+
+stdENDP _RtlEnlargedUnsignedDivide
+
+ page
+ subttl "Extended Large Integer Divide"
+
+;++
+;
+; LARGE_INTEGER
+; RtlExtendedLargeIntegerDivide (
+; IN LARGE_INTEGER Dividend,
+; IN ULONG Divisor,
+; OUT PULONG Remainder OPTIONAL
+; )
+;
+; Routine Description:
+;
+; This routine divides an unsigned 64 bit dividend by a 32 bit divisor
+; and returns a 64-bit quotient, and optionally the 32-bit remainder.
+;
+;
+; Arguments:
+;
+; Dividend - Supplies the 64 bit dividend for the divide operation.
+;
+; Divisor - Supplies the 32 bit divisor for the divide operation.
+;
+; Remainder - Supplies an optional pointer to a variable which receives
+; the remainder
+;
+; Return Value:
+;
+; The 64-bit quotient is returned as the function value.
+;
+;--
+
+cPublicProc _RtlExtendedLargeIntegerDivide, 4
+cPublicFpo 4,3
+
+ push esi
+ push edi
+ push ebx
+
+ mov eax, [esp+16] ; (eax) = Dividend.LowPart
+ mov edx, [esp+20] ; (edx) = Dividend.HighPart
+
+lid00: mov ebx, [esp+24] ; (ebx) = Divisor
+ or ebx, ebx
+ jz short lid_zero ; Attempted a divide by zero
+
+ push ebp
+
+ mov ecx, 64 ; Loop count
+ xor esi, esi ; Clear partial remainder
+
+; (edx:eax) = Dividend
+; (ebx) = Divisor
+; (ecx) = Loop count
+; (esi) = partial remainder
+
+align 4
+lid10: shl eax, 1 ; (LowPart << 1) | 0
+ rcl edx, 1 ; (HighPart << 1) | CF
+ rcl esi, 1 ; (Partial << 1) | CF
+
+ sbb edi, edi ; clone CF into edi (0 or -1)
+
+ cmp esi, ebx ; check if partial remainder less then divisor
+ cmc
+ sbb ebp, ebp ; clone CF intp ebp
+ or edi, ebp ; merge with remainder of high bit
+
+ sub eax, edi ; merge quotient bit
+ and edi, ebx ; Select divisor or 0
+ sub esi, edi
+
+ dec ecx ; dec interration count
+ jnz short lid10 ; go around again
+
+ pop ebp
+ pop ebx
+ pop edi
+
+ mov ecx, [esp+20] ; (ecx) = Remainder
+ or ecx, ecx
+ jnz short lid20
+
+ pop esi
+ stdRET _RtlExtendedLargeIntegerDivide
+
+align 4
+lid20:
+ mov [ecx], esi ; store remainder
+ pop esi
+ stdRET _RtlExtendedLargeIntegerDivide
+
+lid_zero:
+IFNDEF BLDR_KERNEL_RUNTIME
+ stdCall _RtlRaiseStatus, <STATUS_INTEGER_DIVIDE_BY_ZERO>
+ENDIF
+ pop ebx
+ pop edi
+ pop esi
+ stdRET _RtlExtendedLargeIntegerDivide
+
+stdENDP _RtlExtendedLargeIntegerDivide
+
+ page
+ subttl "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:
+;
+; (ebp+8) = Dividend
+; (ebp+16) = MagicDivisor value is a 64-bit multiplicative reciprocal
+; (ebp+24) = ShiftCount - Right shift adjustment value.
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+RemdDiv equ [ebp+8] ; Dividend
+RemdRec equ [ebp+16] ; Reciprocal (magic divisor)
+RemdShift equ [ebp+24]
+RemdTmp1 equ [ebp-4]
+RemdTmp2 equ [ebp-8]
+RemdTmp3 equ [ebp-12]
+
+cPublicProc _RtlExtendedMagicDivide ,5
+
+ push ebp
+ mov ebp,esp
+ sub esp,12
+ push esi
+
+ mov esi, RemdDiv+4
+ test esi,80000000h
+ jz remd10 ; no sign, no need to negate
+
+ neg dword ptr RemdDiv+4
+ neg dword ptr RemdDiv
+ sbb dword ptr RemdDiv+4,0 ; negate
+
+remd10: mov eax,RemdRec
+ mul dword ptr RemdDiv ; (edx:eax) = Div.lo * Rec.lo
+ mov RemdTmp1,edx
+
+ mov eax,RemdRec
+ mul dword ptr RemdDiv+4 ; (edx:eax) = Div.hi * Rec.lo
+ mov RemdTmp2,eax
+ mov RemdTmp3,edx
+
+ mov eax,RemdRec+4
+ mul dword ptr RemdDiv ; (edx:eax) = Div.lo * Rec.hi
+
+;
+; Col 0 doesn't matter
+; Col 1 = Hi(Div.lo * Rec.lo) + Low(Div.Hi * Rec.lo) + Low(Div.lo * Rec.hi)
+; = RemdTmp1 + RemdTmp2 + eax
+; -> Only want carry from Col 1
+;
+
+ xor ecx,ecx ; (ecx) = 0
+ add eax,RemdTmp1
+ adc ecx, 0 ; save carry in ecx
+ add eax,RemdTmp2
+ adc ecx, 0 ; Save Carry, all we want from Col2
+
+ mov RemdTmp1,edx
+
+ mov eax,RemdRec+4
+ mul dword ptr RemdDiv+4 ; (edx:eax) = Div.Hi * Rec.Hi
+
+;
+; TOS = carry flag from Col 1
+;
+; Col 2 = Col1 CF +
+; Hi(Div.Hi * Rec.Lo) + Hi(Div.Lo * Rec.Hi) + Low(Div.Hi * Rec.Hi)
+; = CF + RemdTmp3 + RemdTmp1 + eax
+;
+; Col 3 = Col2 CF + Hi(Div.Hi * Rec.Hi)
+; = CF + edx
+;
+
+ add eax,RemdTmp1
+ adc edx, 0 ; add carry to edx
+ add eax,RemdTmp3 ; (eax) = col 2
+ adc edx, 0 ; add carry to edx
+ add eax, ecx
+ adc edx, 0 ; (edx) = col 3
+
+;
+; (edx:eax) = the high 64 bits of the multiply, shift it right by
+; shift count to discard bits to right of virtual decimal pt.
+;
+; RemdShift could be as large as 63 and still not 0 the result, 386
+; will only shift 31 bits at a time, so must do the sift multiple
+; times to get correct effect.
+;
+
+ mov cl,RemdShift
+remd20: cmp cl,31
+ jbe remd30
+ sub cl,31
+ shrd eax,edx,31
+ shr edx,31
+ jmp remd20
+
+remd30: shrd eax,edx,cl
+ shr edx,cl
+
+;
+; Negate the result if need be
+;
+
+ test esi,80000000h
+ jz remd40 ; no sign, go return without negate
+
+ neg edx
+ neg eax
+ sbb edx,0
+
+;
+; Store the result
+;
+
+remd40:
+ ; results in (edx:eax)
+
+ pop esi
+ mov esp,ebp
+ pop ebp
+ stdRET _RtlExtendedMagicDivide
+
+stdENDP _RtlExtendedMagicDivide
+
+
+ page
+ subttl "Extended Integer Multiply"
+;++
+;
+; LARGE_INTEGER
+; RtlExtendedIntegerMultiply (
+; IN LARGE_INTEGER Multiplicand,
+; IN ULONG Multiplier
+; )
+;
+; Routine Description:
+;
+; This function multiplies a signed large integer by a signed integer and
+; returns the signed large integer result.
+;
+; Arguments:
+;
+; (ebp+8,12)=multiplican (MCAN)
+; (ebp+16)=multiplier (MPER)
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+ReimMCAN equ <dword ptr [ebp+8]>
+ReimMPER equ <dword ptr [ebp+16]>
+
+cPublicProc _RtlExtendedIntegerMultiply ,3
+
+ push ebp
+ mov ebp,esp
+ push esi
+
+ mov esi,ReimMPER
+ xor esi,ReimMCAN+4 ; (esi) = result sign
+
+ test ReimMCAN+4,80000000h
+ jz short reim10 ; MCAN pos, go look at MPER
+
+ neg dword ptr ReimMCAN+4
+ neg dword ptr ReimMCAN
+ sbb dword ptr ReimMCAN+4,0 ; negate multiplican
+
+reim10: test ReimMPER,80000000h
+ jz short reim20 ; MPER pos, go do multiply
+
+ neg dword ptr ReimMPER ; negate multiplier
+
+reim20: mov eax,ReimMPER
+ mul dword ptr ReimMCAN ; (edx:eax) = MPER * MCAN.low
+ push edx
+ mov ecx, eax
+ mov eax,ReimMPER
+ mul dword ptr ReimMCAN+4 ; (edx:eax) = MPER * MCAN.high
+ add eax,[esp] ; (eax) = hi part of MPER*MCAN.low
+ ; plus low part of MPER*MCAN.hi
+
+ test esi,80000000h
+ jz short reim30 ; result sign is OK, go return
+
+ neg eax
+ neg ecx
+ sbb eax,0 ; negate result
+
+reim30: add esp,4 ; clean eax off stack
+ pop esi ; restore nonvolatile reg
+ mov edx,eax ; (edx:ecx) = result
+ mov eax,ecx ; (edx:eax) = result
+
+ pop ebp
+ stdRET _RtlExtendedIntegerMultiply
+
+stdENDP _RtlExtendedIntegerMultiply
+
+ page
+ subttl "Large Integer Shift Left"
+;++
+;
+; LARGE_INTEGER
+; RtlLargeIntegerShiftLeft (
+; IN LARGE_INTEGER LargeInteger,
+; IN CCHAR ShiftCount
+; )
+;
+;
+; Routine Description:
+;
+; This routine does a left logical shift of a large integer by a
+; specified amount (ShiftCount) modulo 64.
+;
+; Arguments:
+;
+; LargeInteger - Supplies the large integer to be shifted
+;
+; ShiftCount - Supplies the left shift count
+;
+; Return Value:
+;
+; LARGE_INTEGER - Receives the shift large integer result
+;
+;--
+cPublicProc _RtlLargeIntegerShiftLeft,3
+cPublicFpo 3,0
+
+ mov ecx, [esp+12] ; (ecx) = ShiftCount
+ and ecx, 3fh ; mod 64
+
+ cmp ecx, 32
+ jnc short sl10
+;
+; Shift count is less then 32 bits.
+;
+ mov eax, [esp+4] ; (eax) = LargeInteger.LowPart
+ mov edx, [esp+8] ; (edx) = LargeInteger.HighPart
+ shld edx, eax, cl
+ shl eax, cl
+
+ stdRET _RtlLargeIntegerShiftLeft
+
+align 4
+sl10:
+;
+; 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.
+;
+ mov edx, [esp+4] ; (edx) = LargeInteger.LowPart
+ xor eax, eax ; store lowpart
+ shl edx, cl ; store highpart
+
+ stdRET _RtlLargeIntegerShiftLeft
+
+stdENDP _RtlLargeIntegerShiftLeft
+
+ page
+ subttl "Large Integer Shift Right"
+
+;--
+;
+;LARGE_INTEGER
+;RtlLargeIntegerShiftRight (
+; IN LARGE_INTEGER LargeInteger,
+; IN CCHAR ShiftCount
+; )
+;
+;Routine Description:
+;
+; This routine does a right logical shift of a large integer by a
+; specified amount (ShiftCount) modulo 64.
+;
+;Arguments:
+;
+; LargeInteger - Supplies the large integer to be shifted
+;
+; ShiftCount - Supplies the right shift count
+;
+;Return Value:
+;
+; LARGE_INTEGER - Receives the shift large integer result
+;
+;--*/
+cPublicProc _RtlLargeIntegerShiftRight,3
+cPublicFpo 3,0
+
+ mov ecx, [esp+12] ; (ecx) = ShiftCount
+ and ecx, 3fh ; mod 64
+
+ cmp ecx, 32
+ jnc short sr10
+
+;
+; Shift count is less then 32 bits.
+;
+ mov eax, [esp+4] ; (eax) = LargeInteger.LowPart
+ mov edx, [esp+8] ; (edx) = LargeInteger.HighPart
+ shrd eax, edx, cl
+ shr edx, cl
+
+ stdRET _RtlLargeIntegerShiftRight
+
+align 4
+sr10:
+;
+; 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.
+;
+ mov eax, [esp+8] ; (eax) = LargeInteger.HighPart
+ xor edx, edx ; store highpart
+ shr eax, cl ; store lowpart
+
+ stdRET _RtlLargeIntegerShiftRight
+
+stdENDP _RtlLargeIntegerShiftRight
+
+;++
+;
+;LARGE_INTEGER
+;RtlLargeIntegerArithmeticShift (
+; IN LARGE_INTEGER LargeInteger,
+; IN CCHAR ShiftCount
+; )
+;
+;Routine Description:
+;
+; This routine does a right arithmetic shift of a large integer by a
+; specified amount (ShiftCount) modulo 64.
+;
+;Arguments:
+;
+; LargeInteger - Supplies the large integer to be shifted
+;
+; ShiftCount - Supplies the right shift count
+;
+;Return Value:
+;
+; LARGE_INTEGER - Receives the shift large integer result
+;
+;--
+cPublicProc _RtlLargeIntegerArithmeticShift,3
+cPublicFpo 3,0
+
+ mov ecx, [esp+12] ; (ecx) = ShiftCount
+ and ecx, 3fh ; mod 64
+
+ cmp ecx, 32
+ jc short sar10
+
+;
+; Shift count is greater than or equal 32 bits - high half of result is sign
+; bit, low half is the high half shifted right by remaining count.
+;
+ mov eax, [esp+8] ; (eax) = LargeInteger.HighPart
+ sar eax, cl ; store highpart
+ bt eax, 31 ; sign bit set?
+ sbb edx, edx ; duplicate sign bit into highpart
+
+ stdRET _RtlLargeIntegerArithmeticShift
+
+align 4
+sar10:
+;
+; Shift count is less then 32 bits.
+;
+;
+ mov eax, [esp+4] ; (eax) = LargeInteger.LowPart
+ mov edx, [esp+8] ; (edx) = LargeInteger.HighPart
+ shrd eax, edx, cl
+ sar edx, cl
+
+ stdRET _RtlLargeIntegerArithmeticShift
+
+stdENDP _RtlLargeIntegerArithmeticShift,3
+
+
+ page
+ subttl "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:
+;
+; (TOS+4) = Subtrahend
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+cPublicProc _RtlLargeIntegerNegate ,2
+cPublicFpo 2,0
+
+ mov eax,[esp]+4 ; (eax) = lo
+ mov edx,[esp]+8
+ neg edx ; (edx) = 2's comp of hi part
+ neg eax ; if ((eax) == 0) CF = 0
+ ; else CF = 1
+ sbb edx,0 ; (edx) = (edx) - CF
+ ; (edx:eax) = result
+ stdRET _RtlLargeIntegerNegate
+
+stdENDP _RtlLargeIntegerNegate
+
+
+ page
+ subttl "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:
+;
+; (TOS+4) = Minuend
+; (TOS+12) = Subtrahend
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+cPublicProc _RtlLargeIntegerSubtract ,4
+cPublicFpo 4,0
+
+ mov eax,[esp]+4
+ sub eax,[esp]+12 ; (eax) = result.low
+ mov edx,[esp]+8
+ sbb edx,[esp]+16 ; (edx) = result.high
+ stdRET _RtlLargeIntegerSubtract
+
+stdENDP _RtlLargeIntegerSubtract
+
+ page
+ subttl "Convert Long to Large Integer"
+;++
+;
+; LARGE_INTEGER
+; RtlConvertLongToLargeInteger (
+; IN LONG SignedInteger
+; )
+;
+; Routine Description:
+;
+; This function converts the input signed integer to a signed large
+; integer and returns the latter as the result.
+;
+; Arguments:
+;
+; (TOS+4) = SignedInteger
+;
+; Return Value:
+;
+; The large integer result is stored (edx:eax)
+;
+;--
+
+cPublicProc _RtlConvertLongToLargeInteger ,1
+cPublicFpo 1,0
+
+ mov eax,[esp]+4 ; (eax) = SignedInteger
+ cdq ; (edx:eax) = signed LargeInt
+ stdRET _RtlConvertLongToLargeInteger
+
+stdENDP _RtlConvertLongToLargeInteger
+
+
+ page
+ subttl "Convert Ulong to Large Integer"
+;++
+;
+; LARGE_INTEGER
+; RtlConvertUlongToLargeInteger (
+; IN LONG UnsignedInteger
+; )
+;
+; Routine Description:
+;
+; This function converts the input unsigned integer to a signed large
+; integer and returns the latter as the result.
+;
+; Arguments:
+;
+; (TOS+4) = UnsignedInteger
+;
+; Return Value:
+;
+; The large integer result is stored in (edx:eax)
+;
+;--
+
+cPublicProc _RtlConvertUlongToLargeInteger ,1
+cPublicFpo 1,0
+
+ mov eax,[esp]+4 ; store low
+ xor edx,edx ; store 0 in high
+ stdRET _RtlConvertUlongToLargeInteger
+
+stdENDP _RtlConvertUlongToLargeInteger
+
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/rtl/i386/lzntx86.asm b/private/ntos/rtl/i386/lzntx86.asm
new file mode 100644
index 000000000..cf21d5456
--- /dev/null
+++ b/private/ntos/rtl/i386/lzntx86.asm
@@ -0,0 +1,445 @@
+ title "Compression and Decompression Engines"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; lzntx86.asm
+;
+; Abstract:
+;
+; This module implements the compression and decompression engines needed
+; to support file system compression. Functions are provided to
+; compress a buffer and decompress a buffer.
+;
+; Author:
+;
+; Mark Zbikowski (markz) 15-Mar-1994
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+; 15-Mar-1994 markz
+;
+; 386 version created
+;
+;--
+.386p
+
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+_TEXT$01 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page
+ subttl "Decompress a buffer"
+;++
+;
+; NTSTATUS
+; LZNT1DecompressChunk (
+; OUT PUCHAR UncompressedBuffer,
+; IN PUCHAR EndOfUncompressedBufferPlus1,
+; IN PUCHAR CompressedBuffer,
+; IN PUCHAR EndOfCompressedBufferPlus1,
+; OUT PULONG FinalUncompressedChunkSize
+; )
+;
+; Routine Description:
+;
+; This function decodes a stream of compression tokens and places the
+; resultant output into the destination buffer. The format of the input
+; is described ..\lznt1.c. As the input is decoded, checks are made to
+; ensure that no data is read past the end of the compressed input buffer
+; and that no data is stored past the end of the output buffer. Violations
+; indicate corrupt input and are indicated by a status return.
+;
+; The following code takes advantage of two 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 register conventions used in the loops below are:
+;
+; (al) contains the current tag byte
+; (ebx) contains the current width in bits of the length given
+; the maximum offset
+; that can be utilized in a copy token. We update this
+; value only prior to performing a copy. This width is used
+; both to index a mask table (for extracting the length) as
+; well as shifting (for extracting the copy offset)
+; (ecx) is used to contain counts during copies
+; (edx) is used as a temp variable during copies
+; (esi) is used mainly as the source of the next compressed token.
+; It is also used for copies.
+; (edi) is used as the destination of literals and copies
+; (ebp) is used as a frame pointer
+;
+; Arguments:
+;
+; UncompressedBuffer (ebp+8) - pointer to destination of uncompression.
+;
+; EndOfUncompressedBufferPlus1 (ebp+12) - pointer just beyond the
+; output buffer. This is used for consistency checking of the stored
+; compressed data.
+;
+; CompressedBuffer (ebp+16) - pointer to compressed source. This pointer
+; has been adjusted by the caller to point past the header word, so
+; the pointer points to the first tag byte describing which of the
+; following tokens are literals and which are copy groups.
+;
+; EndOfCompressedBufferPlus1 (ebp+20) - pointer just beyond end of input
+; buffer. This is used to terminate the decompression.
+;
+; FinalUncompressedChunkSize (ebp+24) - pointer to a returned decompressed
+; size. This has meaningful data ONLY when LZNT1DecompressChunk returns
+; STATUS_SUCCESS
+;
+; Return Value:
+;
+; STATUS_SUCCESS is returned only if the decompression consumes thee entire
+; input buffer and does not exceed the output buffer.
+; STATUS_BAD_COMPRESSION_BUFFER is returned when the output buffer would be
+; overflowed.
+;
+;--
+
+; Decompression macros
+
+;** TestLiteralAt - tests to see if there's a literal at a specific
+; bit position. If so, it branches to the appropriate copy code
+; (decorated by the bit being used).
+;
+; This code does no bounds checking
+
+TestLiteralAt macro CopyLabel,bit,IsMain
+ test al,1 SHL bit ; is there a copy token at this position?
+ jnz CopyLabel&bit ; yes, go copy it
+
+ mov dl,[esi+bit+1] ; (dl) = literal byte from compressed stream
+ifidn <IsMain>,<Y>
+ mov [edi+bit],dl ; store literal byte
+else
+ mov [edi],dl ; store literal byte
+ inc edi ; point to next literal
+endif
+
+endm
+
+
+; Jump - allow specific jumps with computed labels.
+
+Jump macro lab,tag
+ jmp lab&tag
+endm
+
+
+
+;** DoCopy - perform a copy. If a bit position is specified
+; then branch to the appropriate point in the "safe" tail when
+; the copy takes us too close to the end of the output buffer
+;
+; This code checks the bounds of the copy token: copying before the
+; beginning of the buffer and copying beyond the end of the buffer.
+
+DoCopy macro AdjustLabel,bit,IsMain
+
+ifidn <IsMain>,<Y>
+if bit ne 0
+ add edi,bit
+endif
+endif
+
+Test&AdjustLabel&bit:
+ cmp edi,WidthBoundary
+ ja Adjust&AdjustLabel&bit
+
+ xor ecx,ecx
+ mov cx,word ptr [esi+bit+1] ; (ecx) = encoded length:offset
+ lea edx,[esi+1] ; (edx) = next token location
+ mov Temp,edx
+
+ mov esi,ecx ; (esi) = encoded length:offset
+ and ecx,MaskTab[ebx*4] ; (ecx) = length
+ xchg ebx,ecx ; (ebx) = length/(ecx) = width
+ shr esi,cl ; (esi) = offset
+ xchg ebx,ecx ; (ebx) = width, (ecx) = length
+
+ neg esi ; (esi) = negative real offset
+ lea esi,[esi+edi-1] ; (esi) = pointer to previous string
+
+ cmp esi,UncompressedBuffer ; off front of buffer?
+ jb DOA ; yes, error
+
+ add ecx,3 ; (ecx) = real length
+
+ lea edx,[edi+ecx] ; (edx) = end of copy
+ifidn <IsMain>,<Y>
+ cmp edx,EndOfSpecialDest ; do we exceed buffer?
+ jae TailAdd&bit ; yes, handle in safe tail
+else
+ cmp edx,EndOfUncompressedBufferPlus1
+ ; do we exceed buffer?
+ ja DOA ; yes, error
+endif
+
+ rep movsb ; Copy the bytes
+
+ mov esi,Temp ; (esi) = next token location
+
+ifidn <IsMain>,<Y>
+ sub edi,bit+1
+endif
+
+endm
+
+
+
+
+
+;** AdjustWidth - adjust width of length based upon current position of
+; input buffer (max offset)
+
+
+AdjustWidth macro l,i
+Adjust&l&i:
+ dec ebx ; (ebx) = new width pointer
+ mov edx,UncompressedBuffer ; (edx) = pointer to dest buffer
+ add edx,WidthTab[ebx*4] ; (edx) = new width boundary
+ mov WidthBoundary,edx ; save boundary for comparison
+ jmp Test&l&i
+
+endm
+
+
+;** GenerateBlock - generates the unsafe block of copy/literal pieces.
+;
+; This code does no checking for simple input/output checking. Only
+; the data referred to by the copy tokens is checked.
+
+GenerateBlock macro bit
+Copy&bit:
+
+ DoCopy Body,bit,Y
+
+ j = bit + 1
+ while j lt 8
+ TestLiteralAt Copy,%(j),Y
+ j = j + 1
+ endm
+
+ add esi,9
+ add edi,8
+
+ jmp Top
+
+ AdjustWidth Body,bit
+endm
+
+
+
+;** GenerateTailBlock - generates safe tail block for compression. This
+; code checks everything before each byte stored so it is expected
+; to be executed only at the end of the buffer.
+
+
+GenerateTailBlock macro bit
+TailAdd&bit:
+ add EndOfCompressedBufferPlus1,1+2*8
+ ; restore buffer length to true length
+ mov esi,Temp ; (esi) = source of copy token block
+ dec esi
+
+Tail&bit:
+ lea ecx,[esi+bit+1] ; (ecx) = source of next token
+ cmp ecx,EndOfCompressedBufferPlus1 ; are we done?
+ jz Done ; yes - we exactly match end of buffer
+; ja DOA ; INTERNAL ERROR only
+
+ cmp edi,EndOfUncompressedBufferPlus1
+ jz Done ; go quit, destination is full
+; ja DOA ; INTERNAL ERROR only
+
+ TestLiteralAt TailCopy,bit,N
+
+ Jump Tail,%(bit+1)
+
+
+; We expect a copy token to be at [esi+bit+1]. This means that
+; esi+bit+1+tokensize must be <= EndOfCompressedBufferPlus1
+TailCopy&bit:
+ lea ecx,[esi+bit+3] ; (ecx) = next input position
+ cmp ecx,EndOfCompressedBufferPlus1 ; do we go too far
+ ja DOA ; yes, we are beyond the end of buffer
+
+ DoCopy Tail,bit,N ; perform copy
+
+ Jump Tail,%(bit+1)
+
+ AdjustWidth Tail,bit
+
+endm
+
+
+
+cPublicProc _LZNT1DecompressChunk ,5
+ push ebp ; (tos) = saved frame pointer
+ mov ebp,esp ; (ebp) = frame pointer to arguments
+ sub esp,12 ; Open up room for locals
+
+Temp equ dword ptr [ebp-12]
+WidthBoundary equ dword ptr [ebp-8]
+EndOfSpecialDest equ dword ptr [ebp-4]
+
+;SavedEBP equ dword ptr [ebp]
+;ReturnAddress equ dword ptr [ebp+4]
+
+UncompressedBuffer equ dword ptr [ebp+8]
+EndOfUncompressedBufferPlus1 equ dword ptr [ebp+12]
+CompressedBuffer equ dword ptr [ebp+16]
+EndOfCompressedBufferPlus1 equ dword ptr [ebp+20]
+FinalUncompressedChunkSize equ dword ptr [ebp+24]
+
+
+ push ebx
+ push esi
+ push edi
+
+ mov edi,UncompressedBuffer ; (edi) = destination of decompress
+ mov esi,CompressedBuffer ; (esi) = header
+ sub EndOfCompressedBufferPlus1,1+2*8 ; make room for special source
+
+ mov eax,EndOfUncompressedBufferPlus1 ; (eax) = end of destination
+ sub eax,8 ; (eax) = beginning of special tail
+ mov EndOfSpecialDest,eax ; store special tail
+
+ mov WidthBoundary,edi ; force initial width mismatch
+ mov ebx,13 ; initial width of output
+
+
+Top: cmp esi,EndOfCompressedBufferPlus1 ; Will this be the last tag group in source?
+ jae DoTail ; yes, go handle specially
+ cmp edi,EndOfSpecialDest ; are we too close to end of buffer?
+ jae DoTail ; yes, go skip to end
+
+ mov al,byte ptr [esi] ; (al) = tag byte, (esi) points to token
+
+ irpc i,<01234567>
+ TestLiteralAt Copy,%(i),Y
+ endm
+
+ add esi,9
+ add edi,8
+
+ jmp Top
+; ; Width of offset Width of length
+WidthTab dd 0FFFFh ; 16 0
+ dd 0FFFFh ; 15 1
+ dd 0FFFFh ; 14 2
+ dd 0FFFFh ; 13 3
+ dd 0FFFFh ; 12 4
+ dd 2048 ; 11 5
+ dd 1024 ; 10 6
+ dd 512 ; 9 7
+ dd 256 ; 8 8
+ dd 128 ; 7 9
+ dd 64 ; 6 10
+ dd 32 ; 5 11
+ dd 16 ; 4 12
+ dd 0 ; 3 13
+ dd 0 ; 2 14
+ dd 0 ; 1 15
+ dd 0 ; 0 16
+
+
+; ;
+MaskTab dd 0000000000000000b ; 0
+ dd 0000000000000001b ; 1
+ dd 0000000000000011b ; 2
+ dd 0000000000000111b ; 3
+ dd 0000000000001111b ; 4
+ dd 0000000000011111b ; 5
+ dd 0000000000111111b ; 6
+ dd 0000000001111111b ; 7
+ dd 0000000011111111b ; 8
+ dd 0000000111111111b ; 9
+ dd 0000001111111111b ; 10
+ dd 0000011111111111b ; 11
+ dd 0000111111111111b ; 12
+ dd 0001111111111111b ; 13
+ dd 0011111111111111b ; 14
+ dd 0111111111111111b ; 15
+ dd 1111111111111111b ; 16
+
+
+ irpc i,<01234567>
+ GenerateBlock %(i)
+ endm
+
+; We're handling a tail specially for this. We must check at all
+; spots for running out of input as well as overflowing output.
+;
+; (esi) = pointer to possible next tag
+
+DoTail: add EndOfCompressedBufferPlus1,1+2*8 ; point to end of compressed input
+
+TailLoop:
+ cmp esi,EndOfCompressedBufferPlus1 ; are we totally done?
+ jz Done ; yes, go return
+ mov al,byte ptr [esi] ; (al) = tag byte
+
+ jmp Tail0
+
+ irpc i,<01234567>
+ GenerateTailBlock i
+ endm
+
+Tail8: add esi,9
+ jmp TailLoop
+
+
+
+DOA: mov eax,STATUS_BAD_COMPRESSION_BUFFER
+ jmp Final
+
+Done: mov eax,edi ; (eax) = pointer to next byte to store
+ sub eax,UncompressedBuffer ; (eax) = length of uncompressed
+ mov edi,FinalUncompressedChunkSize ; (edi) = user return value location
+ mov [edi],eax ; return total transfer size to user
+ xor eax,eax ; (eax) = STATUS_SUCCESS
+
+Final: pop edi
+ pop esi
+ pop ebx
+ mov esp,ebp
+ pop ebp
+
+
+ stdRET _LZNT1DecompressChunk
+
+stdENDP _LZNT1DecompressChunk
+
+_TEXT$01 ends
+ end
diff --git a/private/ntos/rtl/i386/movemem.asm b/private/ntos/rtl/i386/movemem.asm
new file mode 100644
index 000000000..f5131312d
--- /dev/null
+++ b/private/ntos/rtl/i386/movemem.asm
@@ -0,0 +1,659 @@
+ title "User Mode Zero and Move Memory functions"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; movemem.asm
+;
+; Abstract:
+;
+; This module implements functions to zero and copy blocks of memory
+;
+;
+; Author:
+;
+; Steven R. Wood (stevewo) 25-May-1990
+;
+; Environment:
+;
+; User mode only.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+if DBG
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+ public _RtlpZeroCount
+ public _RtlpZeroBytes
+
+_RtlpZeroCount dd 0
+_RtlpZeroBytes dd 0
+
+ifndef BLDR_KERNEL_RUNTIME
+_MsgUnalignedPtr db 'RTL: RtlCompare/FillMemoryUlong called with unaligned pointer (%x)\n',0
+endif
+
+_DATA ENDS
+
+ifndef BLDR_KERNEL_RUNTIME
+ifdef NTOS_KERNEL_RUNTIME
+ extrn _KdDebuggerEnabled:BYTE
+endif
+ EXTRNP _DbgBreakPoint,0
+ extrn _DbgPrint:near
+endif
+endif
+
+;
+; Alignment parameters for zeroing and moving memory.
+;
+
+ZERO_MEMORY_ALIGNMENT = 4
+ZERO_MEMORY_ALIGNMENT_LOG2 = 2
+ZERO_MEMORY_ALIGNMENT_MASK = ZERO_MEMORY_ALIGNMENT - 1
+
+MEMORY_ALIGNMENT = 4
+MEMORY_ALIGNMENT_LOG2 = 2
+MEMORY_ALIGNMENT_MASK = MEMORY_ALIGNMENT - 1
+
+
+;
+; Alignment for functions in this module
+;
+
+CODE_ALIGNMENT macro
+ align 16
+endm
+
+
+_TEXT$00 SEGMENT PARA PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page , 132
+ subttl "RtlCompareMemory"
+;++
+;
+; 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 (esp+4) - Supplies a pointer to the first block of memory to
+; compare.
+;
+; Source2 (esp+8) - Supplies a pointer to the second block of memory to
+; compare.
+;
+; Length (esp+12) - 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.
+;
+;--
+
+RcmSource1 equ [esp+12]
+RcmSource2 equ [esp+16]
+RcmLength equ [esp+20]
+
+CODE_ALIGNMENT
+cPublicProc _RtlCompareMemory,3
+cPublicFpo 3,0
+
+ push esi ; save registers
+ push edi ;
+ cld ; clear direction
+ mov esi,RcmSource1 ; (esi) -> first block to compare
+ mov edi,RcmSource2 ; (edi) -> second block to compare
+
+;
+; Compare dwords, if any.
+;
+
+rcm10: mov ecx,RcmLength ; (ecx) = length in bytes
+ shr ecx,2 ; (ecx) = length in dwords
+ jz rcm20 ; no dwords, try bytes
+ repe cmpsd ; compare dwords
+ jnz rcm40 ; mismatch, go find byte
+
+;
+; Compare residual bytes, if any.
+;
+
+rcm20: mov ecx,RcmLength ; (ecx) = length in bytes
+ and ecx,3 ; (ecx) = length mod 4
+ jz rcm30 ; 0 odd bytes, go do dwords
+ repe cmpsb ; compare odd bytes
+ jnz rcm50 ; mismatch, go report how far we got
+
+;
+; All bytes in the block match.
+;
+
+rcm30: mov eax,RcmLength ; set number of matching bytes
+ pop edi ; restore registers
+ pop esi ;
+ stdRET _RtlCompareMemory
+
+;
+; When we come to rcm40, esi (and edi) points to the dword after the
+; one which caused the mismatch. Back up 1 dword and find the byte.
+; Since we know the dword didn't match, we can assume one byte won't.
+;
+
+rcm40: sub esi,4 ; back up
+ sub edi,4 ; back up
+ mov ecx,5 ; ensure that ecx doesn't count out
+ repe cmpsb ; find mismatch byte
+
+;
+; When we come to rcm50, esi points to the byte after the one that
+; did not match, which is TWO after the last byte that did match.
+;
+
+rcm50: dec esi ; back up
+ sub esi,RcmSource1 ; compute bytes that matched
+ mov eax,esi ;
+ pop edi ; restore registers
+ pop esi ;
+ stdRET _RtlCompareMemory
+
+stdENDP _RtlCompareMemory
+
+
+ subttl "RtlCompareMemory"
+EcmlSource equ [esp + 4 + 4]
+EcmlLength equ [esp + 4 + 8]
+EcmlPattern equ [esp + 4 + 12]
+
+; end of arguments
+
+CODE_ALIGNMENT
+cPublicProc _RtlCompareMemoryUlong ,3
+
+;
+; Save the non-volatile registers that we will use, without the benefit of
+; a frame pointer. No exception handling in this routine.
+;
+
+ push edi
+
+;
+; Setup the registers for using REP STOS instruction to zero memory.
+;
+; edi -> memory to zero
+; ecx = number of 32-bits words to zero
+; edx = number of extra 8-bit bytes to zero at the end (0 - 3)
+; eax = value to store in destination
+; direction flag is clear for auto-increment
+;
+
+ mov edi,EcmlSource
+if DBG
+ifndef BLDR_KERNEL_RUNTIME
+ test edi,3
+ jz @F
+ push edi
+ push offset FLAT:_MsgUnalignedPtr
+ call _DbgPrint
+ add esp, 2 * 4
+ifdef NTOS_KERNEL_RUNTIME
+ cmp _KdDebuggerEnabled,0
+else
+ mov eax,fs:[PcTeb]
+ mov eax,[eax].TebPeb
+ cmp byte ptr [eax].PebBeingDebugged,0
+endif
+ je @F
+ call _DbgBreakPoint@0
+@@:
+endif
+endif
+ mov ecx,EcmlLength
+ mov eax,EcmlPattern
+ shr ecx,ZERO_MEMORY_ALIGNMENT_LOG2
+
+
+;
+; If number of 32-bit words to compare is non-zero, then do it.
+;
+
+ repe scasd
+ je @F
+ sub edi,4
+@@:
+ sub edi,EcmlSource
+ mov eax,edi
+ pop edi
+ stdRET _RtlCompareMemoryUlong
+
+stdENDP _RtlCompareMemoryUlong
+
+
+ subttl "RtlFillMemory"
+;++
+;
+; VOID
+; RtlFillMemory (
+; IN PVOID Destination,
+; IN ULONG Length,
+; IN UCHAR Fill
+; )
+;
+; Routine Description:
+;
+; This function fills memory with a byte value.
+;
+; Arguments:
+;
+; Destination - Supplies a pointer to the memory to zero.
+;
+; Length - Supplies the Length, in bytes, of the memory to be zeroed.
+;
+; Fill - Supplies the byte value to fill memory with.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+; definitions for arguments
+; (TOS) = Return address
+
+EfmDestination equ [esp + 4 + 4]
+EfmLength equ [esp + 4 + 8]
+EfmFill equ byte ptr [esp + 4 + 12]
+
+; end of arguments
+
+CODE_ALIGNMENT
+cPublicProc _RtlFillMemory ,3
+cPublicFpo 3,1
+
+;
+; Save the non-volatile registers that we will use, without the benefit of
+; a frame pointer. No exception handling in this routine.
+;
+
+ push edi
+
+;
+; Setup the registers for using REP STOS instruction to zero memory.
+;
+; edi -> memory to zero
+; ecx = number of 32-bits words to zero
+; edx = number of extra 8-bit bytes to zero at the end (0 - 3)
+; eax = value to store in destination
+; direction flag is clear for auto-increment
+;
+
+ mov edi,EfmDestination
+ mov ecx,EfmLength
+ mov al,EfmFill
+ mov ah,al
+ shl eax,16
+ mov al,EfmFill
+ mov ah,al
+ cld
+
+ mov edx,ecx
+ and edx,ZERO_MEMORY_ALIGNMENT_MASK
+ shr ecx,ZERO_MEMORY_ALIGNMENT_LOG2
+
+
+;
+; If number of 32-bit words to zero is non-zero, then do it.
+;
+
+ rep stosd
+
+;
+; If number of extra 8-bit bytes to zero is non-zero, then do it. In either
+; case restore non-volatile registers and return.
+;
+
+ or ecx,edx
+ jnz @F
+ pop edi
+ stdRET _RtlFillMemory
+@@:
+ rep stosb
+ pop edi
+ stdRET _RtlFillMemory
+
+stdENDP _RtlFillMemory
+
+ subttl "RtlFillMemory"
+;++
+;
+; VOID
+; RtlFillMemoryUlong (
+; IN PVOID Destination,
+; IN ULONG Length,
+; IN ULONG Fill
+; )
+;
+; Routine Description:
+;
+; This function fills memory with a 32-bit value. The Destination pointer
+; must be aligned on a 4 byte boundary and the low order two bits of the
+; Length parameter are ignored.
+;
+; Arguments:
+;
+; Destination - Supplies a pointer to the memory to zero.
+;
+; Length - Supplies the Length, in bytes, of the memory to be zeroed.
+;
+; Fill - Supplies the 32-bit value to fill memory with.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+; definitions for arguments
+; (TOS) = Return address
+
+EfmlDestination equ [esp + 4 + 4]
+EfmlLength equ [esp + 4 + 8]
+EfmlFill equ [esp + 4 + 12]
+
+; end of arguments
+
+CODE_ALIGNMENT
+cPublicProc _RtlFillMemoryUlong ,3
+cPublicFpo 3,1
+
+;
+; Save the non-volatile registers that we will use, without the benefit of
+; a frame pointer. No exception handling in this routine.
+;
+
+ push edi
+
+;
+; Setup the registers for using REP STOS instruction to zero memory.
+;
+; edi -> memory to zero
+; ecx = number of 32-bits words to zero
+; edx = number of extra 8-bit bytes to zero at the end (0 - 3)
+; eax = value to store in destination
+; direction flag is clear for auto-increment
+;
+
+ mov edi,EfmlDestination
+if DBG
+ifndef BLDR_KERNEL_RUNTIME
+ test edi,3
+ jz @F
+ push edi
+ push offset FLAT:_MsgUnalignedPtr
+ call _DbgPrint
+ add esp, 2 * 4
+ifdef NTOS_KERNEL_RUNTIME
+ cmp _KdDebuggerEnabled,0
+else
+ mov eax,fs:[PcTeb]
+ mov eax,[eax].TebPeb
+ cmp byte ptr [eax].PebBeingDebugged,0
+endif
+ je @F
+ call _DbgBreakPoint@0
+@@:
+endif
+endif
+ mov ecx,EfmlLength
+ mov eax,EfmlFill
+ shr ecx,ZERO_MEMORY_ALIGNMENT_LOG2
+
+
+;
+; If number of 32-bit words to zero is non-zero, then do it.
+;
+
+ rep stosd
+
+ pop edi
+ stdRET _RtlFillMemoryUlong
+
+stdENDP _RtlFillMemoryUlong
+
+ subttl "RtlZeroMemory"
+;++
+;
+; VOID
+; RtlZeroMemory (
+; IN PVOID Destination,
+; IN ULONG Length
+; )
+;
+; Routine Description:
+;
+; This function zeros memory.
+;
+; Arguments:
+;
+; Destination - Supplies a pointer to the memory to zero.
+;
+; Length - Supplies the Length, in bytes, of the memory to be zeroed.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+; definitions for arguments
+; (TOS) = Return address
+
+EzmDestination equ [esp + 4 + 4]
+EzmLength equ [esp + 4 + 8]
+
+; end of arguments
+
+CODE_ALIGNMENT
+cPublicProc _RtlZeroMemory ,2
+cPublicFpo 2,1
+
+;
+; Save the non-volatile registers that we will use, without the benefit of
+; a frame pointer. No exception handling in this routine.
+;
+
+ push edi
+
+;
+; Setup the registers for using REP STOS instruction to zero memory.
+;
+; edi -> memory to zero
+; ecx = number of 32-bits words to zero
+; edx = number of extra 8-bit bytes to zero at the end (0 - 3)
+; eax = zero (value to store in destination)
+; direction flag is clear for auto-increment
+;
+
+ mov edi,EzmDestination
+ mov ecx,EzmLength
+ xor eax,eax
+ cld
+
+ mov edx,ecx
+ and edx,ZERO_MEMORY_ALIGNMENT_MASK
+ shr ecx,ZERO_MEMORY_ALIGNMENT_LOG2
+
+
+;
+; If number of 32-bit words to zero is non-zero, then do it.
+;
+
+ rep stosd
+
+;
+; If number of extra 8-bit bytes to zero is non-zero, then do it. In either
+; case restore non-volatile registers and return.
+;
+
+ or ecx,edx
+ jnz @F
+ pop edi
+ stdRET _RtlZeroMemory
+@@:
+ rep stosb
+ pop edi
+ stdRET _RtlZeroMemory
+
+stdENDP _RtlZeroMemory
+
+ page , 132
+ subttl "RtlMoveMemory"
+;++
+;
+; VOID
+; RtlMoveMemory (
+; IN PVOID Destination,
+; IN PVOID Source OPTIONAL,
+; IN ULONG Length
+; )
+;
+; Routine Description:
+;
+; This function moves memory either forward or backward, aligned or
+; unaligned, in 4-byte blocks, followed by any remaining bytes.
+;
+; Arguments:
+;
+; Destination - Supplies a pointer to the destination of the move.
+;
+; Source - Supplies a pointer to the memory to move.
+;
+; Length - Supplies the Length, in bytes, of the memory to be moved.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+; Definitions of arguments
+; (TOS) = Return address
+
+EmmDestination equ [esp + 8 + 4]
+EmmSource equ [esp + 8 + 8]
+EmmLength equ [esp + 8 + 12]
+
+; End of arguments
+
+CODE_ALIGNMENT
+cPublicProc _RtlMoveMemory ,3
+cPublicFpo 3,2
+
+;
+; Save the non-volatile registers that we will use, without the benefit of
+; a frame pointer. No exception handling in this routine.
+;
+
+ push esi
+ push edi
+
+;
+; Setup the registers for using REP MOVS instruction to move memory.
+;
+; esi -> memory to move (NULL implies the destination will be zeroed)
+; edi -> destination of move
+; ecx = number of 32-bits words to move
+; edx = number of extra 8-bit bytes to move at the end (0 - 3)
+; direction flag is clear for auto-increment
+;
+
+ mov esi,EmmSource
+ mov edi,EmmDestination
+ mov ecx,EmmLength
+if DBG
+ inc _RtlpZeroCount
+ add _RtlpZeroBytes,ecx
+endif
+ cld
+
+ cmp esi,edi ; Special case if Source > Destination
+ jbe overlap
+
+nooverlap:
+ mov edx,ecx
+ and edx,MEMORY_ALIGNMENT_MASK
+ shr ecx,MEMORY_ALIGNMENT_LOG2
+
+;
+; If number of 32-bit words to move is non-zero, then do it.
+;
+
+ rep movsd
+
+;
+; If number of extra 8-bit bytes to move is non-zero, then do it. In either
+; case restore non-volatile registers and return.
+;
+
+ or ecx,edx
+ jnz @F
+ pop edi
+ pop esi
+ stdRET _RtlMoveMemory
+@@:
+ rep movsb
+
+movedone:
+ pop edi
+ pop esi
+ stdRET _RtlMoveMemory
+
+;
+; Here to handle special case when Source > Destination and therefore is a
+; potential overlapping move. If Source == Destination, then nothing to do.
+; Otherwise, increment the Source and Destination pointers by Length and do
+; the move backwards, a byte at a time.
+;
+
+overlap:
+ je movedone
+ mov eax,edi
+ sub eax,esi
+ cmp ecx,eax
+ jbe nooverlap
+
+ std
+ add esi,ecx
+ add edi,ecx
+ dec esi
+ dec edi
+ rep movsb
+ cld
+ jmp short movedone
+
+stdENDP _RtlMoveMemory
+
+_TEXT$00 ends
+ end
diff --git a/private/ntos/rtl/i386/nlssup.asm b/private/ntos/rtl/i386/nlssup.asm
new file mode 100644
index 000000000..3604542a7
--- /dev/null
+++ b/private/ntos/rtl/i386/nlssup.asm
@@ -0,0 +1,146 @@
+ TITLE "String support routines"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; stringsup.asm
+;
+; Abstract:
+;
+; This module implements suplimentary routines for performing string
+; operations.
+;
+; Author:
+;
+; Larry Osterman (larryo) 18-Sep-1991
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+
+.386p
+include callconv.inc ; calling convention macros
+
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "RtlAnsiCharToUnicodeChar"
+;++
+;
+; 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:
+;
+; (TOS+4) = 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.
+;
+; Note:
+;
+; This routine will have to be converted to use the proper unicode mapping
+; tables.
+;
+;--
+cPublicProc _RtlAnsiCharToUnicodeChar ,1
+cPublicFpo 1,2
+ push esi
+ mov esi, [esp+8]
+ push dword ptr [esi] ; Save the old input string
+ inc dword ptr [esi] ; Bump the input string
+ pop esi ; (ESI) = input string
+ xor eax, eax ; Zero out the EAX register.
+ lodsb ; Grab the first character from string
+ pop esi
+ stdRET _RtlAnsiCharToUnicodeChar
+
+stdENDP _RtlAnsiCharToUnicodeChar
+
+ page
+ subttl "RtlpAnsiPszToUnicodePsz"
+;++
+;
+; VOID
+; RtlpAnsiPszToUnicodePsz(
+; IN PCHAR AnsiString,
+; IN PWCHAR 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:
+;
+; (ESP+4) = AnsiString - Supplies a pointer to the ANSI string to convert to unicode.
+; (ESP+8) = UnicodeString - Supplies a pointer to a buffer to hold the unicode string
+; (ESP+12) = AnsiStringLength - Supplies the length of the ANSI string.
+;
+; Return Value:
+;
+; None.
+;
+;
+; Note:
+;
+; This routine will have to be converted to use the proper unicode mapping
+; tables.
+;--
+
+cPublicProc _RtlpAnsiPszToUnicodePsz ,3
+cPublicFpo 3,2
+ push esi
+ push edi
+ xor ecx, ecx
+ mov cx, [esp]+12+8
+ jecxz raptup9
+ mov esi, [esp]+4+8
+ mov edi, [esp]+8+8
+ xor ah, ah
+@@: lodsb
+ stosw
+ loop @b
+ xor eax, eax
+ stosw ; Don't forget to stick the null at end
+raptup9:pop edi
+ pop esi
+ stdRET _RtlpAnsiPszToUnicodePsz
+
+stdENDP _RtlpAnsiPszToUnicodePsz
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/nlstrans.asm b/private/ntos/rtl/i386/nlstrans.asm
new file mode 100644
index 000000000..188e79578
--- /dev/null
+++ b/private/ntos/rtl/i386/nlstrans.asm
@@ -0,0 +1,668 @@
+ title "NLS Translation"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; nlstrans.asm
+;
+; Abstract:
+;
+; This module implements the function to translate from Unicode
+; characters to ANSI and OEM characters. The translation is based on
+; the installed ACP and OEMCP.
+;
+; Author:
+;
+; Gregory Wilson 15 may 92
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+ .list
+
+_DATA SEGMENT DWORD PUBLIC 'DATA'
+
+ extrn _NlsUnicodeToAnsiData:DWORD
+ extrn _NlsUnicodeToMbAnsiData:DWORD
+ extrn _NlsMbCodePageTag:BYTE
+ extrn _NlsUnicodeToOemData:DWORD
+ extrn _NlsUnicodeToMbOemData:DWORD
+ extrn _NlsMbOemCodePageTag:BYTE
+
+_DATA ENDS
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING
+
+_MultiByteString$ equ 8
+_MaxBytesInMultiByteString$ equ 12
+_BytesInMultiByteString$ equ 16
+_UnicodeString$ equ 20
+_BytesInUnicodeString$ equ 24
+_LoopCount$ equ -4
+_MultiByteStringAnchor$ equ -8
+_CharsInUnicodeString$ equ -12
+
+ align 4
+
+ public _RtlUnicodeToMultiByteN
+
+; NTSTATUS
+; RtlUnicodeToMultiByteN(
+; OUT PCH MultiByteString,
+; IN ULONG MaxBytesInMultiByteString,
+; OUT PULONG BytesInMultiByteString OPTIONAL,
+; IN PWCH UnicodeString,
+; IN ULONG BytesInUnicodeString)
+;
+; /*++
+;
+; Routine Description:
+;
+; This functions converts the specified unicode source string into an
+; ansi string. The translation is done with respect to the
+; ANSI Code Page (ACP) loaded at boot time.
+;
+; Arguments:
+;
+; MultiByteString - Returns an ansi string that is equivalent to the
+; unicode source string. If the translation can not be done
+; because a character in the unicode string does not map to an
+; ansi character in the ACP, an error is returned.
+;
+; MaxBytesInMultiByteString - Supplies the maximum number of bytes to be
+; written to MultiByteString. If this causes MultiByteString to be a
+; truncated equivalent of UnicodeString, no error condition results.
+;
+; BytesInMultiByteString - Returns the number of bytes in the returned
+; ansi string pointed to by MultiByteString.
+;
+; UnicodeString - Supplies the unicode source string that is to be
+; converted to ansi.
+;
+; BytesInUnicodeString - The number of bytes in the the string pointed to by
+; UnicodeString.
+;
+; Return Value:
+;
+; SUCCESS - The conversion was successful
+;
+; --*/
+;
+_RtlUnicodeToMultiByteN proc
+ push ebp
+ mov ebp, esp
+ sub esp, 12
+ push ebx
+ ;
+ ; Save beginning position of MultiByteString ptr for later
+ ; use in calculating number of characters translated.
+ ;
+ mov eax, DWORD PTR _MultiByteString$[ebp]
+ mov DWORD PTR _MultiByteStringAnchor$[ebp], eax
+ ;
+ ; Convert BytesInUnicodeString to a character count and
+ ; compare against the maximum number of characters we have
+ ; room to translate. Use the minimum for the loop count.
+ ;
+ mov eax, DWORD PTR _BytesInUnicodeString$[ebp]
+ shr eax, 1
+ mov ecx, DWORD PTR _MaxBytesInMultiByteString$[ebp]
+ sub eax, ecx
+ sbb edx, edx
+ and eax, edx
+ add eax, ecx
+ mov DWORD PTR _LoopCount$[ebp], eax
+
+ ;
+ ; Set up registers such that:
+ ; ebx: UnicodeString
+ ; ecx: NlsUnicodeToAnsiData
+ ; edx: MultiByteString
+ ;
+ mov edx, DWORD PTR _MultiByteString$[ebp]
+ mov ebx, DWORD PTR _UnicodeString$[ebp]
+ mov ecx, DWORD PTR _NlsUnicodeToAnsiData
+ ;
+ ; Determine if we're dealing with SBCS or MBCS.
+ ;
+ cmp BYTE PTR _NlsMbCodePageTag, 0 ; 0 -> sbcs, 1 -> mbcs
+ jne $ACP_MBCS
+ ;
+ ; If the string to be translated does not contain a multiple
+ ; of 16 characters then figure out where to jump into the
+ ; translation loop to translate the left over characters first.
+ ; From then on the loop only deals with 16 characters at a time.
+ ;
+ and eax, 15
+ je SHORT $ACP_TopOfSBLoop ; already a multiple of 16 chars.
+
+ push eax ; save for indexing into jump table
+ sub DWORD PTR _LoopCount$[ebp], eax ; decrement LoopCount
+ add edx, eax ; increment MultiByteString ptr
+ lea eax, DWORD PTR [eax*2]
+ add ebx, eax ; increment UnicodeString ptr
+
+ ;
+ ; Use ACP_JumpTable to jump into the while loop at the appropriate
+ ; spot to take care of the *extra* characters.
+ ;
+ pop eax
+ dec eax
+ jmp DWORD PTR cs:$ACP_JumpTable[eax*4]
+
+ ;
+ ; Main translation loop. Translates 16 characters on each iteration.
+ ;
+$ACP_TopOfSBLoop:
+ cmp DWORD PTR _LoopCount$[ebp], 0
+ jbe $ACP_FinishedTranslation
+ ;
+ ; Adjust pointers for next iteration
+ ;
+ add edx, 16 ; increment MultiByteString ptr
+ add ebx, 32 ; increment UnicodeString ptr
+ sub DWORD PTR _LoopCount$[ebp], 16 ; decrement LoopCount
+
+ ;
+ ; begin translation
+ ;
+ movzx eax, WORD PTR [ebx-32]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-16], al
+$ACP_SBAdjust15:
+ movzx eax, WORD PTR [ebx-30]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-15], al
+$ACP_SBAdjust14:
+ movzx eax, WORD PTR [ebx-28]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-14], al
+$ACP_SBAdjust13:
+ movzx eax, WORD PTR [ebx-26]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-13], al
+$ACP_SBAdjust12:
+ movzx eax, WORD PTR [ebx-24]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-12], al
+$ACP_SBAdjust11:
+ movzx eax, WORD PTR [ebx-22]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-11], al
+$ACP_SBAdjust10:
+ movzx eax, WORD PTR [ebx-20]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-10], al
+$ACP_SBAdjust9:
+ movzx eax, WORD PTR [ebx-18]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-9], al
+$ACP_SBAdjust8:
+ movzx eax, WORD PTR [ebx-16]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-8], al
+$ACP_SBAdjust7:
+ movzx eax, WORD PTR [ebx-14]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-7], al
+$ACP_SBAdjust6:
+ movzx eax, WORD PTR [ebx-12]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-6], al
+$ACP_SBAdjust5:
+ movzx eax, WORD PTR [ebx-10]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-5], al
+$ACP_SBAdjust4:
+ movzx eax, WORD PTR [ebx-8]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-4], al
+$ACP_SBAdjust3:
+ movzx eax, WORD PTR [ebx-6]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-3], al
+$ACP_SBAdjust2:
+ movzx eax, WORD PTR [ebx-4]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-2], al
+$ACP_SBAdjust1:
+ movzx eax, WORD PTR [ebx-2]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-1], al
+
+ jmp $ACP_TopOfSBLoop
+
+ ;
+ ; The ACP is a multibyte code page. Translation is done here.
+ ;
+ ; WARNING!! WARNING!! No optimization has been done on this loop.
+ ;
+$ACP_MBCS:
+ mov eax, DWORD PTR _BytesInUnicodeString$[ebp]
+ shr eax, 1
+ mov DWORD PTR _CharsInUnicodeString$[ebp], eax
+ dec DWORD PTR _CharsInUnicodeString$[ebp]
+ or eax, eax
+ mov ecx, DWORD PTR _NlsUnicodeToMbAnsiData
+ je SHORT $ACP_FinishedTranslation
+$ACP_TopOfMBLoop:
+ ;
+ ; Check to make sure we have room in the destination string.
+ ;
+ mov eax, DWORD PTR _MaxBytesInMultiByteString$[ebp]
+ dec DWORD PTR _MaxBytesInMultiByteString$[ebp]
+ or eax, eax
+ je SHORT $ACP_FinishedTranslation
+ ;
+ ; Grab the multibyte character(s) from the translation table
+ ; and increment the source string pointer.
+ ;
+ mov eax, DWORD PTR _UnicodeString$[ebp]
+ movzx eax, WORD PTR [eax]
+ mov dx, WORD PTR [ecx+eax*2]
+ add DWORD PTR _UnicodeString$[ebp], 2
+ mov bl, dh
+ ;
+ ; Check for a lead byte.
+ ;
+ or bl, bl
+ je SHORT $ACP_NoLeadByte
+ ;
+ ; There is a lead byte. Make sure there's room in the
+ ; destination buffer for both the lead byte and trail byte.
+ ;
+ mov eax, DWORD PTR _MaxBytesInMultiByteString$[ebp]
+ dec DWORD PTR _MaxBytesInMultiByteString$[ebp]
+ or eax, eax
+ je SHORT $ACP_FinishedTranslation
+ ;
+ ; Store the lead byte in the destination buffer, increment
+ ; the destination pointer and decrement the count of remaining
+ ; space.
+ ;
+ mov eax, DWORD PTR _MultiByteString$[ebp]
+ mov BYTE PTR [eax], bl
+ inc DWORD PTR _MultiByteString$[ebp]
+ dec DWORD PTR _MaxBytesInMultiByteString$[ebp]
+ ;
+ ; Store the single byte or trail byte.
+ ;
+$ACP_NoLeadByte:
+ mov eax, DWORD PTR _MultiByteString$[ebp]
+ mov BYTE PTR [eax], dl
+ inc DWORD PTR _MultiByteString$[ebp]
+ ;
+ ; Check to see if there are any more characters to translate.
+ ;
+ mov eax, DWORD PTR _CharsInUnicodeString$[ebp]
+ dec DWORD PTR _CharsInUnicodeString$[ebp]
+ or eax, eax
+ jne SHORT $ACP_TopOfMBLoop
+ ;
+ ; We're finished translating for the multibyte case.
+ ; Set up edx so we can calculate the number of characters
+ ; written (if the user has requested it).
+ ;
+ mov edx, DWORD PTR _MultiByteString$[ebp]
+
+$ACP_FinishedTranslation:
+ mov eax, DWORD PTR _BytesInMultiByteString$[ebp]
+ or eax, eax
+ je SHORT $ACP_NoOptParam
+ sub edx, DWORD PTR _MultiByteStringAnchor$[ebp]
+ mov DWORD PTR [eax], edx
+$ACP_NoOptParam:
+ sub eax, eax
+ pop ebx
+ leave
+ ret 0 ; return STATUS_SUCCESS
+
+$ACP_JumpTable:
+ DD OFFSET FLAT:$ACP_SBAdjust1
+ DD OFFSET FLAT:$ACP_SBAdjust2
+ DD OFFSET FLAT:$ACP_SBAdjust3
+ DD OFFSET FLAT:$ACP_SBAdjust4
+ DD OFFSET FLAT:$ACP_SBAdjust5
+ DD OFFSET FLAT:$ACP_SBAdjust6
+ DD OFFSET FLAT:$ACP_SBAdjust7
+ DD OFFSET FLAT:$ACP_SBAdjust8
+ DD OFFSET FLAT:$ACP_SBAdjust9
+ DD OFFSET FLAT:$ACP_SBAdjust10
+ DD OFFSET FLAT:$ACP_SBAdjust11
+ DD OFFSET FLAT:$ACP_SBAdjust12
+ DD OFFSET FLAT:$ACP_SBAdjust13
+ DD OFFSET FLAT:$ACP_SBAdjust14
+ DD OFFSET FLAT:$ACP_SBAdjust15
+
+_RtlUnicodeToMultiByteN ENDP
+
+_OemString$ equ 8
+_MaxBytesInOemString$ equ 12
+_BytesInOemString$ equ 16
+_UnicodeString$ equ 20
+_BytesInUnicodeString$ equ 24
+_LoopCount$ equ -4
+_OemStringAnchor$ equ -8
+_CharsInUnicodeString$ equ -12
+
+ public _RtlUnicodeToOemN
+
+; NTSTATUS
+; RtlUnicodeToOemN(
+; OUT PCH OemString,
+; IN ULONG MaxBytesInOemString,
+; OUT PULONG BytesInOemString OPTIONAL,
+; IN PWCH UnicodeString,
+; IN ULONG BytesInUnicodeString)
+;
+; /*++
+;
+; Routine Description:
+;
+; This functions converts the specified unicode source string into an
+; oem string. The translation is done with respect to the OEM Code
+; Page (OCP) loaded at boot time.
+;
+; Arguments:
+;
+; OemString - Returns an oem string that is equivalent to the
+; unicode source string. If the translation can not be done
+; because a character in the unicode string does not map to an
+; oem character in the OCP, an error is returned.
+;
+; MaxBytesInOemString - Supplies the maximum number of bytes to be
+; written to OemString. If this causes OemString to be a
+; truncated equivalent of UnicodeString, no error condition results.
+;
+; BytesInOemString - Returns the number of bytes in the returned
+; oem string pointed to by OemString.
+;
+; UnicodeString - Supplies the unicode source string that is to be
+; converted to oem.
+;
+; BytesInUnicodeString - The number of bytes in the the string pointed to by
+; UnicodeString.
+;
+; Return Value:
+;
+; SUCCESS - The conversion was successful
+;
+; STATUS_BUFFER_OVERFLOW - MaxBytesInOemString was not enough to hold
+; the whole Oem string. It was converted correct to the point though.
+;
+; --*/
+_RtlUnicodeToOemN proc
+ push ebp
+ mov ebp, esp
+ sub esp, 12
+ push ebx
+ ;
+ ; Save beginning position of OemString ptr for later
+ ; use in calculating number of characters translated.
+ ;
+ mov eax, DWORD PTR _OemString$[ebp]
+ mov DWORD PTR _OemStringAnchor$[ebp], eax
+ ;
+ ; Convert BytesInUnicodeString to a character count and
+ ; compare against the maximum number of characters we have
+ ; room to translate. Use the minimum for the loop count.
+ ;
+ mov eax, DWORD PTR _BytesInUnicodeString$[ebp]
+ shr eax, 1
+ mov ecx, DWORD PTR _MaxBytesInOemString$[ebp]
+ sub eax, ecx
+ sbb edx, edx
+ and eax, edx
+ add eax, ecx
+ mov DWORD PTR _LoopCount$[ebp], eax
+ ;
+ ; Set up registers such that:
+ ; ebx: UnicodeString
+ ; ecx: NlsUnicodeToOemData
+ ; edx: OemString
+ ;
+ mov edx, DWORD PTR _OemString$[ebp]
+ mov ebx, DWORD PTR _UnicodeString$[ebp]
+ mov ecx, DWORD PTR _NlsUnicodeToOemData
+ ;
+ ; Determine if we're dealing with SBCS or MBCS.
+ ;
+ cmp BYTE PTR _NlsMbOemCodePageTag, 0 ; 0 -> sbcs, 1 -> mbcs
+ jne $OEMCP_MBCS
+ ;
+ ; If the string to be translated does not contain a multiple
+ ; of 16 characters then figure out where to jump into the
+ ; translation loop to translate the left over characters first.
+ ; From then on the loop only deals with 16 characters at a time.
+ ;
+ and eax, 15
+ je SHORT $OEMCP_TopOfSBLoop ; already a multiple of 16 chars.
+
+ push eax ; save for indexing into jump table
+ sub DWORD PTR _LoopCount$[ebp], eax ; decrement LoopCount
+ add edx, eax ; increment OemString ptr
+ lea eax, DWORD PTR [eax*2]
+ add ebx, eax ; increment UnicodeString ptr
+
+ ;
+ ; Use OEMCP_JumpTable to jump into the while loop at the appropriate
+ ; spot to take care of the *extra* characters.
+ ;
+ pop eax
+ dec eax
+ jmp DWORD PTR cs:$OEMCP_JumpTable[eax*4]
+
+ ;
+ ; Main translation loop. Translates 16 characters on each iteration.
+ ;
+$OEMCP_TopOfSBLoop:
+ cmp DWORD PTR _LoopCount$[ebp], 0
+ jbe $OEMCP_FinishedTranslation
+ ;
+ ; Adjust pointers for next iteration
+ ;
+ add edx, 16 ; increment OemString ptr
+ add ebx, 32 ; increment UnicodeString ptr
+ sub DWORD PTR _LoopCount$[ebp], 16 ; decrement LoopCount
+
+ ;
+ ; begin translation
+ ;
+ movzx eax, WORD PTR [ebx-32]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-16], al
+$OEMCP_SBAdjust15:
+ movzx eax, WORD PTR [ebx-30]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-15], al
+$OEMCP_SBAdjust14:
+ movzx eax, WORD PTR [ebx-28]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-14], al
+$OEMCP_SBAdjust13:
+ movzx eax, WORD PTR [ebx-26]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-13], al
+$OEMCP_SBAdjust12:
+ movzx eax, WORD PTR [ebx-24]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-12], al
+$OEMCP_SBAdjust11:
+ movzx eax, WORD PTR [ebx-22]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-11], al
+$OEMCP_SBAdjust10:
+ movzx eax, WORD PTR [ebx-20]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-10], al
+$OEMCP_SBAdjust9:
+ movzx eax, WORD PTR [ebx-18]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-9], al
+$OEMCP_SBAdjust8:
+ movzx eax, WORD PTR [ebx-16]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-8], al
+$OEMCP_SBAdjust7:
+ movzx eax, WORD PTR [ebx-14]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-7], al
+$OEMCP_SBAdjust6:
+ movzx eax, WORD PTR [ebx-12]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-6], al
+$OEMCP_SBAdjust5:
+ movzx eax, WORD PTR [ebx-10]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-5], al
+$OEMCP_SBAdjust4:
+ movzx eax, WORD PTR [ebx-8]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-4], al
+$OEMCP_SBAdjust3:
+ movzx eax, WORD PTR [ebx-6]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-3], al
+$OEMCP_SBAdjust2:
+ movzx eax, WORD PTR [ebx-4]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-2], al
+$OEMCP_SBAdjust1:
+ movzx eax, WORD PTR [ebx-2]
+ mov al, BYTE PTR [eax+ecx]
+ mov BYTE PTR [edx-1], al
+
+ jmp $OEMCP_TopOfSBLoop
+
+ ;
+ ; The OEMCP is a multibyte code page. Translation is done here.
+ ;
+ ; WARNING!! WARNING!! No optimization has been done on this loop.
+ ;
+$OEMCP_MBCS:
+ mov eax, DWORD PTR _BytesInUnicodeString$[ebp]
+ shr eax, 1
+ mov DWORD PTR _CharsInUnicodeString$[ebp], eax
+ dec DWORD PTR _CharsInUnicodeString$[ebp]
+ or eax, eax
+ mov ecx, DWORD PTR _NlsUnicodeToMbOemData
+ je SHORT $OEMCP_FinishedTranslation
+$OEMCP_TopOfMBLoop:
+ ;
+ ; Check to make sure we have room in the destination string.
+ ;
+ mov eax, DWORD PTR _MaxBytesInOemString$[ebp]
+ dec DWORD PTR _MaxBytesInOemString$[ebp]
+ or eax, eax
+ je SHORT $OEMCP_FinishedTranslation
+ ;
+ ; Grab the multibyte character(s) from the translation table
+ ; and increment the source string pointer.
+ ;
+ mov eax, DWORD PTR _UnicodeString$[ebp]
+ movzx eax, WORD PTR [eax]
+ mov dx, WORD PTR [ecx+eax*2]
+ add DWORD PTR _UnicodeString$[ebp], 2
+ mov bl, dh
+ ;
+ ; Check for a lead byte.
+ ;
+ or bl, bl
+ je SHORT $OEMCP_NoLeadByte
+ ;
+ ; There is a lead byte. Make sure there's room in the
+ ; destination buffer for both the lead byte and trail byte.
+ ;
+ mov eax, DWORD PTR _MaxBytesInOemString$[ebp]
+ dec DWORD PTR _MaxBytesInOemString$[ebp]
+ or eax, eax
+ je SHORT $OEMCP_FinishedTranslation
+ ;
+ ; Store the lead byte in the destination buffer, increment
+ ; the destination pointer and decrement the count of remaining
+ ; space.
+ ;
+ mov eax, DWORD PTR _OemString$[ebp]
+ mov BYTE PTR [eax], bl
+ inc DWORD PTR _OemString$[ebp]
+ dec DWORD PTR _MaxBytesInOemString$[ebp]
+ ;
+ ; Store the single byte or trail byte.
+ ;
+$OEMCP_NoLeadByte:
+ mov eax, DWORD PTR _OemString$[ebp]
+ mov BYTE PTR [eax], dl
+ inc DWORD PTR _OemString$[ebp]
+ ;
+ ; Check to see if there are any more characters to translate.
+ ;
+ mov eax, DWORD PTR _CharsInUnicodeString$[ebp]
+ dec DWORD PTR _CharsInUnicodeString$[ebp]
+ or eax, eax
+ jne SHORT $OEMCP_TopOfMBLoop
+ ;
+ ; We're finished translating for the multibyte case.
+ ; Set up edx so we can calculate the number of characters
+ ; written (if the user has requested it).
+ ;
+ mov edx, DWORD PTR _OemString$[ebp]
+
+$OEMCP_FinishedTranslation:
+ mov eax, DWORD PTR _BytesInOemString$[ebp]
+ or eax, eax
+ je SHORT $OEMCP_NoOptParam
+ sub edx, DWORD PTR _OemStringAnchor$[ebp]
+ mov DWORD PTR [eax], edx
+$OEMCP_NoOptParam:
+ ;
+ ; If we ran out of space in the destination buffer before
+ ; translating all of the Unicode characters then return
+ ; STATUS_BUFFER_OVERFLOW. Check is done by looking at
+ ; # of chars in Unicode string left to translate.
+ ;
+;
+; WARNING!
+;
+; we can't check CharsInUnicodeString since we determined the loop
+; count above and don't modify CharsInUnicodeString anymore...
+;
+; cmp DWORD PTR _CharsInUnicodeString$[ebp], 1
+; cmc
+; sbb eax, eax
+; and eax, -2147483643 ; STATUS_BUFFER_OVERFLOW (80000005H)
+ sub eax, eax ; return STATUS_SUCCESS
+ pop ebx
+ leave
+ ret 0
+
+$OEMCP_JumpTable:
+ DD OFFSET FLAT:$OEMCP_SBAdjust1
+ DD OFFSET FLAT:$OEMCP_SBAdjust2
+ DD OFFSET FLAT:$OEMCP_SBAdjust3
+ DD OFFSET FLAT:$OEMCP_SBAdjust4
+ DD OFFSET FLAT:$OEMCP_SBAdjust5
+ DD OFFSET FLAT:$OEMCP_SBAdjust6
+ DD OFFSET FLAT:$OEMCP_SBAdjust7
+ DD OFFSET FLAT:$OEMCP_SBAdjust8
+ DD OFFSET FLAT:$OEMCP_SBAdjust9
+ DD OFFSET FLAT:$OEMCP_SBAdjust10
+ DD OFFSET FLAT:$OEMCP_SBAdjust11
+ DD OFFSET FLAT:$OEMCP_SBAdjust12
+ DD OFFSET FLAT:$OEMCP_SBAdjust13
+ DD OFFSET FLAT:$OEMCP_SBAdjust14
+ DD OFFSET FLAT:$OEMCP_SBAdjust15
+
+_RtlUnicodeToOemN ENDP
+
+_TEXT ENDS
+ end
diff --git a/private/ntos/rtl/i386/ntcurteb.asm b/private/ntos/rtl/i386/ntcurteb.asm
new file mode 100644
index 000000000..1e59478ad
--- /dev/null
+++ b/private/ntos/rtl/i386/ntcurteb.asm
@@ -0,0 +1,79 @@
+ title "NtCurTeb.asm"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; NtCurTeb.asm
+;
+; Abstract:
+;
+; Efficient NtCurrentTeb code.
+;
+; Author:
+;
+; Bryan Willman (bryanwi) 28 feb 90
+;
+; Environment:
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+ page ,132
+ subttl "NtCurrentTeb"
+
+IFDEF NTOS_KERNEL_RUNTIME
+.PAGE SEGMENT DWORD PUBLIC 'CODE'
+ELSE
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ENDIF
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;++
+;
+; PTEB
+; NtCurrentTeb();
+;
+; Routine Description:
+;
+; This routine returns the address of the current TEB.
+;
+; Arguments:
+;
+; None
+;
+; Return Value:
+;
+; Address of TEB.
+;
+;--
+cPublicProc _NtCurrentTeb ,0
+cPublicFpo 0,0
+
+;
+; How this works in both user and kernel mode.
+;
+; In user mode, TEB.TIB.Self is flat address of containing structure.
+; In kernel mode, PCR.TIB.Self is flat address of the TEB.
+; Same offset both places, so fs:PcTeb is always the flat address
+; of the TEB.
+;
+
+ mov eax,fs:[PcTeb]
+ stdRET _NtCurrentTeb
+
+stdENDP _NtCurrentTeb
+IFDEF NTOS_KERNEL_RUNTIME
+.PAGE ENDS
+ELSE
+_TEXT ENDS
+ENDIF
+ end
diff --git a/private/ntos/rtl/i386/ntrtl386.h b/private/ntos/rtl/i386/ntrtl386.h
new file mode 100644
index 000000000..88e69b505
--- /dev/null
+++ b/private/ntos/rtl/i386/ntrtl386.h
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ntrtl386.h
+
+Abstract:
+
+ i386 specific parts of ntrtlp.h
+
+Author:
+
+ Bryan Willman 10 April 90
+
+Environment:
+
+ These routines are statically linked in the caller's executable and
+ are callable in either kernel mode or user mode.
+
+Revision History:
+
+--*/
+
+//
+// Exception handling procedure prototypes.
+//
+VOID
+RtlpCaptureContext (
+ OUT PCONTEXT ContextRecord
+ );
+
+VOID
+RtlpUnlinkHandler (
+ PEXCEPTION_REGISTRATION_RECORD UnlinkPointer
+ );
+
+PEXCEPTION_REGISTRATION_RECORD
+RtlpGetRegistrationHead (
+ VOID
+ );
+
+PVOID
+RtlpGetReturnAddress (
+ VOID
+ );
+
+
+//
+// Record dump procedures.
+//
+
+VOID
+RtlpContextDump(
+ IN PVOID Object,
+ IN ULONG Control OPTIONAL
+ );
+
+VOID
+RtlpExceptionReportDump(
+ IN PVOID Object,
+ IN ULONG Control OPTIONAL
+ );
+
+VOID
+RtlpExceptionRegistrationDump(
+ IN PVOID Object,
+ IN ULONG Control OPTIONAL
+ );
diff --git a/private/ntos/rtl/i386/raise.asm b/private/ntos/rtl/i386/raise.asm
new file mode 100644
index 000000000..18f928fed
--- /dev/null
+++ b/private/ntos/rtl/i386/raise.asm
@@ -0,0 +1,181 @@
+ title "Raise Exception"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; raise.asm
+;
+; Abstract:
+;
+; This module implements the function to raise a software exception.
+;
+; Author:
+;
+; Bryan Willman 11 april 90
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+ EXTRNP _ZwRaiseException,3
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+;
+; Context flags definition.
+;
+
+CONTEXT_SETTING EQU CONTEXT_INTEGER OR CONTEXT_CONTROL OR CONTEXT_SEGMENTS
+
+
+;
+; Exception record length definition.
+;
+
+EXCEPTION_RECORD_LENGTH EQU (ErExceptionInformation + 16) AND 0fffffff0H
+
+ page
+ subttl "Raise Software Exception"
+;++
+;
+; VOID
+; RtlRaiseException (
+; IN PEXCEPTION_RECORD ExceptionRecord
+; )
+;
+; Routine Description:
+;
+; This function raises a software exception by building a context record,
+; establishing the stack limits of the current processor mode, and calling
+; the exception dispatcher. If the exception dispatcher finds a handler
+; to process the exception, then control is returned to the caller using
+; the NtContinue system service. Otherwise the NtLastChance system service
+; is called to provide default handing.
+;
+; N.B. On the 386, floating point state is not defined for non-fp
+; exceptions. Therefore, this routine does not attempt to
+; capture it.
+;
+; This means this routine cannot be used to report fp exceptions.
+;
+; Arguments:
+;
+; ExceptionRecord (ebp+8) - Supplies a pointer to an exception record.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _RtlRaiseException ,1
+
+ push ebp
+ mov ebp,esp
+ pushfd ; save flags before sub
+ sub esp,ContextFrameLength ; Allocate a context record
+
+;
+; Save regs we use in context record
+;
+
+ mov [(ebp-ContextFrameLength-4)+CsEax],eax
+ mov [(ebp-ContextFrameLength-4)+CsEcx],ecx
+
+;
+; Get pointer to exception report record, and set the exceptionaddress
+; field to be our return address
+;
+
+ mov eax,[ebp+8] ; (eax) -> ExceptionReportRecord
+
+ mov ecx,[ebp+4]
+ mov [eax.ErExceptionAddress],ecx
+
+;
+; Copy machine context into the context record
+;
+
+
+ lea eax,[ebp-ContextFrameLength-4] ; (eax) -> Context record
+
+ mov [eax.CsEip],ecx
+
+ mov [eax.CsEbx],ebx
+ mov [eax.CsEdx],edx
+
+ mov [eax.CsEsi],esi
+ mov [eax.CsEdi],edi
+
+;
+; context record's ESP must have the argument popped off the stack
+;
+
+ lea ecx,[ebp+12]
+
+ mov [eax.CsEsp],ecx
+
+ mov ecx,[ebp]
+ mov [eax.CsEbp],ecx
+
+ mov ecx,[ebp-4]
+ mov [eax.CsEflags],ecx
+
+ mov dword ptr [eax.CsSegCs],cs
+ mov dword ptr [eax.CsSegDs],ds
+ mov dword ptr [eax.CsSegEs],es
+ mov dword ptr [eax.CsSegFs],fs
+ mov dword ptr [eax.CsSegGs],gs
+ mov dword ptr [eax.CsSegSs],ss
+
+;
+; Set Context flags, note that FLOATING_POINT is NOT set.
+;
+
+ mov dword ptr [eax.CsContextFlags],CONTEXT_SETTING
+
+;
+; _ZwRaiseException(ExceptionRecord, ContextRecord, FirstChance=TRUE)
+;
+
+; 1 - TRUE
+; eax - Context Record
+; [ebp+8] - Exception Report Record
+
+ stdCall _ZwRaiseException,<[ebp+8],eax,1>
+
+;
+; We came back, suggesting some sort of error in the call. Raise
+; a status exception to report this, return from ZwRaiseException is type.
+;
+
+ sub esp,EXCEPTION_RECORD_LENGTH ; allocate record on stack, esp is base
+ mov [esp.ErExceptionCode],eax ; set exception type
+ or dword ptr [esp.ErExceptionFlags],EXCEPTION_NONCONTINUABLE
+ mov dword ptr [esp.ErNumberParameters],0 ; no parms
+ mov eax,[ebp+8]
+ mov [esp.ErExceptionRecord],eax ; point back to first exception
+ mov eax,esp
+ stdCall _RtlRaiseException,<eax>
+
+;
+; We will never come here, because RtlRaiseException will not allow
+; return if exception is non-continuable.
+;
+
+stdENDP _RtlRaiseException
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/raisests.c b/private/ntos/rtl/i386/raisests.c
new file mode 100644
index 000000000..d58f5f09d
--- /dev/null
+++ b/private/ntos/rtl/i386/raisests.c
@@ -0,0 +1,65 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ raisests.c
+
+Abstract:
+
+ This module implements the routine that raises an exception given a
+ specific status value.
+
+Author:
+
+ David N. Cutler (davec) 8-Aug-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+VOID
+RtlRaiseStatus (
+ IN NTSTATUS Status
+ )
+
+/*++
+
+Routine Description:
+
+ This function raises an exception with the specified status value. The
+ exception is marked as continuable 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.
+
+--*/
+
+{
+
+ EXCEPTION_RECORD ExceptionRecord;
+
+ //
+ // Construct an exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = Status;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ RtlRaiseException(&ExceptionRecord);
+ return;
+}
diff --git a/private/ntos/rtl/i386/rtldump.c b/private/ntos/rtl/i386/rtldump.c
new file mode 100644
index 000000000..a75c27353
--- /dev/null
+++ b/private/ntos/rtl/i386/rtldump.c
@@ -0,0 +1,185 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ rtldump.c
+
+Abstract:
+
+ This module implements dump procedures for:
+
+ ContextRecords,
+ ExceptionReportRecords,
+ ExceptionRegistrationRecords
+
+Author:
+
+ Bryan Willman (bryanwi) 12 April 90
+
+Environment:
+
+ Callable in any mode in which DbgPrint works.
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+VOID
+RtlpContextDump(
+ IN PVOID Object,
+ IN ULONG Control OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the contents of a context record.
+
+ Currently, it does not dump floating point context.
+
+Arguments:
+
+ Object - Address of the record to dump.
+
+ Control - Ignored, here so we look like a standard dump procedure.
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ PCONTEXT Context;
+
+ Context = (PCONTEXT)Object;
+
+ DbgPrint(" Record @ %lx\n", (ULONG)Context);
+ DbgPrint(" ContextFlags: %lx\n", Context->ContextFlags);
+ DbgPrint("\n");
+
+ DbgPrint(" SegGs: %lx\n", Context->SegGs);
+ DbgPrint(" SegFs: %lx\n", Context->SegFs);
+ DbgPrint(" SegEs: %lx\n", Context->SegEs);
+ DbgPrint(" SegDs: %lx\n", Context->SegDs);
+ DbgPrint("\n");
+
+ DbgPrint(" Edi: %lx\n", Context->Edi);
+ DbgPrint(" Esi: %lx\n", Context->Esi);
+ DbgPrint(" Ebx: %lx\n", Context->Ebx);
+ DbgPrint(" Edx: %lx\n", Context->Edx);
+ DbgPrint(" Ecx: %lx\n", Context->Ecx);
+ DbgPrint(" Eax: %lx\n", Context->Eax);
+ DbgPrint("\n");
+
+ DbgPrint(" Ebp: %lx\n", Context->Ebp);
+ DbgPrint(" Eip: %lx\n", Context->Eip);
+ DbgPrint(" SegCs: %lx\n", Context->SegCs);
+ DbgPrint(" EFlags: %lx\n", Context->EFlags);
+ DbgPrint(" Esp: %lx\n", Context->Esp);
+ DbgPrint(" SegSs: %lx\n", Context->SegSs);
+ DbgPrint("\n");
+
+ return;
+}
+
+
+
+VOID
+RtlpExceptionReportDump(
+ IN PVOID Object,
+ IN ULONG Control OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the contents of an Exception report record.
+
+Arguments:
+
+ Object - Address of the record to dump.
+
+ Control - Ignored, here so we look like a standard dump procedure.
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ ULONG i;
+
+ PEXCEPTION_RECORD Exception;
+
+ Exception = (PEXCEPTION_RECORD)Object;
+
+ DbgPrint(" Record @ %lx\n", (ULONG)Exception);
+ DbgPrint(" ExceptionCode: %lx\n", Exception->ExceptionCode);
+ DbgPrint(" ExceptionFlags: %lx\n", Exception->ExceptionFlags);
+ DbgPrint(" ExceptionRecord: %lx\n", Exception->ExceptionRecord);
+ DbgPrint(" ExceptionAddress: %lx\n", Exception->ExceptionAddress);
+ DbgPrint(" NumberParameters: %lx\n", Exception->NumberParameters);
+ for (i = 0; i < Exception->NumberParameters; i++)
+ DbgPrint("ExceptionInformation[%d]: %lx\n",
+ i, Exception->ExceptionInformation[i]);
+ DbgPrint("\n");
+ return;
+}
+
+
+
+VOID
+RtlpExceptionRegistrationDump(
+ IN PVOID Object,
+ IN ULONG Control OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the contents of an exception registration record,
+ unless Object == NULL, in which case it dumps the entire registration
+ chain.
+
+ Currently, it does not dump floating point context.
+
+Arguments:
+
+ Object - Address of the record to dump.
+
+ Control - Ignored, here so we look like a standard dump procedure.
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ PEXCEPTION_REGISTRATION_RECORD RegistrationPointer;
+
+ RegistrationPointer = (PEXCEPTION_REGISTRATION_RECORD)Object;
+
+ if (RegistrationPointer != EXCEPTION_CHAIN_END) {
+ DbgPrint("Record @ %lx\n", (ULONG)RegistrationPointer);
+ DbgPrint(" Next: %lx\n", RegistrationPointer->Next);
+ DbgPrint("Handler: %lx\n", RegistrationPointer->Handler);
+ DbgPrint("\n");
+ } else {
+ RegistrationPointer = RtlpGetRegistrationHead();
+
+ while (RegistrationPointer != EXCEPTION_CHAIN_END) {
+ DbgPrint("Record @ %lx\n", (ULONG)RegistrationPointer);
+ DbgPrint(" Next: %lx\n", RegistrationPointer->Next);
+ DbgPrint("Handler: %lx\n", RegistrationPointer->Handler);
+ DbgPrint("\n");
+ }
+ }
+ return;
+}
diff --git a/private/ntos/rtl/i386/stkwalk.asm b/private/ntos/rtl/i386/stkwalk.asm
new file mode 100644
index 000000000..cf1ea134b
--- /dev/null
+++ b/private/ntos/rtl/i386/stkwalk.asm
@@ -0,0 +1,221 @@
+ TITLE "Capture Stack Back Trace"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; getcalr.s
+;
+; Abstract:
+;
+; This module implements the routine RtlCaptureStackBackTrace. It will
+; walker the stack back trace and capture a portion of it.
+;
+; Author:
+;
+; Steve Wood (stevewo) 29-Jan-1992
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+IFDEF NTOS_KERNEL_RUNTIME
+ EXTRNP _MmIsAddressValid,1
+ EXTRNP _KeGetCurrentIrql,0,IMPORT
+ENDIF
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "RtlCaptureStackBackTrace"
+;++
+;
+; USHORT
+; RtlCaptureStackBackTrace(
+; IN ULONG FramesToSkip,
+; IN ULONG FramesToCapture,
+; OUT PVOID *BackTrace,
+; OUT PULONG BackTraceHash
+; )
+;
+; Routine Description:
+;
+; This routine walks up the stack frames, capturing the return address from
+; each frame requested.
+;
+;
+; Arguments:
+;
+; OUT PVOID BackTrace (eps+0x10) - Returns the caller of the caller.
+;
+;
+; Return Value:
+;
+; Number of return addresses returned in the BackTrace buffer.
+;
+;
+;--
+RcbtFramesToSkip EQU [ebp+08h]
+RcbtFramesToCapture EQU [ebp+0Ch]
+RcbtBackTrace EQU [ebp+010h]
+RcbtBackTraceHash EQU [ebp+014h]
+
+IFDEF NTOS_KERNEL_RUNTIME
+RcbtInitialStack EQU [ebp-10h]
+ENDIF
+
+cPublicProc _RtlCaptureStackBackTrace ,4
+IFDEF NTOS_KERNEL_RUNTIME
+IF FPO
+ xor eax,eax
+ stdRET _RtlCaptureStackBackTrace
+ENDIF
+ push ebp
+ mov ebp, esp
+ push ebx ; Save EBX
+ push esi ; Save ESI
+ push edi ; Save EDI
+ mov eax, PCR[PcPrcbData+PbCurrentThread] ; (eax)->current thread
+ push ThInitialStack[eax] ; RcbtInitialStack = base of kernel stack
+ mov esi,RcbtBackTraceHash ; (ESI) -> where to accumulate hash sum
+ mov edi,RcbtBackTrace ; (EDI) -> where to put backtrace
+ mov edx,ebp ; (EDX) = current frame pointer
+ mov ecx,RcbtFramesToSkip ; (ECX) = number of frames to skip
+ jecxz RcbtSkipLoopDone ; Done if nothing to skip
+RcbtSkipLoop:
+ mov edx,[edx] ; (EDX) = next frame pointer
+ cmp edx,ebp ; If outside stack limits,
+ jbe RcbtCheckSkipU ; ...then exit
+ cmp edx,RcbtInitialStack
+ jae RcbtCheckSkipU
+ loop RcbtSkipLoop
+RcbtSkipLoopDone:
+ mov ecx,RcbtFramesToCapture ; (ECX) = number of frames to capture
+ jecxz RcbtExit ; Bail out if nothing to capture
+RcbtCaptureLoop:
+ mov eax,[edx].4 ; Get next return address
+ stosd ; Store it in the callers buffer
+ add [esi],eax ; Accumulate hash sum
+ mov edx,[edx] ; (EDX) = next frame pointer
+ cmp edx,ebp ; If outside stack limits,
+ jbe RcbtCheckCaptureU ; ...then exit
+ cmp edx,RcbtInitialStack
+ jae RcbtCheckCaptureU
+ loop RcbtCaptureLoop ; Otherwise get next frame
+RcbtExit:
+ mov eax,edi ; (EAX) -> next unused dword in buffer
+ sub eax,RcbtBackTrace ; (EAX) = number of bytes stored
+ shr eax,2 ; (EAX) = number of dwords stored
+ pop edi ; discard RcbtInitialStack
+ pop edi ; Restore EDI
+ pop esi ; Restore ESI
+ pop ebx ; Restore EBX
+ pop ebp
+ stdRET _RtlCaptureStackBackTrace
+
+RcbtCheckSkipU:
+ stdCall _KeGetCurrentIrql ; (al) = CurrentIrql
+ cmp al, 0 ; Bail out if not at IRQL 0
+ ja RcbtExit
+ mov ebx, PCR[PcPrcbData+PbCurrentThread] ; (ebx)->current thread
+ cmp byte ptr ThApcStateIndex[ebx],1 ; Bail out if attached.
+ je RcbtExit
+ mov ebx, ThTeb[ebx] ; (EBX) -> User mode TEB
+ or ebx, ebx
+ jnz @F
+ jmp short RcbtExit
+RcbtSkipLoopU:
+ mov edx,[edx] ; (EDX) = next frame pointer
+@@:
+ cmp edx,PcStackLimit[ebx] ; If outside stack limits,
+ jbe RcbtExit ; ...then exit
+ cmp edx,PcInitialStack[ebx]
+ jae RcbtExit
+ loop RcbtSkipLoopU
+ mov ecx,RcbtFramesToCapture ; (ECX) = number of frames to capture
+ jecxz RcbtExit ; Bail out if nothing to capture
+RcbtCaptureLoopU:
+ mov eax,[edx].4 ; Get next return address
+ stosd ; Store it in the callers buffer
+ add [esi],eax ; Accumulate hash sum
+ mov edx,[edx] ; (EDX) = next frame pointer
+@@:
+ cmp edx,PcStackLimit[ebx] ; If outside stack limits,
+ jbe RcbtExit ; ...then exit
+ cmp edx,PcInitialStack[ebx]
+ jae RcbtExit
+ loop RcbtCaptureLoopU ; Otherwise get next frame
+ jmp short RcbtExit
+
+RcbtCheckCaptureU:
+ stdCall _KeGetCurrentIrql ; (al) = CurrentIrql
+ cmp al, 0 ; Bail out if not at IRQL 0
+ ja RcbtExit
+ mov ebx, PCR[PcPrcbData+PbCurrentThread] ; (ebx)->current thread
+ cmp byte ptr ThApcStateIndex[ebx],1 ; Bail out if attached.
+ je RcbtExit
+ mov ebx, ThTeb[ebx] ; (EBX) -> User mode TEB
+ or ebx, ebx
+ jnz @B
+ jmp RcbtExit ; Bail out if TEB == NULL
+
+ELSE
+;
+; This is defined always for user mode code, although it can be
+; unreliable if called from code compiled with FPO enabled.
+;
+ push ebp
+ mov ebp, esp
+ push esi ; Save ESI
+ push edi ; Save EDI
+ mov esi,RcbtBackTraceHash ; (ESI) -> where to accumulate hash sum
+ mov edi,RcbtBackTrace ; (EDI) -> where to put backtrace
+ mov edx,ebp ; (EDX) = current frame pointer
+ mov ecx,RcbtFramesToSkip ; (ECX) = number of frames to skip
+ jecxz RcbtSkipLoopDone ; Done if nothing to skip
+RcbtSkipLoop:
+ mov edx,[edx] ; (EDX) = next frame pointer
+ cmp edx,fs:PcStackLimit ; If outside stack limits,
+ jbe RcbtExit ; ...then exit
+ cmp edx,fs:PcInitialStack
+ jae RcbtExit
+ loop RcbtSkipLoop
+RcbtSkipLoopDone:
+ mov ecx,RcbtFramesToCapture ; (ECX) = number of frames to capture
+ jecxz RcbtExit ; Bail out if nothing to capture
+RcbtCaptureLoop:
+ mov eax,[edx].4 ; Get next return address
+ stosd ; Store it in the callers buffer
+ add [esi],eax ; Accumulate hash sum
+ mov edx,[edx] ; (EDX) = next frame pointer
+ cmp edx,fs:PcStackLimit ; If outside stack limits,
+ jbe RcbtExit ; ...then exit
+ cmp edx,fs:PcInitialStack
+ jae RcbtExit
+ loop RcbtCaptureLoop ; Otherwise get next frame
+RcbtExit:
+ mov eax,edi ; (EAX) -> next unused dword in buffer
+ sub eax,RcbtBackTrace ; (EAX) = number of bytes stored
+ shr eax,2 ; (EAX) = number of dwords stored
+ pop edi ; Restore EDI
+ pop esi ; Restore ESI
+ pop ebp
+ stdRET _RtlCaptureStackBackTrace
+ENDIF
+
+stdENDP _RtlCaptureStackBackTrace
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/stringsp.asm b/private/ntos/rtl/i386/stringsp.asm
new file mode 100644
index 000000000..e722f1808
--- /dev/null
+++ b/private/ntos/rtl/i386/stringsp.asm
@@ -0,0 +1,175 @@
+ TITLE "String support routines"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; stringsup.asm
+;
+; Abstract:
+;
+; This module implements suplimentary routines for performing string
+; operations.
+;
+; Author:
+;
+; Larry Osterman (larryo) 18-Sep-1991
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+;--
+
+.386p
+
+include callconv.inc ; calling convention macros
+
+
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page ,132
+ subttl "RtlInitAnsiString"
+;++
+;
+; 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:
+;
+; (TOS+4) = DestinationString - Pointer to the counted string to initialize
+;
+; (TOS+8) = SourceString - Optional pointer to a null terminated string that
+; the counted string is to point to.
+;
+;
+; Return Value:
+;
+; None.
+;
+; NOTE:
+; This routine assumes that the string is less than 64K in size.
+;
+;--
+
+cPublicProc _RtlInitString ,2
+cPublicFpo 2,2
+ push edi
+ mov edi,[esp]+8+4 ; (edi)= SourceString
+ mov edx,[esp]+4+4 ; (esi)= DestinationString
+ mov DWORD PTR [edx], 0 ; (Destination).Length = (Destination).MaximumLength = 0
+ mov DWORD PTR [edx]+4, edi ; (Destination).Buffer = 0
+ or edi, edi
+ jz @f
+ or ecx, -1
+ xor eax, eax
+ repne scasb
+ not ecx
+ mov [edx]+2, cx ; Save maximum length
+ dec ecx
+ mov [edx], cx ; Save length
+@@: pop edi
+ stdRET _RtlInitString
+
+stdENDP _RtlInitString
+
+
+cPublicProc _RtlInitAnsiString ,2
+cPublicFpo 2,2
+ push edi
+ mov edi,[esp]+8+4 ; (edi)= SourceString
+ mov edx,[esp]+4+4 ; (esi)= DestinationString
+ mov DWORD PTR [edx], 0 ; (Destination).Length = (Destination).MaximumLength = 0
+ mov DWORD PTR [edx]+4, edi ; (Destination).Buffer = 0
+ or edi, edi
+ jz @f
+ or ecx, -1
+ xor eax, eax
+ repne scasb
+ not ecx
+ mov [edx]+2, cx ; Save maximum length
+ dec ecx
+ mov [edx], cx ; Save length
+@@: pop edi
+ stdRET _RtlInitAnsiString
+
+stdENDP _RtlInitAnsiString
+
+
+ page
+ subttl "RtlInitAnsiString"
+;++
+;
+; VOID
+; RtlInitAnsiString(
+; OUT PANSI_STRING DestinationString,
+; IN PSZ SourceString OPTIONAL
+; )
+;
+;
+; Routine Description:
+;
+; The RtlInitUnicodeString 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:
+;
+; (TOS+4) = DestinationString - Pointer to the counted string to initialize
+;
+; (TOS+8) = SourceString - Optional pointer to a null terminated string that
+; the counted string is to point to.
+;
+;
+; Return Value:
+;
+; None.
+;
+; NOTE:
+; This routine assumes that the string is less than 64K in size.
+;
+;--
+
+cPublicProc _RtlInitUnicodeString ,2
+cPublicFpo 2,2
+ push edi
+ mov edi,[esp]+8+4 ; (edi)= SourceString
+ mov edx,[esp]+4+4 ; (esi)= DestinationString
+ mov DWORD PTR [edx], 0 ; (Destination).Length = (Destination).MaximumLength = 0
+ mov DWORD PTR [edx]+4, edi ; (Destination).Buffer = 0
+ or edi, edi
+ jz @f
+ or ecx, -1
+ xor eax, eax
+ repne scasw
+ not ecx
+ shl ecx,1
+ mov [edx]+2, cx ; Save maximum length
+ dec ecx
+ dec ecx
+ mov [edx], cx ; Save length
+@@: pop edi
+ stdRET _RtlInitUnicodeString
+
+stdENDP _RtlInitUnicodeString
+
+_TEXT ends
+ end
diff --git a/private/ntos/rtl/i386/userdisp.asm b/private/ntos/rtl/i386/userdisp.asm
new file mode 100644
index 000000000..88871f0ee
--- /dev/null
+++ b/private/ntos/rtl/i386/userdisp.asm
@@ -0,0 +1,324 @@
+ title "User Mode Dispatcher Code"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; userdisp.asm
+;
+; Abstract:
+;
+; The module contains procedures to do user mode dispatching
+; ("trampolining") of user apcs and user exceptions.
+;
+; Author:
+;
+; Bryan M Willman (bryanwi) 31-Aug-90
+;
+; Environment:
+;
+; User mode.
+;
+; Revision History:
+;
+;--
+.386p
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+ifndef WX86_i386
+ EXTRNP _ZwCallbackReturn,3
+endif
+
+ EXTRNP _ZwContinue,2
+ EXTRNP _RtlDispatchException,2
+ EXTRNP _RtlRaiseStatus,1
+ EXTRNP _ZwRaiseException,3
+ EXTRNP _RtlRaiseException,1
+;
+; Exception record size definition.
+;
+
+ExceptionRecordSize = (ErNumberParameters + 4 + 3) AND 0fffffffcH ;
+
+ page ,132
+_TEXT SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ifndef WX86_i386
+ page
+ subttl "User APC Dispatcher"
+;++
+;
+; VOID
+; KiUserApcDispatcher (
+; IN PKNORMAL_ROUTINE NormalRoutine,
+; IN PVOID NormalContext,
+; IN PVOID SystemArgument1,
+; IN PVOID SystemArgument2,
+; IN CONTEXT ContinueContext
+; )
+;
+; 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:
+;
+; NormalRoutine - Supplies that address of the function that is to be called.
+;
+; NormalContext] - Supplies the normal context parameter that was specified
+; when the APC was initialized.
+;
+; SystemArgument1 - Supplies the first argument that was provied by the
+; executive when the APC was queued.
+;
+; SystemArgument2 - Supplies the second argument that was provided by
+; the executive when the APC was queued.
+;
+; ContinueContext - Context record to pass to Continue call.
+;
+;
+; Return Value:
+;
+; None.
+;
+;--
+cPublicProc _KiUserApcDispatcher ,5
+
+ lea edi, [esp+16] ; (edi)->context frame
+ pop eax ; (eax)->specified function
+ call eax ; call the specified function
+
+; 1 - set alert argument true
+; ebp - addr of context frame
+; execute system service to continue
+ stdCall _ZwContinue, <edi, 1>
+
+stdENDP _KiUserApcDispatcher
+
+
+ page
+ subttl "User Callback Dispatcher"
+;++
+;
+; VOID
+; KiUserCallbackDispatcher (
+; IN ULONG ApiNumber,
+; IN PVOID InputBuffer,
+; IN ULONG INputLength
+; )
+;
+; 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:
+;
+; ApiNumber - Supplies the API number of the callback function that is
+; executed.
+;
+; InputBuffer - Supplies a pointer to the input buffer.
+;
+; InputLength - Supplies the input buffer length.
+;
+; Return Value:
+;
+; This function returns to kernel mode.
+;
+;--
+cPublicProc _KiUserCallbackDispatcher, 3
+.FPO (0, 0, 0, 0, 0, 0)
+
+ add esp,4 ; skip over return address
+ pop edx ; get address of callback function
+
+ ; get peb pointer from teb
+ mov eax,fs:[PcTeb]
+ mov eax,[eax].TebPeb
+ mov eax,[eax].PebKernelCallbackTable ; get address of callback table
+
+ call [eax+edx*4] ; call specified function
+
+;
+; If a return from the callback function occurs, then the output buffer
+; address and length are returned as NULL.
+;
+
+ xor ecx,ecx ; clear output buffer address
+ xor edx,edx ; clear output buffer length
+ int 02bH ; return from callback
+ int 3 ; break if return occurs
+
+stdENDP _KiUserCallbackDispatcher
+
+endif ;; ndef WX86_i386
+
+ page
+ subttl "User Exception Dispatcher"
+;++
+;
+; 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.
+;
+; NOTE: This procedure is not called, but rather dispatched to.
+; It depends on there not being a return address on the stack
+; (assumption w.r.t. argument offsets.)
+;
+; Arguments:
+;
+; ExceptionRecord (esp+0) - Supplies a pointer to an exception record.
+;
+; ContextRecord (esp+4) - Supplies a pointer to a context frame.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _KiUserExceptionDispatcher ,2
+.FPO (0, 2, 0, 0, 0, 0)
+
+ mov ecx, [esp+4] ; (ecx)->context record
+ mov ebx, [esp] ; (ebx)->exception record
+
+; attempt to dispatch the exception
+ stdCall _RtlDispatchException, <ebx, ecx>
+
+;
+; 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 statusn is FALSE, then the exception was not handled
+; and ZwRaiseException is called to perform last chance exception processing.
+;
+
+ or al,al
+ je short kued10
+
+;
+; Continue execution.
+;
+
+ pop ebx ; (ebx)->exception record
+ pop ecx ; (ecx)->context record
+
+; continue execution
+ stdCall _ZwContinue, <ecx, 0>
+ jmp short kued20 ; join common code
+
+;
+; Last chance processing.
+;
+; (esp+0) = ExceptionRecord
+; (esp+4) = ContextRecord
+;
+
+kued10: pop ebx ; (ebx)->exception record
+ pop ecx ; (ecx)->context record
+
+; ecx - context record
+; ebx - exception record
+; perform last chance processiong
+ stdCall _ZwRaiseException, <ebx, ecx, 0>
+
+;
+; Common code for nonsuccessful completion of the continue or raiseexception
+; services. 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.
+;
+
+.FPO(0, 0, 0, 0, 0, 0)
+
+kued20: add esp, -ExceptionRecordSize ; allocate stack space
+ mov [esp]+ErExceptionCode, eax ; set exception code
+ mov dword ptr [esp]+ErExceptionFlags, EXCEPTION_NONCONTINUABLE
+ mov [esp]+ErExceptionRecord,ebx ; set associated exception record
+ mov dword ptr [esp]+ErNumberParameters, 0
+ ; set number of parameters
+; esp - addr of exception record
+ stdCall _RtlRaiseException, <esp>
+; never return
+ stdRET _KiUserExceptionDispatcher
+
+stdENDP _KiUserExceptionDispatcher
+
+ page
+ subttl "Raise User Exception Dispatcher"
+ifndef WX86_i386
+;++
+;
+; NTSTATUS
+; KiUserExceptionDispatcher (
+; IN PVOID ReturnAddress
+; IN NTSTATUS ExceptionCode
+; )
+;
+; Routine Description:
+;
+; This routine is entered on return from kernel mode to raise a user
+; mode exception.
+;
+; NOTE: This procedure is not called, but rather dispatched to.
+;
+; The address this routine must return to is passed in EAX.
+; The exception code to be raised is passed in the TEB.
+;
+; Arguments:
+;
+; ReturnAddress (eax) - Supplies the address to return to
+;
+; ExceptionCode (TEB->ExceptionCode) - Supplies the exception code to be raised
+;
+; Return Value:
+;
+; The exception code that was raised.
+;
+;--
+
+cPublicProc _KiRaiseUserExceptionDispatcher
+
+ push eax ; push address to return to
+ push ebp ; make the debugger happy
+ mov ebp, esp
+ sub esp, ExceptionRecordLength ; allocate exception record
+ mov [esp].ErExceptionAddress, eax ; set exception address
+ mov eax,fs:[PcTeb] ; get exception code to be raised
+ mov eax,[eax].TbExceptionCode ;
+ mov [esp].ErExceptionCode, eax ; store exception code
+ mov [esp].ErExceptionFlags, 0 ; set exception flags
+ mov [esp].ErExceptionRecord, 0 ; set exception record
+ mov [esp].ErNumberParameters, 0 ; set number of parameters
+; raise the exception
+ stdCall _RtlRaiseException, <esp>
+ mov eax, [esp].ErExceptionCode
+ mov esp,ebp
+ pop ebp ; restore return code
+ ret
+
+stdENDP _KiRaiseUserExceptionDispatcher
+endif ;; ndef WX86_i386
+
+
+_TEXT ENDS
+
+ END
diff --git a/private/ntos/rtl/i386/xcptmisc.asm b/private/ntos/rtl/i386/xcptmisc.asm
new file mode 100644
index 000000000..c4c0489c8
--- /dev/null
+++ b/private/ntos/rtl/i386/xcptmisc.asm
@@ -0,0 +1,612 @@
+ title "Miscellaneous Exception Handling"
+;++
+;
+; Copyright (c) 1989 Microsoft Corporation
+;
+; Module Name:
+;
+; xcptmisc.asm
+;
+; 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, get
+; the caller's stack pointer, get the caller's frame pointer, get the
+; caller's floating status, get the caller's processor state, get the
+; caller's extended processor status, and get the current stack limits.
+;
+; Author:
+;
+; David N. Cutler (davec) 14-Aug-1989
+;
+; Environment:
+;
+; Any mode.
+;
+; Revision History:
+;
+; 6 April 90 bryanwi
+;
+; 386 version created
+;
+;--
+.386p
+
+ .xlist
+include ks386.inc
+include callconv.inc ; calling convention macros
+ .list
+
+;
+; Unwind flags.
+;
+
+Unwind equ EXCEPTION_UNWINDING OR EXCEPTION_EXIT_UNWIND
+
+_TEXT$01 SEGMENT DWORD PUBLIC 'CODE'
+ ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING
+
+ page
+ subttl "Execute Handler for Exception"
+;++
+;
+; EXCEPTION_DISPOSITION
+; RtlpExecuteHandlerForException (
+; 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 handler address and
+; 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
+; of this function is called and the handler address and establisher
+; frame pointer are 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 (ebp+8) - Supplies a pointer to an exception record.
+;
+; EstablisherFrame (ebp+12) - Supplies the frame pointer of the establisher
+; of the exception handler that is to be called.
+;
+; ContextRecord (ebp+16) - Supplies a pointer to a context record.
+;
+; DispatcherContext (ebp+20) - Supplies a pointer to the dispatcher context
+; record.
+;
+; ExceptionRoutine (ebp+24) - 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.
+;
+;--
+
+cPublicProc _RtlpExecuteHandlerForException,5
+
+ mov edx,offset FLAT:ExceptionHandler ; Set who to register
+ jmp ExecuteHandler ; jump to common code
+
+stdENDP _RtlpExecuteHandlerForException
+
+
+ page
+ subttl "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 handler address and
+; establisher frame pointer 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 handler address and establisher
+; frame pointer 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 (ebp+8) - Supplies a pointer to an exception record.
+;
+; EstablisherFrame (ebp+12) - Supplies the frame pointer of the establisher
+; of the exception handler that is to be called.
+;
+; ContextRecord (ebp+16) - Supplies a pointer to a context record.
+;
+; DispatcherContext (ebp+20) - Supplies a pointer to the dispatcher context
+; record.
+;
+; ExceptionRoutine (ebp+24) - 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.
+;
+;--
+
+cPublicProc _RtlpExecuteHandlerForUnwind ,5
+
+ mov edx,offset FLAT:UnwindHandler
+
+;; N.B. - FALL into ExecuteHandler
+
+stdENDP _RtlpExecuteHandlerForUnwind
+
+
+
+;
+; ExecuteHandler is the common tail for RtlpExecuteHandlerForException
+; and RtlpExecuteHandlerForUnwind.
+;
+; (edx) = handler (Exception or Unwind) address
+;
+
+
+ExceptionRecord equ [ebp+8]
+EstablisherFrame equ [ebp+12]
+ContextRecord equ [ebp+16]
+DispatcherContext equ [ebp+20]
+ExceptionRoutine equ [ebp+24]
+
+
+cPublicProc ExecuteHandler,5
+
+ push ebp
+ mov ebp,esp
+
+ push EstablisherFrame ; Save context of exception handler
+ ; that we're about to call.
+
+ .errnz ErrHandler-4
+ push edx ; Set Handler address
+
+ .errnz ErrNext-0
+ push fs:PcExceptionList ; Set next pointer
+
+
+ mov fs:PcExceptionList,esp ; Link us on
+
+; Call the specified exception handler.
+
+ push DispatcherContext
+ push ContextRecord
+ push EstablisherFrame
+ push ExceptionRecord
+
+ mov ecx,ExceptionRoutine
+ call ecx
+ mov esp,fs:PcExceptionList
+
+; Don't clean stack here, code in front of ret will blow it off anyway
+
+; Disposition is in eax, so all we do is deregister handler and return
+
+ .errnz ErrNext-0
+ pop fs:PcExceptionList
+
+ mov esp,ebp
+ pop ebp
+ stdRET ExecuteHandler
+
+stdENDP ExecuteHandler
+
+ page
+ subttl "Local Exception Handler"
+;++
+;
+; EXCEPTION_DISPOSITION
+; ExceptionHandler (
+; IN PEXCEPTION_RECORD ExceptionRecord,
+; IN PVOID EstablisherFrame,
+; IN OUT PCONTEXT ContextRecord,
+; IN OUT PVOID DispatcherContext
+; )
+;
+; Routine Description:
+;
+; This function is called when a nested exception occurs. Its function
+; is to retrieve the establisher frame pointer and handler address from
+; its establisher's call frame, store this information in the dispatcher
+; context record, and return a disposition value of nested exception.
+;
+; Arguments:
+;
+; ExceptionRecord (exp+4) - Supplies a pointer to an exception record.
+;
+; EstablisherFrame (esp+8) - Supplies the frame pointer of the establisher
+; of this exception handler.
+;
+; ContextRecord (esp+12) - Supplies a pointer to a context record.
+;
+; DispatcherContext (esp+16) - 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.
+;
+;--
+
+stdPROC ExceptionHandler,4
+
+ mov ecx,dword ptr [esp+4] ; (ecx) -> ExceptionRecord
+ test dword ptr [ecx.ErExceptionFlags],Unwind
+ mov eax,ExceptionContinueSearch ; Assume unwind
+ jnz eh10 ; unwind, go return
+
+;
+; Unwind is not in progress - return nested exception disposition.
+;
+
+ mov ecx,[esp+8] ; (ecx) -> EstablisherFrame
+ mov edx,[esp+16] ; (edx) -> DispatcherContext
+ mov eax,[ecx+8] ; (eax) -> EstablisherFrame for the
+ ; handler active when we
+ ; nested.
+ mov [edx],eax ; Set DispatcherContext field.
+ mov eax,ExceptionNestedException
+
+eh10: stdRET ExceptionHandler
+
+stdENDP ExceptionHandler
+
+ page
+ subttl "Local Unwind Handler"
+;++
+;
+; EXCEPTION_DISPOSITION
+; UnwindHandler (
+; 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 frame pointer and handler address from
+; its establisher's call frame, store this information in the dispatcher
+; context record, and return a disposition value of nested unwind.
+;
+; Arguments:
+;
+; ExceptionRecord (esp+4) - Supplies a pointer to an exception record.
+;
+; EstablisherFrame (esp+8) - Supplies the frame pointer of the establisher
+; of this exception handler.
+;
+; ContextRecord (esp+12) - Supplies a pointer to a context record.
+;
+; DispatcherContext (esp+16) - 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.
+;
+;--
+
+stdPROC UnwindHandler,4
+
+ mov ecx,dword ptr [esp+4] ; (ecx) -> ExceptionRecord
+ test dword ptr [ecx.ErExceptionFlags],Unwind
+ mov eax,ExceptionContinueSearch ; Assume NOT unwind
+ jz uh10 ; not unwind, go return
+
+
+;
+; Unwind is in progress - return collided unwind disposition.
+;
+
+ mov ecx,[esp+8] ; (ecx) -> EstablisherFrame
+ mov edx,[esp+16] ; (edx) -> DispatcherContext
+ mov eax,[ecx+8] ; (eax) -> EstablisherFrame for the
+ ; handler active when we
+ ; nested.
+ mov [edx],eax ; Set DispatcherContext field.
+ mov eax,ExceptionCollidedUnwind
+
+uh10: stdRET UnwindHandler
+
+stdENDP UnwindHandler
+
+ page
+ subttl "Unlink Exception Registration Record & Handler"
+;++
+;
+; VOID
+; RtlpUnlinkHandler(PEXCEPTION_REGISTRATION_RECORD UnlinkPointer)
+;
+; Routine Description:
+;
+; This function removes the specified exception registration record
+; (and thus the relevent handler) from the exception traversal
+; chain.
+;
+; Arguments:
+;
+; UnlinkPointer (esp+4) - Address of registration record to unlink.
+;
+; Return Value:
+;
+; The caller's return address.
+;
+;--
+
+cPublicProc _RtlpUnlinkHandler ,1
+
+ mov ecx,dword ptr [esp+4]
+ mov ecx,[ecx.ErrNext]
+ mov fs:PcExceptionList,ecx
+ stdRET _RtlpUnlinkHandler
+
+stdENDP _RtlpUnlinkHandler
+
+ page
+ subttl "Capture Context"
+;++
+;
+; VOID
+; RtlCaptureContext (PCONTEXT ContextRecord)
+; RtlpCaptureContext (PCONTEXT ContextRecord)
+;
+; Routine Description:
+;
+; This fucntion fills in the specified context record with the
+; current state of the machine, except that the values of EBP
+; and ESP are computed to be those of the caller's caller.
+;
+; N.B. This function assumes it is called from a 'C' procedure with
+; the old ebp at [ebp], the return address at [ebp+4], and
+; old esp = ebp + 8.
+;
+; Certain 'C' optimizations may cause this to not be true.
+;
+; N.B. This function does NOT adjust ESP to pop the arguments off
+; the caller's stack. In other words, it provides a __cdecl ESP,
+; NOT a __stdcall ESP. This is mainly because we can't figure
+; out how many arguments the caller takes.
+;
+; N.B. Floating point state is NOT captured.
+;
+; RtlpCaptureContext does not capture volitales.
+; RtlCaptureContext captures volitales.
+;
+; Arguments:
+;
+; ContextRecord (esp+4) - Address of context record to fill in.
+;
+; Return Value:
+;
+; The caller's return address.
+;
+;--
+
+cPublicProc _RtlCaptureContext ,1
+
+ push ebx
+ mov ebx,[esp+8] ; (ebx) -> ContextRecord
+
+ mov dword ptr [ebx.CsEax],eax
+ mov dword ptr [ebx.CsEcx],ecx
+ mov dword ptr [ebx.CsEdx],edx
+ mov eax, [esp]
+ mov dword ptr [ebx.CsEbx],eax
+
+ mov dword ptr [ebx.CsEsi],esi
+ mov dword ptr [ebx.CsEdi],edi
+ jmp RtlpCaptureCommon
+stdENDP _RtlCaptureContext
+
+cPublicProc _RtlpCaptureContext ,1
+
+ push ebx
+ mov ebx,[esp+8] ; (ebx) -> ContextRecord
+
+ mov dword ptr [ebx.CsEax],0
+ mov dword ptr [ebx.CsEcx],0
+ mov dword ptr [ebx.CsEdx],0
+ mov dword ptr [ebx.CsEbx],0
+
+ mov dword ptr [ebx.CsEsi],0
+ mov dword ptr [ebx.CsEdi],0
+
+RtlpCaptureCommon:
+ mov [ebx.CsSegCs],cs
+ mov [ebx.CsSegDs],ds
+ mov [ebx.CsSegEs],es
+ mov [ebx.CsSegFs],fs
+ mov [ebx.CsSegGs],gs
+ mov [ebx.CsSegSs],ss
+
+ pushfd
+ pop [ebx.CsEflags]
+
+ mov eax,[ebp+4]
+ mov [ebx.CsEip],eax
+
+ mov eax,[ebp]
+ mov [ebx.CsEbp],eax
+
+ lea eax,[ebp+8]
+ mov [ebx.CsEsp],eax
+
+ pop ebx
+ stdRET _RtlpCaptureContext
+
+stdENDP _RtlpCaptureContext
+
+ page
+ subttl "Capture Context (private)"
+;++
+;
+; VOID
+; RtlCaptureContext (PCONTEXT ContextRecord)
+;
+; Routine Description:
+;
+; This function is similiar too RtlpCaptureContext expect that
+; volitales are captured as well.
+;
+; This fucntion fills in the specified context record with the
+; current state of the machine, except that the values of EBP
+; and ESP are computed to be those of the caller's caller.
+;
+; N.B. This function does NOT adjust ESP to pop the arguments off
+; the caller's stack. In other words, it provides a __cdecl ESP,
+; NOT a __stdcall ESP. This is mainly because we can't figure
+; out how many arguments the caller takes.
+;
+; N.B. Floating point state is NOT captured.
+;
+; Arguments:
+;
+; ContextRecord (esp+4) - Address of context record to fill in.
+;
+; Return Value:
+;
+; The caller's return address.
+;
+;--
+
+
+ifndef WX86_i386
+ page
+ subttl "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.
+;
+; On the 386 we always store the stack limits in the PCR, and address
+; both PCR and TEB the same way, so this code is mode independent.
+;
+; Arguments:
+;
+; LowLimit (esp+4) - Supplies a pointer to a variable that is to receive
+; the low limit of the stack.
+;
+; HighLimit (esp+8) - Supplies a pointer to a variable that is to receive
+; the high limit of the stack.
+;
+; Return Value:
+;
+; None.
+;
+;--
+
+cPublicProc _RtlpGetStackLimits ,2
+;cPublicFpo 2,0
+
+ mov eax,fs:PcStackLimit
+ mov ecx,[esp+4]
+ mov [ecx],eax ; Save low limit
+
+ mov eax,fs:PcInitialStack
+ mov ecx,[esp+8]
+ mov [ecx],eax ; Save high limit
+
+ stdRET _RtlpGetStackLimits
+
+stdENDP _RtlpGetStackLimits
+
+endif
+ page
+ subttl "Get Exception Registration List Head"
+;++
+;
+; PVOID
+; RtlpGetRegistrationHead()
+;
+; Routine Description:
+;
+; This function returns the address of the first Exception
+; registration record for the current context.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; The address of the first registration record.
+;
+;--
+
+cPublicProc _RtlpGetRegistrationHead ,0
+;cPublicFpo 0,0
+
+ mov eax,fs:PcExceptionList
+ stdRET _RtlpGetRegistrationHead
+
+stdENDP _RtlpGetRegistrationHead
+
+ page
+ subttl "Get Return Address"
+;++
+;
+; PVOID
+; RtlpGetReturnAddress()
+;
+; Routine Description:
+;
+; This function returns the caller's return address. It assumes
+; that a standard 'C' entry sequence has been used, be warned
+; that certain optimizations might invalidate that assumption.
+;
+; Arguments:
+;
+; None.
+;
+; Return Value:
+;
+; The address of the first registration record.
+;
+;--
+
+cPublicProc _RtlpGetReturnAddress ,0
+;cPublicFpo 0,0
+
+ mov eax,[ebp+4]
+ stdRET _RtlpGetReturnAddress
+
+stdENDP _RtlpGetReturnAddress
+_TEXT$01 ends
+ end
diff --git a/private/ntos/rtl/imagedir.c b/private/ntos/rtl/imagedir.c
new file mode 100644
index 000000000..8b55c6a59
--- /dev/null
+++ b/private/ntos/rtl/imagedir.c
@@ -0,0 +1,352 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ imagedir.c
+
+Abstract:
+
+ The module contains the code to translate an image directory type to
+ the address of the data for that entry.
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Environment:
+
+ User Mode or Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+PIMAGE_NT_HEADERS
+RtlImageNtHeader (
+ IN PVOID Base
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the address of the NT Header.
+
+Arguments:
+
+ Base - Supplies the base of the image.
+
+Return Value:
+
+ Returns the address of the NT Header.
+
+--*/
+
+{
+
+ PIMAGE_NT_HEADERS NtHeaders;
+
+ if (Base != NULL && Base != (PVOID)-1) {
+ try {
+ if (((PIMAGE_DOS_HEADER)Base)->e_magic == IMAGE_DOS_SIGNATURE) {
+ NtHeaders = (PIMAGE_NT_HEADERS)((PCHAR)Base + ((PIMAGE_DOS_HEADER)Base)->e_lfanew);
+ if (NtHeaders->Signature == IMAGE_NT_SIGNATURE) {
+ return NtHeaders;
+ }
+ }
+ }
+ except(EXCEPTION_EXECUTE_HANDLER) {
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+
+PIMAGE_SECTION_HEADER
+RtlSectionTableFromVirtualAddress (
+ IN PIMAGE_NT_HEADERS NtHeaders,
+ IN PVOID Base,
+ IN PVOID Address
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates a VirtualAddress within the image header
+ of a file that is mapped as a file and returns a pointer to the
+ section table entry for that virtual address
+
+Arguments:
+
+ NtHeaders - Supplies the pointer to the image or data file.
+
+ Base - Supplies the base of the image or data file.
+
+ Address - Supplies the virtual address to locate.
+
+Return Value:
+
+ NULL - The file does not contain data for the specified directory entry.
+
+ NON-NULL - Returns the pointer of the section entry containing the data.
+
+--*/
+
+{
+ ULONG i;
+ PIMAGE_SECTION_HEADER NtSection;
+
+ NtSection = IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<NtHeaders->FileHeader.NumberOfSections; i++) {
+ if ((ULONG)Address >= NtSection->VirtualAddress &&
+ (ULONG)Address < NtSection->VirtualAddress + NtSection->SizeOfRawData
+ ) {
+ return NtSection;
+ }
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+
+PVOID
+RtlAddressInSectionTable (
+ IN PIMAGE_NT_HEADERS NtHeaders,
+ IN PVOID Base,
+ IN PVOID Address
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates a VirtualAddress within the image header
+ of a file that is mapped as a file and returns the seek address
+ of the data the Directory describes.
+
+Arguments:
+
+ NtHeaders - Supplies the pointer to the image or data file.
+
+ Base - Supplies the base of the image or data file.
+
+ Address - Supplies the virtual address to locate.
+
+Return Value:
+
+ NULL - The file does not contain data for the specified directory entry.
+
+ NON-NULL - Returns the address of the raw data the directory describes.
+
+--*/
+
+{
+ PIMAGE_SECTION_HEADER NtSection;
+
+ NtSection = RtlSectionTableFromVirtualAddress( NtHeaders,
+ Base,
+ Address
+ );
+ if (NtSection != NULL) {
+ return( (PVOID)((ULONG)Base + ((ULONG)Address - NtSection->VirtualAddress) + NtSection->PointerToRawData) );
+ }
+ else {
+ return( NULL );
+ }
+}
+
+
+
+PVOID
+RtlImageDirectoryEntryToData (
+ IN PVOID Base,
+ IN BOOLEAN MappedAsImage,
+ IN USHORT DirectoryEntry,
+ OUT PULONG Size
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates a Directory Entry within the image header
+ and returns either the virtual address or seek address of the
+ data the Directory describes.
+
+Arguments:
+
+ Base - Supplies the base of the image or data file.
+
+ MappedAsImage - FALSE if the file is mapped as a data file.
+ - TRUE if the file is mapped as an image.
+
+ DirectoryEntry - Supplies the directory entry to locate.
+
+ Size - Return the size of the directory.
+
+Return Value:
+
+ NULL - The file does not contain data for the specified directory entry.
+
+ NON-NULL - Returns the address of the raw data the directory describes.
+
+--*/
+
+{
+ ULONG i, DirectoryAddress;
+ PIMAGE_NT_HEADERS NtHeaders;
+
+ if ((ULONG)Base & 0x00000001) {
+ Base = (PVOID)((ULONG)Base & ~0x00000001);
+ MappedAsImage = FALSE;
+ }
+
+ NtHeaders = RtlImageNtHeader(Base);
+
+ if ((!NtHeaders) ||
+ (DirectoryEntry >= NtHeaders->OptionalHeader.NumberOfRvaAndSizes)) {
+ return( NULL );
+ }
+
+ if (!(DirectoryAddress = NtHeaders->OptionalHeader.DataDirectory[ DirectoryEntry ].VirtualAddress)) {
+ return( NULL );
+ }
+ *Size = NtHeaders->OptionalHeader.DataDirectory[ DirectoryEntry ].Size;
+ if (MappedAsImage || DirectoryAddress < NtHeaders->OptionalHeader.SizeOfHeaders) {
+ return( (PVOID)((ULONG)Base + DirectoryAddress) );
+ }
+
+ return( RtlAddressInSectionTable(NtHeaders, Base, (PVOID)DirectoryAddress ));
+}
+
+
+#if !defined(NTOS_KERNEL_RUNTIME) && !defined(BLDR_KERNEL_RUNTIME)
+
+PIMAGE_SECTION_HEADER
+RtlImageRvaToSection(
+ IN PIMAGE_NT_HEADERS NtHeaders,
+ IN PVOID Base,
+ IN ULONG Rva
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates an RVA within the image header of a file
+ that is mapped as a file and returns a pointer to the section
+ table entry for that virtual address
+
+Arguments:
+
+ NtHeaders - Supplies the pointer to the image or data file.
+
+ Base - Supplies the base of the image or data file. The image
+ was mapped as a data file.
+
+ Rva - Supplies the relative virtual address (RVA) to locate.
+
+Return Value:
+
+ NULL - The RVA was not found within any of the sections of the image.
+
+ NON-NULL - Returns the pointer to the image section that contains
+ the RVA
+
+--*/
+
+{
+ ULONG i;
+ PIMAGE_SECTION_HEADER NtSection;
+
+ NtSection = IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<NtHeaders->FileHeader.NumberOfSections; i++) {
+ if (Rva >= NtSection->VirtualAddress &&
+ Rva < NtSection->VirtualAddress + NtSection->SizeOfRawData
+ ) {
+ return NtSection;
+ }
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+
+
+PVOID
+RtlImageRvaToVa(
+ IN PIMAGE_NT_HEADERS NtHeaders,
+ IN PVOID Base,
+ IN ULONG Rva,
+ IN OUT PIMAGE_SECTION_HEADER *LastRvaSection OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates an RVA within the image header of a file that
+ is mapped as a file and returns the virtual addrees of the
+ corresponding byte in the file.
+
+
+Arguments:
+
+ NtHeaders - Supplies the pointer to the image or data file.
+
+ Base - Supplies the base of the image or data file. The image
+ was mapped as a data file.
+
+ Rva - Supplies the relative virtual address (RVA) to locate.
+
+ LastRvaSection - Optional parameter that if specified, points
+ to a variable that contains the last section value used for
+ the specified image to translate and RVA to a VA.
+
+Return Value:
+
+ NULL - The file does not contain the specified RVA
+
+ NON-NULL - Returns the virtual addrees in the mapped file.
+
+--*/
+
+{
+ PIMAGE_SECTION_HEADER NtSection;
+
+ if (!ARGUMENT_PRESENT( LastRvaSection ) ||
+ (NtSection = *LastRvaSection) == NULL ||
+ Rva < NtSection->VirtualAddress ||
+ Rva >= NtSection->VirtualAddress + NtSection->SizeOfRawData
+ ) {
+ NtSection = RtlImageRvaToSection( NtHeaders,
+ Base,
+ Rva
+ );
+ }
+
+ if (NtSection != NULL) {
+ if (LastRvaSection != NULL) {
+ *LastRvaSection = NtSection;
+ }
+
+ return (PVOID)((ULONG)Base +
+ (Rva - NtSection->VirtualAddress) +
+ NtSection->PointerToRawData
+ );
+ }
+ else {
+ return NULL;
+ }
+}
+
+#endif
diff --git a/private/ntos/rtl/ldrreloc.c b/private/ntos/rtl/ldrreloc.c
new file mode 100644
index 000000000..fe211eff4
--- /dev/null
+++ b/private/ntos/rtl/ldrreloc.c
@@ -0,0 +1,217 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ldrreloc.c
+
+Abstract:
+
+ This module contains the code to relocate an image when
+ the preferred base isn't available. This is called by the
+ boot loader, device driver loader, and system loader.
+
+Author:
+
+ Mike O'Leary (mikeol) 03-Feb-1992
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,LdrRelocateImage)
+#pragma alloc_text(PAGE,LdrProcessRelocationBlock)
+#endif
+
+ULONG
+LdrRelocateImage (
+ IN PVOID NewBase,
+ IN PUCHAR LoaderName,
+ IN ULONG Success,
+ IN ULONG Conflict,
+ IN ULONG Invalid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine relocates an image file that was not loaded into memory
+ at the prefered address.
+
+Arguments:
+
+ NewBase - Supplies a pointer to the image base.
+
+ LoaderName - Indicates which loader routine is being called from.
+
+ Success - Value to return if relocation successful.
+
+ Conflict - Value to return if can't relocate.
+
+ Invalid - Value to return if relocations are invalid.
+
+Return Value:
+
+ Success if image is relocated.
+ Conflict if image can't be relocated.
+ Invalid if image contains invalid fixups.
+
+--*/
+
+{
+ LONG Temp, Diff;
+ ULONG TotalCountBytes, VA, OldBase, SizeOfBlock;
+ PUCHAR FixupVA;
+ USHORT Offset;
+ PUSHORT NextOffset;
+ PIMAGE_NT_HEADERS NtHeaders;
+ PIMAGE_BASE_RELOCATION NextBlock;
+
+ RTL_PAGED_CODE();
+
+ NtHeaders = RtlImageNtHeader( NewBase );
+ OldBase = NtHeaders->OptionalHeader.ImageBase;
+
+ //
+ // Locate the relocation section.
+ //
+
+ NextBlock = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData(
+ NewBase, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &TotalCountBytes);
+
+ if (!NextBlock || !TotalCountBytes) {
+
+ //
+ // The image does not contain a relocation table, and therefore
+ // cannot be relocated.
+ //
+#if DBG
+ DbgPrint("%s: Image can't be relocated, no fixup information.\n", LoaderName);
+#endif // DBG
+ return Conflict;
+ }
+
+ //
+ // If the image has a relocation table, then apply the specified fixup
+ // information to the image.
+ //
+
+ while (TotalCountBytes) {
+ SizeOfBlock = NextBlock->SizeOfBlock;
+ TotalCountBytes -= SizeOfBlock;
+ SizeOfBlock -= sizeof(IMAGE_BASE_RELOCATION);
+ SizeOfBlock /= sizeof(USHORT);
+ NextOffset = (PUSHORT)((ULONG)NextBlock + sizeof(IMAGE_BASE_RELOCATION));
+
+ VA = (ULONG)NewBase + NextBlock->VirtualAddress;
+ Diff = (LONG)NewBase - (LONG)OldBase;
+
+ if ( !(NextBlock = LdrProcessRelocationBlock(VA,SizeOfBlock,NextOffset,Diff)) ) {
+#if DBG
+ DbgPrint("%s: Unknown base relocation type\n", LoaderName);
+#endif
+ return Invalid;
+ }
+ }
+
+ return Success;
+}
+
+PIMAGE_BASE_RELOCATION
+LdrProcessRelocationBlock(
+ IN ULONG VA,
+ IN ULONG SizeOfBlock,
+ IN PUSHORT NextOffset,
+ IN LONG Diff
+ )
+{
+ PUCHAR FixupVA;
+ USHORT Offset;
+ LONG Temp;
+
+ RTL_PAGED_CODE();
+
+ while (SizeOfBlock--) {
+
+ Offset = *NextOffset & (USHORT)0xfff;
+ FixupVA = (PUCHAR)(VA + Offset);
+
+ //
+ // Apply the fixups.
+ //
+
+ switch ((*NextOffset) >> 12) {
+
+ case IMAGE_REL_BASED_HIGHLOW :
+ //
+ // HighLow - (32-bits) relocate the high and low half
+ // of an address.
+ //
+ *(LONG UNALIGNED *)FixupVA += Diff;
+ break;
+
+ case IMAGE_REL_BASED_HIGH :
+ //
+ // High - (16-bits) relocate the high half of an address.
+ //
+ Temp = *(PUSHORT)FixupVA << 16;
+ Temp += Diff;
+ *(PUSHORT)FixupVA = (USHORT)(Temp >> 16);
+ break;
+
+ case IMAGE_REL_BASED_HIGHADJ :
+ //
+ // Adjust high - (16-bits) relocate the high half of an
+ // address and adjust for sign extension of low half.
+ //
+ Temp = *(PUSHORT)FixupVA << 16;
+ ++NextOffset;
+ --SizeOfBlock;
+ Temp += (LONG)(*(PSHORT)NextOffset);
+ Temp += Diff;
+ Temp += 0x8000;
+ *(PUSHORT)FixupVA = (USHORT)(Temp >> 16);
+ break;
+
+ case IMAGE_REL_BASED_LOW :
+ //
+ // Low - (16-bit) relocate the low half of an address.
+ //
+ Temp = *(PSHORT)FixupVA;
+ Temp += Diff;
+ *(PUSHORT)FixupVA = (USHORT)Temp;
+ break;
+
+ case IMAGE_REL_BASED_MIPS_JMPADDR :
+ //
+ // JumpAddress - (32-bits) relocate a MIPS jump address.
+ //
+ Temp = (*(PULONG)FixupVA & 0x3ffffff) << 2;
+ Temp += Diff;
+ *(PULONG)FixupVA = (*(PULONG)FixupVA & ~0x3ffffff) |
+ ((Temp >> 2) & 0x3ffffff);
+
+ break;
+
+ case IMAGE_REL_BASED_ABSOLUTE :
+ //
+ // Absolute - no fixup required.
+ //
+ break;
+
+ default :
+ //
+ // Illegal - illegal relocation type.
+ //
+
+ return (PIMAGE_BASE_RELOCATION)NULL;
+ }
+ ++NextOffset;
+ }
+ return (PIMAGE_BASE_RELOCATION)NextOffset;
+}
diff --git a/private/ntos/rtl/ldrrsrc.c b/private/ntos/rtl/ldrrsrc.c
new file mode 100644
index 000000000..95e664f2b
--- /dev/null
+++ b/private/ntos/rtl/ldrrsrc.c
@@ -0,0 +1,930 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ ldrrsrc.c
+
+Abstract:
+
+ Loader API calls for accessing resource sections.
+
+Author:
+
+ Steve Wood (stevewo) 16-Sep-1991
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,LdrAccessResource)
+#pragma alloc_text(PAGE,LdrpAccessResourceData)
+#pragma alloc_text(PAGE,LdrFindEntryForAddress)
+#pragma alloc_text(PAGE,LdrFindResource_U)
+#pragma alloc_text(PAGE,LdrFindResourceDirectory_U)
+#pragma alloc_text(PAGE,LdrpCompareResourceNames_U)
+#pragma alloc_text(PAGE,LdrpSearchResourceSection_U)
+#pragma alloc_text(PAGE,LdrEnumResources)
+#endif
+
+NTSTATUS
+LdrAccessResource(
+ IN PVOID DllHandle,
+ IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
+ OUT PVOID *Address OPTIONAL,
+ OUT PULONG Size OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates the address of the specified resource in the
+ specified DLL and returns its address.
+
+Arguments:
+
+ DllHandle - Supplies a handle to the image file that the resource is
+ contained in.
+
+ ResourceDataEntry - Supplies a pointer to the resource data entry in
+ the resource data section of the image file specified by the
+ DllHandle parameter. This pointer should have been one returned
+ by the LdrFindResource function.
+
+ Address - Optional pointer to a variable that will receive the
+ address of the resource specified by the first two parameters.
+
+ Size - Optional pointer to a variable that will receive the size of
+ the resource specified by the first two parameters.
+
+Return Value:
+
+ TBD
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return LdrpAccessResourceData(
+ DllHandle,
+ ResourceDataEntry,
+ Address,
+ Size
+ );
+}
+
+
+NTSTATUS
+LdrpAccessResourceData(
+ IN PVOID DllHandle,
+ IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
+ OUT PVOID *Address OPTIONAL,
+ OUT PULONG Size OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the data necessary to actually examine the
+ contents of a particular resource.
+
+Arguments:
+
+ DllHandle - Supplies a handle to the image file that the resource is
+ contained in.
+
+ ResourceDataEntry - Supplies a pointer to the resource data entry in
+ the resource data directory of the image file specified by the
+ DllHandle parameter. This pointer should have been one returned
+ by the LdrFindResource function.
+
+ Address - Optional pointer to a variable that will receive the
+ address of the resource specified by the first two parameters.
+
+ Size - Optional pointer to a variable that will receive the size of
+ the resource specified by the first two parameters.
+
+
+Return Value:
+
+ TBD
+
+--*/
+
+{
+ PIMAGE_RESOURCE_DIRECTORY ResourceDirectory;
+ ULONG ResourceSize;
+ PIMAGE_NT_HEADERS NtHeaders;
+ ULONG VirtualAddressOffset;
+ PIMAGE_SECTION_HEADER NtSection;
+
+ RTL_PAGED_CODE();
+
+ ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ RtlImageDirectoryEntryToData(DllHandle,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_RESOURCE,
+ &ResourceSize
+ );
+ if (!ResourceDirectory) {
+ return( STATUS_RESOURCE_DATA_NOT_FOUND );
+ }
+
+ if ((ULONG)DllHandle & 0x00000001) {
+ DllHandle = (PVOID)((ULONG)DllHandle & ~0x00000001);
+ NtHeaders = RtlImageNtHeader( DllHandle );
+ VirtualAddressOffset =
+ (ULONG)DllHandle +
+ NtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress -
+ (ULONG)ResourceDirectory;
+
+ //
+ // Now, we must check to see if the resource is not in the
+ // same section as the resource table. If it's in .rsrc1,
+ // we've got to adjust the RVA in the ResourceDataEntry
+ // to point to the correct place in the non-VA data file.
+ //
+ NtSection= RtlSectionTableFromVirtualAddress( NtHeaders, DllHandle,
+ (PVOID)NtHeaders->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_RESOURCE ].VirtualAddress );
+ if (!NtSection) {
+ return( STATUS_RESOURCE_DATA_NOT_FOUND );
+ }
+ if ( ResourceDataEntry->OffsetToData > NtSection->Misc.VirtualSize ) {
+ ULONG rva;
+
+ rva = NtSection->VirtualAddress;
+ NtSection= RtlSectionTableFromVirtualAddress
+ (
+ NtHeaders,
+ DllHandle,
+ (PVOID)ResourceDataEntry->OffsetToData
+ );
+ VirtualAddressOffset +=
+ ((ULONG)NtSection->VirtualAddress - rva) -
+ ((ULONG)RtlAddressInSectionTable ( NtHeaders, DllHandle, (PVOID)NtSection->VirtualAddress ) - (ULONG)ResourceDirectory);
+ }
+ }
+ else {
+ VirtualAddressOffset = 0;
+ }
+
+ try {
+ if (ARGUMENT_PRESENT( Address )) {
+ *Address = (PVOID)( (ULONG)DllHandle +
+ (ResourceDataEntry->OffsetToData - VirtualAddressOffset)
+ );
+ }
+
+ if (ARGUMENT_PRESENT( Size )) {
+ *Size = ResourceDataEntry->Size;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+LdrFindEntryForAddress(
+ IN PVOID Address,
+ OUT PLDR_DATA_TABLE_ENTRY *TableEntry
+ )
+/*++
+
+Routine Description:
+
+ This function returns the load data table entry that describes the virtual
+ address range that contains the passed virtual address.
+
+Arguments:
+
+ Address - Supplies a 32-bit virtual address.
+
+ TableEntry - Supplies a pointer to the variable that will receive the
+ address of the loader data table entry.
+
+
+Return Value:
+
+ Status
+
+--*/
+{
+ PPEB_LDR_DATA Ldr;
+ PLIST_ENTRY Head, Next;
+ PLDR_DATA_TABLE_ENTRY Entry;
+ PIMAGE_NT_HEADERS NtHeaders;
+ PVOID ImageBase;
+ PVOID EndOfImage;
+
+ Ldr = NtCurrentPeb()->Ldr;
+ if (Ldr == NULL) {
+ return( STATUS_NO_MORE_ENTRIES );
+ }
+
+ Head = &Ldr->InMemoryOrderModuleList;
+ Next = Head->Flink;
+ while ( Next != Head ) {
+ Entry = CONTAINING_RECORD( Next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks );
+
+ NtHeaders = RtlImageNtHeader( Entry->DllBase );
+ if (NtHeaders != NULL) {
+ ImageBase = (PVOID)Entry->DllBase;
+
+ EndOfImage = (PVOID)
+ ((ULONG)ImageBase + NtHeaders->OptionalHeader.SizeOfImage);
+
+ if ((ULONG)Address >= (ULONG)ImageBase && (ULONG)Address < (ULONG)EndOfImage) {
+ *TableEntry = Entry;
+ return( STATUS_SUCCESS );
+ }
+ }
+
+ Next = Next->Flink;
+ }
+
+ return( STATUS_NO_MORE_ENTRIES );
+}
+
+
+NTSTATUS
+LdrFindResource_U(
+ IN PVOID DllHandle,
+ IN PULONG ResourceIdPath,
+ IN ULONG ResourceIdPathLength,
+ OUT PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates the address of the specified resource in the
+ specified DLL and returns its address.
+
+Arguments:
+
+ DllHandle - Supplies a handle to the image file that the resource is
+ contained in.
+
+ ResourceIdPath - Supplies a pointer to an array of 32-bit resource
+ identifiers. Each identifier is either an integer or a pointer
+ to a STRING structure that specifies a resource name. The array
+ is used to traverse the directory structure contained in the
+ resource section in the image file specified by the DllHandle
+ parameter.
+
+ ResourceIdPathLength - Supplies the number of elements in the
+ ResourceIdPath array.
+
+ ResourceDataEntry - Supplies a pointer to a variable that will
+ receive the address of the resource data entry in the resource
+ data section of the image file specified by the DllHandle
+ parameter.
+
+Return Value:
+
+ TBD
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return LdrpSearchResourceSection_U(
+ DllHandle,
+ ResourceIdPath,
+ ResourceIdPathLength,
+ FALSE, // Look for a leaf node
+ (PVOID *)ResourceDataEntry
+ );
+}
+
+
+NTSTATUS
+LdrFindResourceDirectory_U(
+ IN PVOID DllHandle,
+ IN PULONG ResourceIdPath,
+ IN ULONG ResourceIdPathLength,
+ OUT PIMAGE_RESOURCE_DIRECTORY *ResourceDirectory
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates the address of the specified resource directory in
+ specified DLL and returns its address.
+
+Arguments:
+
+ DllHandle - Supplies a handle to the image file that the resource
+ directory is contained in.
+
+ ResourceIdPath - Supplies a pointer to an array of 32-bit resource
+ identifiers. Each identifier is either an integer or a pointer
+ to a STRING structure that specifies a resource name. The array
+ is used to traverse the directory structure contained in the
+ resource section in the image file specified by the DllHandle
+ parameter.
+
+ ResourceIdPathLength - Supplies the number of elements in the
+ ResourceIdPath array.
+
+ ResourceDirectory - Supplies a pointer to a variable that will
+ receive the address of the resource directory specified by
+ ResourceIdPath in the resource data section of the image file
+ the DllHandle parameter.
+
+Return Value:
+
+ TBD
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return LdrpSearchResourceSection_U(
+ DllHandle,
+ ResourceIdPath,
+ ResourceIdPathLength,
+ TRUE, // Look for a directory node
+ (PVOID *)ResourceDirectory
+ );
+}
+
+
+LONG
+LdrpCompareResourceNames_U(
+ IN ULONG ResourceName,
+ IN PIMAGE_RESOURCE_DIRECTORY ResourceDirectory,
+ IN PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirectoryEntry
+ )
+{
+ LONG li;
+ PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
+
+ if (ResourceName & LDR_RESOURCE_ID_NAME_MASK) {
+ if (!ResourceDirectoryEntry->NameIsString) {
+ return( -1 );
+ }
+
+ ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
+ ((PCHAR)ResourceDirectory + ResourceDirectoryEntry->NameOffset);
+
+ li = wcsncmp( (LPWSTR)ResourceName,
+ ResourceNameString->NameString,
+ ResourceNameString->Length
+ );
+
+ if (!li && wcslen((PWSTR)ResourceName) != ResourceNameString->Length) {
+ return( 1 );
+ }
+
+ return(li);
+ }
+ else {
+ if (ResourceDirectoryEntry->NameIsString) {
+ return( 1 );
+ }
+
+ return( ResourceName - ResourceDirectoryEntry->Name );
+ }
+}
+
+
+#define USE_FIRSTAVAILABLE_LANGID (0xFFFFFFFF & ~LDR_RESOURCE_ID_NAME_MASK)
+
+NTSTATUS
+LdrpSearchResourceSection_U(
+ IN PVOID DllHandle,
+ IN PULONG ResourceIdPath,
+ IN ULONG ResourceIdPathLength,
+ IN BOOLEAN FindDirectoryEntry,
+ OUT PVOID *ResourceDirectoryOrData
+ )
+
+/*++
+
+Routine Description:
+
+ This function locates the address of the specified resource in the
+ specified DLL and returns its address.
+
+Arguments:
+
+ DllHandle - Supplies a handle to the image file that the resource is
+ contained in.
+
+ ResourceIdPath - Supplies a pointer to an array of 32-bit resource
+ identifiers. Each identifier is either an integer or a pointer
+ to a null terminated string (PSZ) that specifies a resource
+ name. The array is used to traverse the directory structure
+ contained in the resource section in the image file specified by
+ the DllHandle parameter.
+
+ ResourceIdPathLength - Supplies the number of elements in the
+ ResourceIdPath array.
+
+ FindDirectoryEntry - Supplies a boolean that is TRUE if caller is
+ searching for a resource directory, otherwise the caller is
+ searching for a resource data entry.
+
+ ResourceDirectoryOrData - Supplies a pointer to a variable that will
+ receive the address of the resource directory or data entry in
+ the resource data section of the image file specified by the
+ DllHandle parameter.
+
+Return Value:
+
+ TBD
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIMAGE_RESOURCE_DIRECTORY LanguageResourceDirectory, ResourceDirectory, TopResourceDirectory;
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntLow;
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntMiddle;
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirEntHigh;
+ PIMAGE_RESOURCE_DATA_ENTRY ResourceEntry;
+ USHORT n, half;
+ LONG dir;
+ ULONG size;
+ ULONG ResourceIdRetry, RetryCount;
+ LCID DefaultThreadLocale, DefaultSystemLocale;
+ LANGID NewLangId;
+ PULONG IdPath = ResourceIdPath;
+ ULONG IdPathLength = ResourceIdPathLength;
+ BOOLEAN fIsNeutral = FALSE;
+ LANGID GivenLanguage;
+
+ RTL_PAGED_CODE();
+
+ TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ RtlImageDirectoryEntryToData(DllHandle,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_RESOURCE,
+ &size
+ );
+ if (!TopResourceDirectory) {
+ return( STATUS_RESOURCE_DATA_NOT_FOUND );
+ }
+
+ try {
+ ResourceDirectory = TopResourceDirectory;
+ ResourceIdRetry = USE_FIRSTAVAILABLE_LANGID;
+ RetryCount = 0;
+ ResourceEntry = NULL;
+ LanguageResourceDirectory = NULL;
+ while (ResourceDirectory != NULL && ResourceIdPathLength--) {
+ //
+ // If search path includes a language id, then attempt to
+ // match the following language ids in this order:
+ //
+ // (0) use given language id
+ // (1) use primary language of given language id
+ // (2) use id 0 (neutral resource)
+ //
+ // If the PRIMARY language id is ZERO, then ALSO attempt to
+ // match the following language ids in this order:
+ //
+ // (3) use lang id from locale in TEB
+ // (4) use lang id from user's locale id
+ // (5) use primary language of user's locale id
+ // (6) use lang id from system default locale id
+ // (7) use primary language of system default locale id
+ // (8) use US English lang id
+ // (9) use any lang id that matches requested info
+ //
+ if (ResourceIdPathLength == 0 && IdPathLength == 3) {
+ LanguageResourceDirectory = ResourceDirectory;
+ }
+
+ if (LanguageResourceDirectory != NULL) {
+ GivenLanguage = (LANGID)IdPath[ 2 ];
+ fIsNeutral = (PRIMARYLANGID( GivenLanguage ) == LANG_NEUTRAL);
+TryNextLangId:
+ switch( RetryCount++ ) {
+ case 0: // Use given language id
+ NewLangId = GivenLanguage;
+ break;
+
+ case 1: // Use primary language of given language id
+ NewLangId = PRIMARYLANGID( GivenLanguage );
+ break;
+
+ case 2: // Use id 0 (neutral resource)
+ NewLangId = LANG_USER_DEFAULT;
+ NewLangId = LANG_SYSTEM_DEFAULT;
+ NewLangId = 0;
+ break;
+
+ case 3: // Use lang id from locale in TEB
+ if ( !fIsNeutral ) {
+ // Stop looking - Not in the neutral case
+ goto ReturnFailure;
+ break;
+ }
+
+ if (SUBLANGID( GivenLanguage ) == SUBLANG_SYS_DEFAULT) {
+ // Skip over all USER locale options
+ DefaultThreadLocale = 0;
+ RetryCount += 2;
+ break;
+ }
+
+ if (NtCurrentTeb() != NULL) {
+ NewLangId = LANGIDFROMLCID(NtCurrentTeb()->CurrentLocale);
+ }
+ break;
+
+ case 4: // Use User's default locale
+ Status = NtQueryDefaultLocale( TRUE, &DefaultThreadLocale );
+ if (NT_SUCCESS( Status )) {
+ NewLangId = LANGIDFROMLCID(DefaultThreadLocale);
+ break;
+ }
+
+ RetryCount++;
+ break;
+
+ case 5: // Use primary language of User's default locale
+ NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
+ break;
+
+ case 6: // Use System default locale
+ Status = NtQueryDefaultLocale( FALSE, &DefaultSystemLocale );
+ if (!NT_SUCCESS( Status )) {
+ RetryCount++;
+ break;
+ }
+
+ if (DefaultSystemLocale != DefaultThreadLocale) {
+ NewLangId = LANGIDFROMLCID(DefaultSystemLocale);
+ break;
+ }
+
+ RetryCount += 2;
+ // fall through
+
+ case 8: // Use US English language
+ NewLangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
+ break;
+
+ case 7: // Use primary language of System default locale
+ NewLangId = PRIMARYLANGID( (LANGID)ResourceIdRetry );
+ break;
+
+ case 9: // Take any lang id that matches
+ NewLangId = USE_FIRSTAVAILABLE_LANGID;
+ break;
+
+ default: // No lang ids to match
+ goto ReturnFailure;
+ break;
+ }
+
+ //
+ // If looking for a specific language id and same as the
+ // one we just looked up, then skip it.
+ //
+ if (NewLangId != USE_FIRSTAVAILABLE_LANGID &&
+ NewLangId == ResourceIdRetry
+ ) {
+ goto TryNextLangId;
+ }
+
+ //
+ // Try this new language Id
+ //
+ ResourceIdRetry = (ULONG)NewLangId;
+ ResourceIdPath = &ResourceIdRetry;
+ ResourceDirectory = LanguageResourceDirectory;
+ }
+
+ n = ResourceDirectory->NumberOfNamedEntries;
+ ResourceDirEntLow = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResourceDirectory+1);
+ if (!(*ResourceIdPath & LDR_RESOURCE_ID_NAME_MASK)) {
+ ResourceDirEntLow += n;
+ n = ResourceDirectory->NumberOfIdEntries;
+ }
+
+ if (!n) {
+ ResourceDirectory = NULL;
+ goto NotFound;
+ }
+
+ if (LanguageResourceDirectory != NULL &&
+ *ResourceIdPath == USE_FIRSTAVAILABLE_LANGID
+ ) {
+ ResourceDirectory = NULL;
+ ResourceIdRetry = ResourceDirEntLow->Name;
+ ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
+ ((PCHAR)TopResourceDirectory +
+ ResourceDirEntLow->OffsetToData
+ );
+
+ break;
+ }
+
+ ResourceDirectory = NULL;
+ ResourceDirEntHigh = ResourceDirEntLow + n - 1;
+ while (ResourceDirEntLow <= ResourceDirEntHigh) {
+ if ((half = (n >> 1)) != 0) {
+ ResourceDirEntMiddle = ResourceDirEntLow;
+ if (*(PUCHAR)&n & 1) {
+ ResourceDirEntMiddle += half;
+ }
+ else {
+ ResourceDirEntMiddle += half - 1;
+ }
+ dir = LdrpCompareResourceNames_U( *ResourceIdPath,
+ TopResourceDirectory,
+ ResourceDirEntMiddle
+ );
+ if (!dir) {
+ if (ResourceDirEntMiddle->DataIsDirectory) {
+ ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ ((PCHAR)TopResourceDirectory +
+ ResourceDirEntMiddle->OffsetToDirectory
+ );
+ }
+ else {
+ ResourceDirectory = NULL;
+ ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
+ ((PCHAR)TopResourceDirectory +
+ ResourceDirEntMiddle->OffsetToData
+ );
+ }
+
+ break;
+ }
+ else {
+ if (dir < 0) {
+ ResourceDirEntHigh = ResourceDirEntMiddle - 1;
+ if (*(PUCHAR)&n & 1) {
+ n = half;
+ }
+ else {
+ n = half - 1;
+ }
+ }
+ else {
+ ResourceDirEntLow = ResourceDirEntMiddle + 1;
+ n = half;
+ }
+ }
+ }
+ else {
+ if (n != 0) {
+ dir = LdrpCompareResourceNames_U( *ResourceIdPath,
+ TopResourceDirectory,
+ ResourceDirEntLow
+ );
+ if (!dir) {
+ if (ResourceDirEntLow->DataIsDirectory) {
+ ResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ ((PCHAR)TopResourceDirectory +
+ ResourceDirEntLow->OffsetToDirectory
+ );
+ }
+ else {
+ ResourceEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
+ ((PCHAR)TopResourceDirectory +
+ ResourceDirEntLow->OffsetToData
+ );
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ ResourceIdPath++;
+ }
+
+ if (ResourceEntry != NULL && !FindDirectoryEntry) {
+ *ResourceDirectoryOrData = (PVOID)ResourceEntry;
+ Status = STATUS_SUCCESS;
+ }
+ else
+ if (ResourceDirectory != NULL && FindDirectoryEntry) {
+ *ResourceDirectoryOrData = (PVOID)ResourceDirectory;
+ Status = STATUS_SUCCESS;
+ }
+ else {
+NotFound:
+ switch( IdPathLength - ResourceIdPathLength) {
+ case 3: Status = STATUS_RESOURCE_LANG_NOT_FOUND; break;
+ case 2: Status = STATUS_RESOURCE_NAME_NOT_FOUND; break;
+ case 1: Status = STATUS_RESOURCE_TYPE_NOT_FOUND; break;
+ default: Status = STATUS_INVALID_PARAMETER; break;
+ }
+ }
+
+ if (Status == STATUS_RESOURCE_LANG_NOT_FOUND &&
+ LanguageResourceDirectory != NULL
+ ) {
+ ResourceEntry = NULL;
+ goto TryNextLangId;
+ReturnFailure: ;
+ }
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ return Status;
+}
+
+NTSTATUS
+LdrEnumResources(
+ IN PVOID DllHandle,
+ IN PULONG ResourceIdPath,
+ IN ULONG ResourceIdPathLength,
+ IN OUT PULONG NumberOfResources,
+ OUT PLDR_ENUM_RESOURCE_ENTRY Resources OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ PIMAGE_RESOURCE_DIRECTORY TopResourceDirectory;
+ PIMAGE_RESOURCE_DIRECTORY TypeResourceDirectory;
+ PIMAGE_RESOURCE_DIRECTORY NameResourceDirectory;
+ PIMAGE_RESOURCE_DIRECTORY LangResourceDirectory;
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY TypeResourceDirectoryEntry;
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY NameResourceDirectoryEntry;
+ PIMAGE_RESOURCE_DIRECTORY_ENTRY LangResourceDirectoryEntry;
+ ULONG TypeDirectoryIndex, NumberOfTypeDirectoryEntries;
+ ULONG NameDirectoryIndex, NumberOfNameDirectoryEntries;
+ ULONG LangDirectoryIndex, NumberOfLangDirectoryEntries;
+ BOOLEAN ScanTypeDirectory;
+ BOOLEAN ScanNameDirectory;
+ BOOLEAN ReturnThisResource;
+ PIMAGE_RESOURCE_DIR_STRING_U ResourceNameString;
+ ULONG TypeResourceNameOrId;
+ ULONG NameResourceNameOrId;
+ ULONG LangResourceNameOrId;
+ PLDR_ENUM_RESOURCE_ENTRY ResourceInfo;
+ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
+ ULONG ResourceIndex, MaxResourceIndex;
+ ULONG Size;
+
+ ResourceIndex = 0;
+ if (!ARGUMENT_PRESENT( Resources )) {
+ MaxResourceIndex = 0;
+ }
+ else {
+ MaxResourceIndex = *NumberOfResources;
+ }
+ *NumberOfResources = 0;
+
+ TopResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ RtlImageDirectoryEntryToData( DllHandle,
+ TRUE,
+ IMAGE_DIRECTORY_ENTRY_RESOURCE,
+ &Size
+ );
+ if (!TopResourceDirectory) {
+ return STATUS_RESOURCE_DATA_NOT_FOUND;
+ }
+
+ TypeResourceDirectory = TopResourceDirectory;
+ TypeResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(TypeResourceDirectory+1);
+ NumberOfTypeDirectoryEntries = TypeResourceDirectory->NumberOfNamedEntries +
+ TypeResourceDirectory->NumberOfIdEntries;
+ TypeDirectoryIndex = 0;
+ Status = STATUS_SUCCESS;
+ for (TypeDirectoryIndex=0;
+ TypeDirectoryIndex<NumberOfTypeDirectoryEntries;
+ TypeDirectoryIndex++, TypeResourceDirectoryEntry++
+ ) {
+ if (ResourceIdPathLength > 0) {
+ ScanTypeDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 0 ],
+ TopResourceDirectory,
+ TypeResourceDirectoryEntry
+ ) == 0;
+ }
+ else {
+ ScanTypeDirectory = TRUE;
+ }
+ if (ScanTypeDirectory) {
+ if (!TypeResourceDirectoryEntry->DataIsDirectory) {
+ return STATUS_INVALID_IMAGE_FORMAT;
+ }
+ if (TypeResourceDirectoryEntry->NameIsString) {
+ ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
+ ((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->NameOffset);
+
+ TypeResourceNameOrId = (ULONG)ResourceNameString;
+ }
+ else {
+ TypeResourceNameOrId = (ULONG)TypeResourceDirectoryEntry->Id;
+ }
+
+ NameResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ ((PCHAR)TopResourceDirectory + TypeResourceDirectoryEntry->OffsetToDirectory);
+ NameResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(NameResourceDirectory+1);
+ NumberOfNameDirectoryEntries = NameResourceDirectory->NumberOfNamedEntries +
+ NameResourceDirectory->NumberOfIdEntries;
+ NameDirectoryIndex = 0;
+ for (NameDirectoryIndex=0;
+ NameDirectoryIndex<NumberOfNameDirectoryEntries;
+ NameDirectoryIndex++, NameResourceDirectoryEntry++
+ ) {
+ if (ResourceIdPathLength > 1) {
+ ScanNameDirectory = LdrpCompareResourceNames_U( ResourceIdPath[ 1 ],
+ TopResourceDirectory,
+ NameResourceDirectoryEntry
+ ) == 0;
+ }
+ else {
+ ScanNameDirectory = TRUE;
+ }
+ if (ScanNameDirectory) {
+ if (!NameResourceDirectoryEntry->DataIsDirectory) {
+ return STATUS_INVALID_IMAGE_FORMAT;
+ }
+
+ if (NameResourceDirectoryEntry->NameIsString) {
+ ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
+ ((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->NameOffset);
+
+ NameResourceNameOrId = (ULONG)ResourceNameString;
+ }
+ else {
+ NameResourceNameOrId = (ULONG)NameResourceDirectoryEntry->Id;
+ }
+
+ LangResourceDirectory = (PIMAGE_RESOURCE_DIRECTORY)
+ ((PCHAR)TopResourceDirectory + NameResourceDirectoryEntry->OffsetToDirectory);
+
+ LangResourceDirectoryEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(LangResourceDirectory+1);
+ NumberOfLangDirectoryEntries = LangResourceDirectory->NumberOfNamedEntries +
+ LangResourceDirectory->NumberOfIdEntries;
+ LangDirectoryIndex = 0;
+ for (LangDirectoryIndex=0;
+ LangDirectoryIndex<NumberOfLangDirectoryEntries;
+ LangDirectoryIndex++, LangResourceDirectoryEntry++
+ ) {
+ if (ResourceIdPathLength > 2) {
+ ReturnThisResource = LdrpCompareResourceNames_U( ResourceIdPath[ 2 ],
+ TopResourceDirectory,
+ LangResourceDirectoryEntry
+ ) == 0;
+ }
+ else {
+ ReturnThisResource = TRUE;
+ }
+ if (ReturnThisResource) {
+ if (LangResourceDirectoryEntry->DataIsDirectory) {
+ return STATUS_INVALID_IMAGE_FORMAT;
+ }
+
+ if (LangResourceDirectoryEntry->NameIsString) {
+ ResourceNameString = (PIMAGE_RESOURCE_DIR_STRING_U)
+ ((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->NameOffset);
+
+ LangResourceNameOrId = (ULONG)ResourceNameString;
+ }
+ else {
+ LangResourceNameOrId = (ULONG)LangResourceDirectoryEntry->Id;
+ }
+
+ ResourceDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)
+ ((PCHAR)TopResourceDirectory + LangResourceDirectoryEntry->OffsetToData);
+
+ ResourceInfo = &Resources[ ResourceIndex++ ];
+ if (ResourceIndex <= MaxResourceIndex) {
+ ResourceInfo->Path[ 0 ].NameOrId = TypeResourceNameOrId;
+ ResourceInfo->Path[ 1 ].NameOrId = NameResourceNameOrId;
+ ResourceInfo->Path[ 2 ].NameOrId = LangResourceNameOrId;
+ ResourceInfo->Data = (PVOID)((ULONG)DllHandle + ResourceDataEntry->OffsetToData);
+ ResourceInfo->Size = ResourceDataEntry->Size;
+ ResourceInfo->Reserved = 0;
+ }
+ else {
+ Status = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ *NumberOfResources = ResourceIndex;
+ return Status;
+}
diff --git a/private/ntos/rtl/lznt1.c b/private/ntos/rtl/lznt1.c
new file mode 100644
index 000000000..9403d387d
--- /dev/null
+++ b/private/ntos/rtl/lznt1.c
@@ -0,0 +1,2203 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ LZNT1.c
+
+Abstract:
+
+ This module implements the LZNT1 compression engine.
+
+Author:
+
+ Gary Kimura [GaryKi] 21-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#include <stdio.h>
+
+
+//
+// Declare the internal workspace that we need
+//
+
+typedef struct _LZNT1_STANDARD_WORKSPACE {
+
+ PUCHAR UncompressedBuffer;
+ PUCHAR EndOfUncompressedBufferPlus1;
+ ULONG MaxLength;
+ PUCHAR MatchedString;
+
+ PUCHAR IndexPTable[4096][2];
+
+} LZNT1_STANDARD_WORKSPACE, *PLZNT1_STANDARD_WORKSPACE;
+
+typedef struct _LZNT1_MAXIMUM_WORKSPACE {
+
+ PUCHAR UncompressedBuffer;
+ PUCHAR EndOfUncompressedBufferPlus1;
+ ULONG MaxLength;
+ PUCHAR MatchedString;
+
+} LZNT1_MAXIMUM_WORKSPACE, *PLZNT1_MAXIMUM_WORKSPACE;
+
+typedef struct _LZNT1_FRAGMENT_WORKSPACE {
+
+ UCHAR Buffer[0x1000];
+
+} LZNT1_FRAGMENT_WORKSPACE, *PLZNT1_FRAGMENT_WORKSPACE;
+
+//
+// Now define the local procedure prototypes.
+//
+
+typedef ULONG (*PLZNT1_MATCH_FUNCTION) (
+ );
+
+NTSTATUS
+LZNT1CompressChunk (
+ IN PLZNT1_MATCH_FUNCTION MatchFunction,
+ IN PUCHAR UncompressedBuffer,
+ IN PUCHAR EndOfUncompressedBufferPlus1,
+ OUT PUCHAR CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PULONG FinalCompressedChunkSize,
+ IN PVOID WorkSpace
+ );
+
+NTSTATUS
+LZNT1DecompressChunk (
+ OUT PUCHAR UncompressedBuffer,
+ IN PUCHAR EndOfUncompressedBufferPlus1,
+ IN PUCHAR CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PULONG FinalUncompressedChunkSize
+ );
+
+ULONG
+LZNT1FindMatchStandard (
+ IN PUCHAR ZivString,
+ IN PLZNT1_STANDARD_WORKSPACE WorkSpace
+ );
+
+ULONG
+LZNT1FindMatchMaximum (
+ IN PUCHAR ZivString,
+ IN PVOID WorkSpace
+ );
+
+
+//
+// Local data structures
+//
+
+//
+// The compressed chunk header is the structure that starts every
+// new chunk in the compressed data stream. In our definition here
+// we union it with a ushort to make setting and retrieving the chunk
+// header easier. The header stores the size of the compressed chunk,
+// its signature, and if the data stored in the chunk is compressed or
+// not.
+//
+// Compressed Chunk Size:
+//
+// The actual size of a compressed chunk ranges from 4 bytes (2 byte
+// header, 1 flag byte, and 1 literal byte) to 4098 bytes (2 byte
+// header, and 4096 bytes of uncompressed data). The size is encoded
+// in a 12 bit field biased by 3. A value of 1 corresponds to a chunk
+// size of 4, 2 => 5, ..., 4095 => 4098. A value of zero is special
+// because it denotes the ending chunk header.
+//
+// Chunk Signature:
+//
+// The only valid signature value is 3. This denotes a 4KB uncompressed
+// chunk using with the 4/12 to 12/4 sliding offset/length encoding.
+//
+// Is Chunk Compressed:
+//
+// If the data in the chunk is compressed this field is 1 otherwise
+// the data is uncompressed and this field is 0.
+//
+// The ending chunk header in a compressed buffer contains the a value of
+// zero (space permitting).
+//
+
+typedef union _COMPRESSED_CHUNK_HEADER {
+
+ struct {
+
+ USHORT CompressedChunkSizeMinus3 : 12;
+ USHORT ChunkSignature : 3;
+ USHORT IsChunkCompressed : 1;
+
+ } Chunk;
+
+ USHORT Short;
+
+} COMPRESSED_CHUNK_HEADER, *PCOMPRESSED_CHUNK_HEADER;
+
+#define MAX_UNCOMPRESSED_CHUNK_SIZE (4096)
+
+//
+// USHORT
+// GetCompressedChunkSize (
+// IN COMPRESSED_CHUNK_HEADER ChunkHeader
+// );
+//
+// USHORT
+// GetUncompressedChunkSize (
+// IN COMPRESSED_CHUNK_HEADER ChunkHeader
+// );
+//
+// VOID
+// SetCompressedChunkHeader (
+// IN OUT COMPRESSED_CHUNK_HEADER ChunkHeader,
+// IN USHORT CompressedChunkSize,
+// IN BOOLEAN IsChunkCompressed
+// );
+//
+
+#define GetCompressedChunkSize(CH) ( \
+ (CH).Chunk.CompressedChunkSizeMinus3 + 3 \
+)
+
+#define GetUncompressedChunkSize(CH) (MAX_UNCOMPRESSED_CHUNK_SIZE)
+
+#define SetCompressedChunkHeader(CH,CCS,ICC) { \
+ ASSERT((CCS) >= 4 && (CCS) <= 4098); \
+ (CH).Chunk.CompressedChunkSizeMinus3 = (CCS) - 3; \
+ (CH).Chunk.ChunkSignature = 3; \
+ (CH).Chunk.IsChunkCompressed = (ICC); \
+}
+
+
+//
+// Local macros
+//
+
+#define FlagOn(F,SF) ((F) & (SF))
+#define SetFlag(F,SF) { (F) |= (SF); }
+#define ClearFlag(F,SF) { (F) &= ~(SF); }
+
+#define Minimum(A,B) ((A) < (B) ? (A) : (B))
+#define Maximum(A,B) ((A) > (B) ? (A) : (B))
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+
+#pragma alloc_text(PAGE, RtlCompressWorkSpaceSizeLZNT1)
+#pragma alloc_text(PAGE, RtlCompressBufferLZNT1)
+#pragma alloc_text(PAGE, RtlDecompressBufferLZNT1)
+#pragma alloc_text(PAGE, RtlDecompressFragmentLZNT1)
+#pragma alloc_text(PAGE, RtlDescribeChunkLZNT1)
+#pragma alloc_text(PAGE, RtlReserveChunkLZNT1)
+
+#pragma alloc_text(PAGE, LZNT1CompressChunk)
+
+#if !defined(_ALPHA_)
+#if !defined(_MIPS_)
+#if !defined(_PPC_)
+#if !defined(i386)
+#pragma alloc_text(PAGE, LZNT1DecompressChunk)
+#endif
+#endif
+#endif
+#endif
+
+#pragma alloc_text(PAGE, LZNT1FindMatchStandard)
+#pragma alloc_text(PAGE, LZNT1FindMatchMaximum)
+
+#endif
+
+
+NTSTATUS
+RtlCompressWorkSpaceSizeLZNT1 (
+ IN USHORT Engine,
+ OUT PULONG CompressBufferWorkSpaceSize,
+ OUT PULONG CompressFragmentWorkSpaceSize
+ )
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+
+{
+ if (Engine == COMPRESSION_ENGINE_STANDARD) {
+
+ *CompressBufferWorkSpaceSize = sizeof(LZNT1_STANDARD_WORKSPACE);
+ *CompressFragmentWorkSpaceSize = sizeof(LZNT1_FRAGMENT_WORKSPACE);
+
+ return STATUS_SUCCESS;
+
+ } else if (Engine == COMPRESSION_ENGINE_MAXIMUM) {
+
+ *CompressBufferWorkSpaceSize = sizeof(LZNT1_MAXIMUM_WORKSPACE);
+ *CompressFragmentWorkSpaceSize = sizeof(LZNT1_FRAGMENT_WORKSPACE);
+
+ return STATUS_SUCCESS;
+
+ } else {
+
+ return STATUS_NOT_SUPPORTED;
+ }
+}
+
+
+NTSTATUS
+RtlCompressBufferLZNT1 (
+ IN USHORT Engine,
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG UncompressedChunkSize,
+ OUT PULONG FinalCompressedSize,
+ IN PVOID WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input an uncompressed buffer and produces
+ its compressed equivalent provided the compressed data fits within
+ the specified destination buffer.
+
+ An output variable indicates the number of bytes used to store
+ the compressed buffer.
+
+Arguments:
+
+ UncompressedBuffer - Supplies a pointer to the uncompressed data.
+
+ UncompressedBufferSize - Supplies the size, in bytes, of the
+ uncompressed buffer.
+
+ CompressedBuffer - Supplies a pointer to where the compressed data
+ is to be stored.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ UncompressedChunkSize - Ignored.
+
+ FinalCompressedSize - Receives the number of bytes needed in
+ the compressed buffer to store the compressed data.
+
+ WorkSpace - Mind your own business, just give it to me.
+
+Return Value:
+
+ STATUS_SUCCESS - the compression worked without a hitch.
+
+ STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
+ addition the input buffer was all zeros.
+
+ STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
+ compressed data.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PLZNT1_MATCH_FUNCTION MatchFunction;
+
+ PUCHAR UncompressedChunk;
+ PUCHAR CompressedChunk;
+ LONG CompressedChunkSize;
+
+ //
+ // The following variable is used to tell if we have processed an entire
+ // buffer of zeros and that we should return an alternate status value
+ //
+
+ BOOLEAN AllZero = TRUE;
+
+ //
+ // The following variables are pointers to the byte following the
+ // end of each appropriate buffer.
+ //
+
+ PUCHAR EndOfUncompressedBuffer = UncompressedBuffer + UncompressedBufferSize;
+ PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
+
+ //
+ // Get the match function we are to be using
+ //
+
+ if (Engine == COMPRESSION_ENGINE_STANDARD) {
+
+ MatchFunction = LZNT1FindMatchStandard;
+
+ } else if (Engine == COMPRESSION_ENGINE_MAXIMUM) {
+
+ MatchFunction = LZNT1FindMatchMaximum;
+
+ } else {
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // For each uncompressed chunk (even the odd sized ending buffer) we will
+ // try and compress the chunk
+ //
+
+ for (UncompressedChunk = UncompressedBuffer, CompressedChunk = CompressedBuffer;
+ UncompressedChunk < EndOfUncompressedBuffer;
+ UncompressedChunk += MAX_UNCOMPRESSED_CHUNK_SIZE, CompressedChunk += CompressedChunkSize) {
+
+ ASSERT(EndOfUncompressedBuffer >= UncompressedChunk);
+ ASSERT(EndOfCompressedBuffer >= CompressedChunk);
+
+ //
+ // Call the appropriate engine to compress one chunk. and
+ // return an error if we got one.
+ //
+
+ if (!NT_SUCCESS(Status = LZNT1CompressChunk( MatchFunction,
+ UncompressedChunk,
+ EndOfUncompressedBuffer,
+ CompressedChunk,
+ EndOfCompressedBuffer,
+ &CompressedChunkSize,
+ WorkSpace ))) {
+
+ return Status;
+ }
+
+ //
+ // See if we stay all zeros. If not then all zeros will become
+ // false and stay that way no matter what we later compress
+ //
+
+ AllZero = AllZero && (Status == STATUS_BUFFER_ALL_ZEROS);
+ }
+
+ //
+ // If we are not within two bytes of the end of the compressed buffer then we
+ // need to zero out two more for the ending compressed header and update
+ // the compressed chunk pointer value. Don't include these bytes in
+ // the count however, as that may force our caller to allocate an unneeded
+ // cluster, since on decompress we will terminate either on these two
+ // bytes of 0, or byte count.
+ //
+
+ if (CompressedChunk <= (EndOfCompressedBuffer - 2)) {
+
+ *(CompressedChunk) = 0;
+ *(CompressedChunk + 1) = 0;
+ }
+
+ //
+ // The final compressed size is the difference between the start of the
+ // compressed buffer and where the compressed chunk pointer was left
+ //
+
+ *FinalCompressedSize = CompressedChunk - CompressedBuffer;
+
+ //
+ // Check if the input buffer was all zeros and return the alternate status
+ // if appropriate
+ //
+
+ if (AllZero) { return STATUS_BUFFER_ALL_ZEROS; }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlDecompressBufferLZNT1 (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer and produces
+ its uncompressed equivalent provided the uncompressed data fits
+ within the specified destination buffer.
+
+ An output variable indicates the number of bytes used to store the
+ uncompressed data.
+
+Arguments:
+
+ UncompressedBuffer - Supplies a pointer to where the uncompressed
+ data is to be stored.
+
+ UncompressedBufferSize - Supplies the size, in bytes, of the
+ uncompressed buffer.
+
+ CompressedBuffer - Supplies a pointer to the compressed data.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ FinalUncompressedSize - Receives the number of bytes needed in
+ the uncompressed buffer to store the uncompressed data.
+
+Return Value:
+
+ STATUS_SUCCESS - the decompression worked without a hitch.
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PUCHAR CompressedChunk = CompressedBuffer;
+ PUCHAR UncompressedChunk = UncompressedBuffer;
+
+ COMPRESSED_CHUNK_HEADER ChunkHeader;
+ LONG SavedChunkSize;
+
+ LONG UncompressedChunkSize;
+ LONG CompressedChunkSize;
+
+ //
+ // The following to variables are pointers to the byte following the
+ // end of each appropriate buffer. This saves us from doing the addition
+ // for each loop check
+ //
+
+ PUCHAR EndOfUncompressedBuffer = UncompressedBuffer + UncompressedBufferSize;
+ PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
+
+ //
+ // Make sure that the compressed buffer is at least four bytes long to
+ // start with, and then get the first chunk header and make sure it
+ // is not an ending chunk header.
+ //
+
+ ASSERT(CompressedChunk <= EndOfCompressedBuffer - 4);
+
+ RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
+
+ ASSERT(ChunkHeader.Short != 0);
+
+ //
+ // Now while there is space in the uncompressed buffer to store data
+ // we will loop through decompressing chunks
+ //
+
+ while (TRUE) {
+
+ ASSERT(ChunkHeader.Chunk.ChunkSignature == 3);
+
+ CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
+
+ //
+ // Check that the chunk actually fits in the buffer supplied
+ // by the caller
+ //
+
+ if (CompressedChunk + CompressedChunkSize > EndOfCompressedBuffer) {
+
+ ASSERTMSG("CompressedBuffer is too small", FALSE);
+
+ *FinalUncompressedSize = (ULONG)CompressedChunk;
+
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // First make sure the chunk contains compressed data
+ //
+
+ if (ChunkHeader.Chunk.IsChunkCompressed) {
+
+ //
+ // Decompress a chunk and return if we get an error
+ //
+
+ if (!NT_SUCCESS(Status = LZNT1DecompressChunk( UncompressedChunk,
+ EndOfUncompressedBuffer,
+ CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
+ CompressedChunk + CompressedChunkSize,
+ &UncompressedChunkSize ))) {
+
+ *FinalUncompressedSize = UncompressedChunkSize;
+
+ return Status;
+ }
+
+ } else {
+
+ //
+ // The chunk does not contain compressed data so we need to simply
+ // copy over the uncompressed data
+ //
+
+ UncompressedChunkSize = GetUncompressedChunkSize( ChunkHeader );
+
+ //
+ // Make sure the data will fit into the output buffer
+ //
+
+ if (UncompressedChunk + UncompressedChunkSize > EndOfUncompressedBuffer) {
+
+ UncompressedChunkSize = EndOfUncompressedBuffer - UncompressedChunk;
+ }
+
+ //
+ // Check that the compressed chunk has this many bytes to copy.
+ //
+
+ if (CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER) + UncompressedChunkSize > EndOfCompressedBuffer) {
+
+ ASSERTMSG("CompressedBuffer is too small", FALSE);
+ *FinalUncompressedSize = (ULONG)CompressedChunk;
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ RtlCopyMemory( UncompressedChunk,
+ CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
+ UncompressedChunkSize );
+ }
+
+ //
+ // Now update the compressed and uncompressed chunk pointers with
+ // the size of the compressed chunk and the number of bytes we
+ // decompressed into, and then make sure we didn't exceed our buffers
+ //
+
+ CompressedChunk += CompressedChunkSize;
+ UncompressedChunk += UncompressedChunkSize;
+
+ ASSERT( CompressedChunk <= EndOfCompressedBuffer );
+ ASSERT( UncompressedChunk <= EndOfUncompressedBuffer );
+
+ //
+ // Now if the uncompressed is full then we are done
+ //
+
+ if (UncompressedChunk == EndOfUncompressedBuffer) { break; }
+
+ //
+ // Otherwise we need to get the next chunk header. We first
+ // check if there is one, save the old chunk size for the
+ // chunk we just read in, get the new chunk, and then check
+ // if it is the ending chunk header
+ //
+
+ if (CompressedChunk > EndOfCompressedBuffer - 2) { break; }
+
+ SavedChunkSize = GetUncompressedChunkSize(ChunkHeader);
+
+ RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
+ if (ChunkHeader.Short == 0) { break; }
+
+ //
+ // At this point we are not at the end of the uncompressed buffer
+ // and we have another chunk to process. But before we go on we
+ // need to see if the last uncompressed chunk didn't fill the full
+ // uncompressed chunk size.
+ //
+
+ if (UncompressedChunkSize < SavedChunkSize) {
+
+ LONG t1;
+ PUCHAR t2;
+
+ //
+ // Now we only need to zero out data if the really are going
+ // to process another chunk, to test for that we check if
+ // the zero will go beyond the end of the uncompressed buffer
+ //
+
+ if ((t2 = (UncompressedChunk +
+ (t1 = (SavedChunkSize -
+ UncompressedChunkSize)))) >= EndOfUncompressedBuffer) {
+
+ break;
+ }
+
+ RtlZeroMemory( UncompressedChunk, t1);
+ UncompressedChunk = t2;
+ }
+ }
+
+ //
+ // If we got out of the loop with the compressed chunk pointer beyond the
+ // end of compressed buffer then the compression buffer is ill formed.
+ //
+
+ if (CompressedChunk > EndOfCompressedBuffer) {
+
+ *FinalUncompressedSize = (ULONG)CompressedChunk;
+
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // The final uncompressed size is the difference between the start of the
+ // uncompressed buffer and where the uncompressed chunk pointer was left
+ //
+
+ *FinalUncompressedSize = UncompressedChunk - UncompressedBuffer;
+
+ //
+ // And return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlDecompressFragmentLZNT1 (
+ OUT PUCHAR UncompressedFragment,
+ IN ULONG UncompressedFragmentSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG FragmentOffset,
+ OUT PULONG FinalUncompressedSize,
+ IN PLZNT1_FRAGMENT_WORKSPACE WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer and extract an
+ uncompressed fragment.
+
+ Output bytes are copied to the fragment buffer until either the
+ fragment buffer is full or the end of the uncompressed buffer is
+ reached.
+
+ An output variable indicates the number of bytes used to store the
+ uncompressed fragment.
+
+Arguments:
+
+ UncompressedFragment - Supplies a pointer to where the uncompressed
+ fragment is to be stored.
+
+ UncompressedFragmentSize - Supplies the size, in bytes, of the
+ uncompressed fragment buffer.
+
+ CompressedBuffer - Supplies a pointer to the compressed data buffer.
+
+ CompressedBufferSize - Supplies the size, in bytes, of the
+ compressed buffer.
+
+ FragmentOffset - Supplies the offset (zero based) where the uncompressed
+ fragment is being extract from. The offset is the position within
+ the original uncompressed buffer.
+
+ FinalUncompressedSize - Receives the number of bytes needed in
+ the Uncompressed fragment buffer to store the data.
+
+ WorkSpace - Stop looking.
+
+Return Value:
+
+ STATUS_SUCCESS - the operation worked without a hitch.
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PUCHAR CompressedChunk = CompressedBuffer;
+
+ COMPRESSED_CHUNK_HEADER ChunkHeader;
+ ULONG UncompressedChunkSize;
+ ULONG CompressedChunkSize;
+
+ PUCHAR EndOfUncompressedFragment = UncompressedFragment + UncompressedFragmentSize;
+ PUCHAR EndOfCompressedBuffer = CompressedBuffer + CompressedBufferSize;
+ PUCHAR CurrentUncompressedFragment;
+
+ ULONG CopySize;
+
+ ASSERT(UncompressedFragmentSize > 0);
+
+ //
+ // Get the chunk header for the first chunk in the
+ // compressed buffer and extract the uncompressed and
+ // the compressed chunk sizes
+ //
+
+ ASSERT(CompressedChunk <= EndOfCompressedBuffer - 2);
+
+ RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
+
+ ASSERT(ChunkHeader.Short != 0);
+ ASSERT(ChunkHeader.Chunk.ChunkSignature == 3);
+
+ UncompressedChunkSize = GetUncompressedChunkSize(ChunkHeader);
+ CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
+
+ //
+ // Now we want to skip over chunks that precede the fragment
+ // we're after. To do that we'll loop until the fragment
+ // offset is within the current chunk. If it is not within
+ // the current chunk then we'll skip to the next chunk and
+ // subtract the uncompressed chunk size from the fragment offset
+ //
+
+ while (FragmentOffset >= UncompressedChunkSize) {
+
+ //
+ // Check that the chunk actually fits in the buffer supplied
+ // by the caller
+ //
+
+ if (CompressedChunk + CompressedChunkSize > EndOfCompressedBuffer) {
+
+ ASSERTMSG("CompressedBuffer is too small", FALSE);
+
+ *FinalUncompressedSize = (ULONG)CompressedChunk;
+
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // Adjust the fragment offset and move the compressed
+ // chunk pointer to the next chunk
+ //
+
+ FragmentOffset -= UncompressedChunkSize;
+ CompressedChunk += CompressedChunkSize;
+
+ //
+ // Get the next chunk header and if it is not in use
+ // then the fragment that the user wants is beyond the
+ // compressed data so we'll return a zero sized fragment
+ //
+
+ if (CompressedChunk > EndOfCompressedBuffer - 2) {
+
+ *FinalUncompressedSize = 0;
+ return STATUS_SUCCESS;
+ }
+
+ RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
+
+ if (ChunkHeader.Short == 0) {
+
+ *FinalUncompressedSize = 0;
+ return STATUS_SUCCESS;
+ }
+
+ ASSERT(ChunkHeader.Chunk.ChunkSignature == 3);
+
+ //
+ // Decode the chunk sizes for the new current chunk
+ //
+
+ UncompressedChunkSize = GetUncompressedChunkSize(ChunkHeader);
+ CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
+ }
+
+ //
+ // At this point the current chunk contains the starting point
+ // for the fragment. Now we'll loop extracting data until
+ // we've filled up the uncompressed fragment buffer or until
+ // we've run out of chunks. Both test are done near the end of
+ // the loop
+ //
+
+ CurrentUncompressedFragment = UncompressedFragment;
+
+ while (TRUE) {
+
+ //
+ // Check that the chunk actually fits in the buffer supplied
+ // by the caller
+ //
+
+ if (CompressedChunk + CompressedChunkSize > EndOfCompressedBuffer) {
+
+ ASSERTMSG("CompressedBuffer is too small", FALSE);
+
+ *FinalUncompressedSize = (ULONG)CompressedChunk;
+
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+
+ //
+ // Now we need to compute the amount of data to copy from the
+ // chunk. It will be based on either to the end of the chunk
+ // size or the amount of data the user specified
+ //
+
+ CopySize = Minimum( UncompressedChunkSize - FragmentOffset, UncompressedFragmentSize );
+
+ //
+ // Now check if the chunk contains compressed data
+ //
+
+ if (ChunkHeader.Chunk.IsChunkCompressed) {
+
+ //
+ // The chunk is compressed but now check if the amount
+ // we need to get is the entire chunk and if so then
+ // we can do the decompress straight into the caller's
+ // buffer
+ //
+
+ if ((FragmentOffset == 0) && (CopySize == UncompressedChunkSize)) {
+
+ if (!NT_SUCCESS(Status = LZNT1DecompressChunk( CurrentUncompressedFragment,
+ EndOfUncompressedFragment,
+ CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
+ CompressedChunk + CompressedChunkSize,
+ &CopySize ))) {
+
+ *FinalUncompressedSize = CopySize;
+
+ return Status;
+ }
+
+ } else {
+
+ //
+ // The caller wants only a portion of this compressed chunk
+ // so we need to read it into our work buffer and then copy
+ // the parts from the work buffer into the caller's buffer
+ //
+
+ if (!NT_SUCCESS(Status = LZNT1DecompressChunk( (PUCHAR)WorkSpace,
+ &WorkSpace->Buffer[0] + sizeof(LZNT1_FRAGMENT_WORKSPACE),
+ CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER),
+ CompressedChunk + CompressedChunkSize,
+ &UncompressedChunkSize ))) {
+
+ *FinalUncompressedSize = UncompressedChunkSize;
+
+ return Status;
+ }
+
+ //
+ // If we got less than we were looking for then we are at the
+ // end of the file. Remember the real uncompressed size and
+ // break out of the loop.
+ //
+
+ if ((UncompressedChunkSize - FragmentOffset) < CopySize) {
+
+ RtlCopyMemory( CurrentUncompressedFragment,
+ &WorkSpace->Buffer[ FragmentOffset ],
+ (UncompressedChunkSize - FragmentOffset) );
+
+ CurrentUncompressedFragment += (UncompressedChunkSize - FragmentOffset);
+ break;
+ }
+
+ RtlCopyMemory( CurrentUncompressedFragment,
+ &WorkSpace->Buffer[ FragmentOffset ],
+ CopySize );
+ }
+
+ } else {
+
+ //
+ // The chunk is not compressed so we can do a simple copy of the
+ // data. First verify that the compressed buffer holds this much
+ // data.
+ //
+
+ if (CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER) + FragmentOffset + CopySize > EndOfCompressedBuffer) {
+
+ ASSERTMSG("CompressedBuffer is too small", FALSE);
+ *FinalUncompressedSize = (ULONG)CompressedChunk;
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ RtlCopyMemory( CurrentUncompressedFragment,
+ CompressedChunk + sizeof(COMPRESSED_CHUNK_HEADER) + FragmentOffset,
+ CopySize );
+ }
+
+ //
+ // Now that we've done at least one copy make sure the fragment
+ // offset is set to zero so the next time through the loop will
+ // start at the right offset
+ //
+
+ FragmentOffset = 0;
+
+ //
+ // Adjust the uncompressed fragment information by moving the
+ // pointer up by the copy size and subtracting copy size from
+ // the amount of data the user wants
+ //
+
+ CurrentUncompressedFragment += CopySize;
+ UncompressedFragmentSize -= CopySize;
+
+ //
+ // Now if the uncompressed fragment size is zero then we're
+ // done
+ //
+
+ if (UncompressedFragmentSize == 0) { break; }
+
+ //
+ // Otherwise the user wants more data so we'll move to the
+ // next chunk, and then check if the chunk is is use. If
+ // it is not in use then we the user is trying to read beyond
+ // the end of compressed data so we'll break out of the loop
+ //
+
+ CompressedChunk += CompressedChunkSize;
+
+ if (CompressedChunk > EndOfCompressedBuffer - 2) { break; }
+
+ RtlRetrieveUshort( &ChunkHeader, CompressedChunk );
+
+ if (ChunkHeader.Short == 0) { break; }
+
+ ASSERT(ChunkHeader.Chunk.ChunkSignature == 3);
+
+ //
+ // Decode the chunk sizes for the new current chunk
+ //
+
+ UncompressedChunkSize = GetUncompressedChunkSize(ChunkHeader);
+ CompressedChunkSize = GetCompressedChunkSize(ChunkHeader);
+ }
+
+ //
+ // Now either we finished filling up the caller's buffer (and
+ // uncompressed fragment size is zero) or we've exhausted the
+ // compresed buffer (and chunk header is zero). In either case
+ // we're done and we can now compute the size of the fragment
+ // that we're returning to the caller it is simply the difference
+ // between the start of the buffer and the current position
+ //
+
+ *FinalUncompressedSize = CurrentUncompressedFragment - UncompressedFragment;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlDescribeChunkLZNT1 (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ OUT PULONG ChunkSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed buffer, and returns
+ a description of the current chunk in that buffer, updating
+ the CompressedBuffer pointer to point to the next chunk (if
+ there is one).
+
+Arguments:
+
+ CompressedBuffer - Supplies a pointer to the current chunk in
+ the compressed data, and returns pointing to the next chunk
+
+ EndOfCompressedBufferPlus1 - Points at first byte beyond
+ compressed buffer
+
+ ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
+ is nonzero, else undefined
+
+ ChunkSize - Receives the size of the current chunk pointed
+ to by CompressedBuffer. Returns 0 if STATUS_NO_MORE_ENTRIES.
+
+Return Value:
+
+ STATUS_SUCCESS - the chunk size is being returned
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+ STATUS_NO_MORE_ENTRIES - There is no chunk at the current pointer.
+
+--*/
+
+{
+ COMPRESSED_CHUNK_HEADER ChunkHeader;
+ NTSTATUS Status = STATUS_NO_MORE_ENTRIES;
+
+ //
+ // First initialize outputs
+ //
+
+ *ChunkBuffer = *CompressedBuffer;
+ *ChunkSize = 0;
+
+ //
+ // Make sure that the compressed buffer is at least four bytes long to
+ // start with, otherwise just return a zero chunk.
+ //
+
+ if (*CompressedBuffer <= EndOfCompressedBufferPlus1 - 4) {
+
+ RtlRetrieveUshort( &ChunkHeader, *CompressedBuffer );
+
+ //
+ // Check for end of chunks, terminated by USHORT of 0.
+ // First assume there are no more.
+ //
+
+ if (ChunkHeader.Short != 0) {
+
+ Status = STATUS_SUCCESS;
+
+ *ChunkSize = GetCompressedChunkSize(ChunkHeader);
+ *CompressedBuffer += *ChunkSize;
+
+ //
+ // Check that the chunk actually fits in the buffer supplied
+ // by the caller. If not, restore *CompressedBuffer for debug!
+ //
+
+ if ((*CompressedBuffer > EndOfCompressedBufferPlus1) ||
+ (ChunkHeader.Chunk.ChunkSignature != 3)) {
+
+ ASSERTMSG("CompressedBuffer is bad or too small", FALSE);
+
+ *CompressedBuffer -= *ChunkSize;
+
+ Status = STATUS_BAD_COMPRESSION_BUFFER;
+
+ //
+ // First make sure the chunk contains compressed data
+ //
+
+ } else if (!ChunkHeader.Chunk.IsChunkCompressed) {
+
+ //
+ // The uncompressed chunk must be exactly this size!
+ // If not, restore *CompressedBuffer for debug!
+ //
+
+ if (*ChunkSize != MAX_UNCOMPRESSED_CHUNK_SIZE + 2) {
+
+ ASSERTMSG("Uncompressed chunk is wrong size", FALSE);
+
+ *CompressedBuffer -= *ChunkSize;
+
+ Status = STATUS_BAD_COMPRESSION_BUFFER;
+
+ //
+ // The chunk does not contain compressed data so we need to
+ // remove the chunk header from the chunk description.
+ //
+
+ } else {
+
+ *ChunkBuffer += 2;
+ *ChunkSize -= 2;
+ }
+
+ //
+ // Otherwise we have a compressed chunk, and we only need to
+ // see if it is all zeros! Since the header is already interpreted,
+ // we only have to see if there is exactly one literal and if it
+ // is zero - it doesn't matter what the copy token says - we have
+ // a chunk of zeros!
+ //
+
+ } else if ((*ChunkSize == 6) && (*(*ChunkBuffer + 2) == 2) && (*(*ChunkBuffer + 3) == 0)) {
+
+ *ChunkSize = 0;
+ }
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+RtlReserveChunkLZNT1 (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ IN ULONG ChunkSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reserves space for a chunk of the specified
+ size in the buffer, writing in a chunk header if necessary
+ (uncompressed or all zeros case). On return the CompressedBuffer
+ pointer points to the next chunk.
+
+Arguments:
+
+ CompressedBuffer - Supplies a pointer to the current chunk in
+ the compressed data, and returns pointing to the next chunk
+
+ EndOfCompressedBufferPlus1 - Points at first byte beyond
+ compressed buffer
+
+ ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
+ is nonzero, else undefined
+
+ ChunkSize - Supplies the compressed size of the chunk to be received.
+ Two special values are 0 and MAX_UNCOMPRESSED_CHUNK_SIZE (4096).
+ 0 means the chunk should be filled with a pattern that equates
+ to 4096 0's. 4096 implies that the compression routine should
+ prepare to receive all of the data in uncompressed form.
+
+Return Value:
+
+ STATUS_SUCCESS - the chunk size is being returned
+
+ STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
+ compressed data.
+
+--*/
+
+{
+ COMPRESSED_CHUNK_HEADER ChunkHeader;
+ BOOLEAN Compressed;
+ PUCHAR Tail, NextChunk, DontCare;
+ ULONG Size;
+ NTSTATUS Status;
+
+ ASSERT(ChunkSize <= MAX_UNCOMPRESSED_CHUNK_SIZE);
+
+ //
+ // Calculate the address of the tail of this buffer and its
+ // size, so it can be moved before we store anything.
+ //
+
+ Tail = NextChunk = *CompressedBuffer;
+ while (NT_SUCCESS(Status = RtlDescribeChunkLZNT1( &NextChunk,
+ EndOfCompressedBufferPlus1,
+ &DontCare,
+ &Size))) {
+
+ //
+ // First time through the loop, capture the address of the next chunk.
+ //
+
+ if (Tail == *CompressedBuffer) {
+ Tail = NextChunk;
+ }
+ }
+
+ //
+ // The buffer could be invalid.
+ //
+
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+
+ //
+ // The only way to successfully terminate the loop is by finding a USHORT
+ // terminator of 0. Now calculate the size including the final USHORT
+ // we stopped on.
+ //
+
+ Size = NextChunk - Tail + sizeof(USHORT);
+
+ //
+ // First initialize outputs
+ //
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ *ChunkBuffer = *CompressedBuffer;
+
+ //
+ // Make sure that the compressed buffer is at least four bytes long to
+ // start with, otherwise just return a zero chunk.
+ //
+
+ if (*CompressedBuffer <= (EndOfCompressedBufferPlus1 - ChunkSize)) {
+
+ //
+ // If the chunk is uncompressed, then we have to adjust the
+ // chunk description for the header.
+ //
+
+ if (ChunkSize == MAX_UNCOMPRESSED_CHUNK_SIZE) {
+
+ //
+ // Increase ChunkSize to include header.
+ //
+
+ ChunkSize += 2;
+
+ //
+ // Move the tail now that we know where to put it.
+ //
+
+ if ((*CompressedBuffer + ChunkSize + Size) <= EndOfCompressedBufferPlus1) {
+
+ RtlMoveMemory( *CompressedBuffer + ChunkSize, Tail, Size );
+
+ //
+ // Build the header and store it for an uncompressed chunk.
+ //
+
+ SetCompressedChunkHeader( ChunkHeader,
+ MAX_UNCOMPRESSED_CHUNK_SIZE + 2,
+ FALSE );
+
+ RtlStoreUshort( *CompressedBuffer, ChunkHeader.Short );
+
+ //
+ // Advance to where the uncompressed data goes.
+ //
+
+ *ChunkBuffer += 2;
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Otherwise, if this is a zero chunk we have to build it.
+ //
+
+ } else if (ChunkSize == 0) {
+
+ //
+ // It takes 6 bytes to describe a chunk of zeros.
+ //
+
+ ChunkSize = 6;
+
+ if ((*CompressedBuffer + ChunkSize + Size) <= EndOfCompressedBufferPlus1) {
+
+ //
+ // Move the tail now that we know where to put it.
+ //
+
+ RtlMoveMemory( *CompressedBuffer + ChunkSize, Tail, Size );
+
+ //
+ // Build the header and store it
+ //
+
+ SetCompressedChunkHeader( ChunkHeader,
+ 6,
+ TRUE );
+
+ RtlStoreUshort( *CompressedBuffer, ChunkHeader.Short );
+
+ //
+ // Now store the mask byte with one literal and the literal
+ // is 0.
+ //
+
+ RtlStoreUshort( *CompressedBuffer + 2, (USHORT)2 );
+
+ //
+ // Now store the copy token for copying 4095 bytes from
+ // the preceding byte (stored as offset 0).
+ //
+
+ RtlStoreUshort( *CompressedBuffer + 4, (USHORT)(4095-3));
+
+ Status = STATUS_SUCCESS;
+ }
+
+ //
+ // Otherwise we have a normal compressed chunk.
+ //
+
+ } else {
+
+ //
+ // Move the tail now that we know where to put it.
+ //
+
+ if ((*CompressedBuffer + ChunkSize + Size) <= EndOfCompressedBufferPlus1) {
+
+ RtlMoveMemory( *CompressedBuffer + ChunkSize, Tail, Size );
+
+ Status = STATUS_SUCCESS;
+ }
+ }
+
+ //
+ // Advance the *CompressedBuffer before return
+ //
+
+ *CompressedBuffer += ChunkSize;
+ }
+ }
+
+ return Status;
+}
+
+
+//
+// The Copy token is two bytes in size.
+// Our definition uses a union to make it easier to set and retrieve token values.
+//
+// Copy Token
+//
+// Length Displacement
+//
+// 12 bits 3 to 4098 4 bits 1 to 16
+// 11 bits 3 to 2050 5 bits 1 to 32
+// 10 bits 3 to 1026 6 bits 1 to 64
+// 9 bits 3 to 514 7 bits 1 to 128
+// 8 bits 3 to 258 8 bits 1 to 256
+// 7 bits 3 to 130 9 bits 1 to 512
+// 6 bits 3 to 66 10 bits 1 to 1024
+// 5 bits 3 to 34 11 bits 1 to 2048
+// 4 bits 3 to 18 12 bits 1 to 4096
+//
+
+#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 };
+
+typedef union _LZNT1_COPY_TOKEN {
+
+ struct { USHORT Length : 12; USHORT Displacement : 4; } Fields412;
+ struct { USHORT Length : 11; USHORT Displacement : 5; } Fields511;
+ struct { USHORT Length : 10; USHORT Displacement : 6; } Fields610;
+ struct { USHORT Length : 9; USHORT Displacement : 7; } Fields79;
+ struct { USHORT Length : 8; USHORT Displacement : 8; } Fields88;
+ struct { USHORT Length : 7; USHORT Displacement : 9; } Fields97;
+ struct { USHORT Length : 6; USHORT Displacement : 10; } Fields106;
+ struct { USHORT Length : 5; USHORT Displacement : 11; } Fields115;
+ struct { USHORT Length : 4; USHORT Displacement : 12; } Fields124;
+
+ UCHAR Bytes[2];
+
+} LZNT1_COPY_TOKEN, *PLZNT1_COPY_TOKEN;
+
+//
+// USHORT
+// GetLZNT1Length (
+// IN COPY_TOKEN_FORMAT Format,
+// IN LZNT1_COPY_TOKEN CopyToken
+// );
+//
+// USHORT
+// GetLZNT1Displacement (
+// IN COPY_TOKEN_FORMAT Format,
+// IN LZNT1_COPY_TOKEN CopyToken
+// );
+//
+// VOID
+// SetLZNT1 (
+// IN COPY_TOKEN_FORMAT Format,
+// IN LZNT1_COPY_TOKEN CopyToken,
+// IN USHORT Length,
+// IN USHORT Displacement
+// );
+//
+
+#define GetLZNT1Length(F,CT) ( \
+ ( F == FORMAT412 ? (CT).Fields412.Length + 3 \
+ : F == FORMAT511 ? (CT).Fields511.Length + 3 \
+ : F == FORMAT610 ? (CT).Fields610.Length + 3 \
+ : F == FORMAT79 ? (CT).Fields79.Length + 3 \
+ : F == FORMAT88 ? (CT).Fields88.Length + 3 \
+ : F == FORMAT97 ? (CT).Fields97.Length + 3 \
+ : F == FORMAT106 ? (CT).Fields106.Length + 3 \
+ : F == FORMAT115 ? (CT).Fields115.Length + 3 \
+ : (CT).Fields124.Length + 3 \
+ ) \
+)
+
+#define GetLZNT1Displacement(F,CT) ( \
+ ( F == FORMAT412 ? (CT).Fields412.Displacement + 1 \
+ : F == FORMAT511 ? (CT).Fields511.Displacement + 1 \
+ : F == FORMAT610 ? (CT).Fields610.Displacement + 1 \
+ : F == FORMAT79 ? (CT).Fields79.Displacement + 1 \
+ : F == FORMAT88 ? (CT).Fields88.Displacement + 1 \
+ : F == FORMAT97 ? (CT).Fields97.Displacement + 1 \
+ : F == FORMAT106 ? (CT).Fields106.Displacement + 1 \
+ : F == FORMAT115 ? (CT).Fields115.Displacement + 1 \
+ : (CT).Fields124.Displacement + 1 \
+ ) \
+)
+
+#define SetLZNT1(F,CT,L,D) { \
+ if (F == FORMAT412) { (CT).Fields412.Length = (L) - 3; (CT).Fields412.Displacement = (D) - 1; } \
+ else if (F == FORMAT511) { (CT).Fields511.Length = (L) - 3; (CT).Fields511.Displacement = (D) - 1; } \
+ else if (F == FORMAT610) { (CT).Fields610.Length = (L) - 3; (CT).Fields610.Displacement = (D) - 1; } \
+ else if (F == FORMAT79) { (CT).Fields79.Length = (L) - 3; (CT).Fields79.Displacement = (D) - 1; } \
+ else if (F == FORMAT88) { (CT).Fields88.Length = (L) - 3; (CT).Fields88.Displacement = (D) - 1; } \
+ else if (F == FORMAT97) { (CT).Fields97.Length = (L) - 3; (CT).Fields97.Displacement = (D) - 1; } \
+ else if (F == FORMAT106) { (CT).Fields106.Length = (L) - 3; (CT).Fields106.Displacement = (D) - 1; } \
+ else if (F == FORMAT115) { (CT).Fields115.Length = (L) - 3; (CT).Fields115.Displacement = (D) - 1; } \
+ else { (CT).Fields124.Length = (L) - 3; (CT).Fields124.Displacement = (D) - 1; } \
+}
+
+
+//
+// Local support routine
+//
+
+NTSTATUS
+LZNT1CompressChunk (
+ IN PLZNT1_MATCH_FUNCTION MatchFunction,
+ IN PUCHAR UncompressedBuffer,
+ IN PUCHAR EndOfUncompressedBufferPlus1,
+ OUT PUCHAR CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PULONG FinalCompressedChunkSize,
+ IN PVOID WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input an uncompressed chunk and produces
+ one compressed chunk provided the compressed data fits within
+ the specified destination buffer.
+
+ The LZNT1 format used to store the compressed buffer.
+
+ An output variable indicates the number of bytes used to store
+ the compressed chunk.
+
+Arguments:
+
+ UncompressedBuffer - Supplies a pointer to the uncompressed chunk.
+
+ EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
+ following the end of the uncompressed buffer. This is supplied
+ instead of the size in bytes because our caller and ourselves
+ test against the pointer and by passing the pointer we get to
+ skip the code to compute it each time.
+
+ CompressedBuffer - Supplies a pointer to where the compressed chunk
+ is to be stored.
+
+ EndOfCompressedBufferPlus1 - Supplies a pointer to the next
+ byte following the end of the compressed buffer.
+
+ FinalCompressedChunkSize - Receives the number of bytes needed in
+ the compressed buffer to store the compressed chunk.
+
+Return Value:
+
+ STATUS_SUCCESS - the compression worked without a hitch.
+
+ STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
+ addition the input chunk was all zeros.
+
+ STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
+ compressed data.
+
+--*/
+
+{
+ PUCHAR EndOfCompressedChunkPlus1;
+
+ PUCHAR InputPointer;
+ PUCHAR OutputPointer;
+
+ PUCHAR FlagPointer;
+ UCHAR FlagByte;
+ ULONG FlagBit;
+
+ LONG Length;
+ LONG Displacement;
+
+ LZNT1_COPY_TOKEN CopyToken;
+
+ COMPRESSED_CHUNK_HEADER ChunkHeader;
+
+ UCHAR NullCharacter = 0;
+
+ ULONG Format = FORMAT412;
+
+ //
+ // First adjust the end of the uncompressed buffer pointer to the smaller
+ // of what we're passed in and the uncompressed chunk size. We use this
+ // to make sure we never compress more than a chunk worth at a time
+ //
+
+ if ((UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE) < EndOfUncompressedBufferPlus1) {
+
+ EndOfUncompressedBufferPlus1 = UncompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE;
+ }
+
+ //
+ // Now set the end of the compressed chunk pointer to be the smaller of the
+ // compressed size necessary to hold the data in an uncompressed form and
+ // the compressed buffer size. We use this to decide if we can't compress
+ // any more because the buffer is too small or just because the data
+ // doesn't compress very well.
+ //
+
+ if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER)) < EndOfCompressedBufferPlus1) {
+
+ EndOfCompressedChunkPlus1 = CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER);
+
+ } else {
+
+ EndOfCompressedChunkPlus1 = EndOfCompressedBufferPlus1;
+ }
+
+ //
+ // Now set the input and output pointers to the next byte we are
+ // go to process and asser that the user gave use buffers that were
+ // large enough to hold the minimum size chunks
+ //
+
+ InputPointer = UncompressedBuffer;
+ OutputPointer = CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER);
+
+ ASSERT(InputPointer < EndOfUncompressedBufferPlus1);
+ //**** ASSERT(OutputPointer + 2 <= EndOfCompressedChunkPlus1);
+
+ //
+ // The flag byte stores a copy of the flags for the current
+ // run and the flag bit denotes the current bit position within
+ // the flag that we are processing. The Flag pointer denotes
+ // where in the compressed buffer we will store the current
+ // flag byte
+ //
+
+ FlagPointer = OutputPointer++;
+ FlagBit = 0;
+ FlagByte = 0;
+
+ ChunkHeader.Short = 0;
+
+ //
+ // While there is some more data to be compressed we will do the
+ // following loop
+ //
+
+ ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->UncompressedBuffer = UncompressedBuffer;
+ ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->EndOfUncompressedBufferPlus1 = EndOfUncompressedBufferPlus1;
+ ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MaxLength = FormatMaxLength[FORMAT412];
+
+ while (InputPointer < EndOfUncompressedBufferPlus1) {
+
+ while (UncompressedBuffer + FormatMaxDisplacement[Format] < InputPointer) {
+
+ Format += 1;
+ ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MaxLength = FormatMaxLength[Format];
+ }
+
+ //
+ // Search for a string in the Lempel
+ //
+
+ Length = 0;
+ if ((InputPointer + 3) <= EndOfUncompressedBufferPlus1) {
+
+ Length = (MatchFunction)( InputPointer, WorkSpace );
+ }
+
+ //
+ // If the return length is zero then we need to output
+ // a literal. We clear the flag bit to denote the literal
+ // output the charcter and build up a character bits
+ // composite that if it is still zero when we are done then
+ // we know the uncompressed buffer contained only zeros.
+ //
+
+ if (!Length) {
+
+ //
+ // There is more data to output now make sure the output
+ // buffer is not already full and can contain at least one
+ // more byte
+ //
+
+ if (OutputPointer >= EndOfCompressedChunkPlus1) { break; }
+
+ ClearFlag(FlagByte, (1 << FlagBit));
+
+ NullCharacter |= *(OutputPointer++) = *(InputPointer++);
+
+ } else {
+
+ //
+ // We need to output two byte, now make sure that
+ // the output buffer can contain at least two more
+ // bytes.
+ //
+
+ if ((OutputPointer+1) >= EndOfCompressedChunkPlus1) { break; }
+
+ //
+ // Compute the displacement from the current pointer
+ // to the matched string
+ //
+
+ Displacement = InputPointer - ((PLZNT1_STANDARD_WORKSPACE)WorkSpace)->MatchedString;
+
+ //
+ // Make sure there is enough room in the output buffer
+ // for two bytes
+ //
+
+ if ((OutputPointer + 1) >= EndOfCompressedChunkPlus1) { break; }
+
+ SetFlag(FlagByte, (1 << FlagBit));
+
+ SetLZNT1(Format, CopyToken, Length, Displacement);
+
+ *(OutputPointer++) = CopyToken.Bytes[0];
+ *(OutputPointer++) = CopyToken.Bytes[1];
+
+ InputPointer += Length;
+ }
+
+ //
+ // Now adjust the flag bit and check if the flag byte
+ // should now be output. If so output the flag byte
+ // and scarf up a new byte in the output buffer for the
+ // next flag byte
+ //
+
+ FlagBit = (FlagBit + 1) % 8;
+
+ if (!FlagBit) {
+
+ *FlagPointer = FlagByte;
+ FlagByte = 0;
+
+ FlagPointer = (OutputPointer++);
+ }
+ }
+
+ //
+ // We've exited the preceeding loop because either the input buffer is
+ // all compressed or because we ran out of space in the output buffer.
+ // Check here if the input buffer is not exhasted (i.e., we ran out
+ // of space)
+ //
+
+ if (InputPointer < EndOfUncompressedBufferPlus1) {
+
+ //
+ // We ran out of space, but now if the total space available
+ // for the compressed chunk is equal to the uncompressed data plus
+ // the header then we will make this an uncompressed chunk and copy
+ // over the uncompressed data
+ //
+
+ if ((CompressedBuffer + MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER)) <= EndOfCompressedBufferPlus1) {
+
+ RtlCopyMemory( CompressedBuffer + sizeof(COMPRESSED_CHUNK_HEADER),
+ UncompressedBuffer,
+ MAX_UNCOMPRESSED_CHUNK_SIZE );
+
+ *FinalCompressedChunkSize = MAX_UNCOMPRESSED_CHUNK_SIZE + sizeof(COMPRESSED_CHUNK_HEADER);
+
+ SetCompressedChunkHeader( ChunkHeader,
+ (LONG)*FinalCompressedChunkSize,
+ FALSE );
+
+ RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Otherwise the input buffer really is too small to store the
+ // compressed chuunk
+ //
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // At this point the entire input buffer has been compressed so we need
+ // to output the last flag byte, provided it fits in the compressed buffer,
+ // set and store the chunk header. Now if the Flag pointer doesn't fit
+ // in the output buffer that is because it is one beyond the end and
+ // we incremented output pointer too far so now bring output pointer
+ // back down.
+ //
+
+ if (FlagPointer < EndOfCompressedChunkPlus1) {
+
+ *FlagPointer = FlagByte;
+
+ } else {
+
+ OutputPointer--;
+ }
+
+ *FinalCompressedChunkSize = (OutputPointer - CompressedBuffer);
+
+ SetCompressedChunkHeader( ChunkHeader,
+ (LONG)*FinalCompressedChunkSize,
+ TRUE );
+
+ RtlStoreUshort( CompressedBuffer, ChunkHeader.Short );
+
+ //
+ // Now if the only literal we ever output was a null then the
+ // input buffer was all zeros.
+ //
+
+ if (!NullCharacter) {
+
+ return STATUS_BUFFER_ALL_ZEROS;
+ }
+
+ //
+ // Otherwise return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+
+#if !defined(_ALPHA_)
+#if !defined(_MIPS_)
+#if !defined(_PPC_)
+#if !defined(i386)
+//
+// Local support routine
+//
+
+NTSTATUS
+LZNT1DecompressChunk (
+ OUT PUCHAR UncompressedBuffer,
+ IN PUCHAR EndOfUncompressedBufferPlus1,
+ IN PUCHAR CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PULONG FinalUncompressedChunkSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes as input a compressed chunk and produces its
+ uncompressed equivalent chunk provided the uncompressed data fits
+ within the specified destination buffer.
+
+ The compressed buffer must be stored in the LZNT1 format.
+
+ An output variable indicates the number of bytes used to store the
+ uncompressed data.
+
+Arguments:
+
+ UncompressedBuffer - Supplies a pointer to where the uncompressed
+ chunk is to be stored.
+
+ EndOfUncompressedBufferPlus1 - Supplies a pointer to the next byte
+ following the end of the uncompressed buffer. This is supplied
+ instead of the size in bytes because our caller and ourselves
+ test against the pointer and by passing the pointer we get to
+ skip the code to compute it each time.
+
+ CompressedBuffer - Supplies a pointer to the compressed chunk. (This
+ pointer has already been adjusted to point past the chunk header.)
+
+ EndOfCompressedBufferPlus1 - Supplies a pointer to the next
+ byte following the end of the compressed buffer.
+
+ FinalUncompressedChunkSize - Receives the number of bytes needed in
+ the uncompressed buffer to store the uncompressed chunk.
+
+Return Value:
+
+ STATUS_SUCCESS - the decompression worked without a hitch.
+
+ STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
+ ill-formed.
+
+--*/
+
+{
+ PUCHAR OutputPointer;
+ PUCHAR InputPointer;
+
+ UCHAR FlagByte;
+ ULONG FlagBit;
+
+ ULONG Format = FORMAT412;
+
+ //
+ // The two pointers will slide through our input and input buffer.
+ // For the input buffer we skip over the chunk header.
+ //
+
+ OutputPointer = UncompressedBuffer;
+ InputPointer = CompressedBuffer;
+
+ //
+ // The flag byte stores a copy of the flags for the current
+ // run and the flag bit denotes the current bit position within
+ // the flag that we are processing
+ //
+
+ FlagByte = *(InputPointer++);
+ FlagBit = 0;
+
+ //
+ // While we haven't exhausted either the input or output buffer
+ // we will do some more decompression
+ //
+
+ while ((OutputPointer < EndOfUncompressedBufferPlus1) && (InputPointer < EndOfCompressedBufferPlus1)) {
+
+ while (UncompressedBuffer + FormatMaxDisplacement[Format] < OutputPointer) { Format += 1; }
+
+ //
+ // Check the current flag if it is zero then the current
+ // input token is a literal byte that we simply copy over
+ // to the output buffer
+ //
+
+ if (!FlagOn(FlagByte, (1 << FlagBit))) {
+
+ *(OutputPointer++) = *(InputPointer++);
+
+ } else {
+
+ LZNT1_COPY_TOKEN CopyToken;
+ LONG Displacement;
+ LONG Length;
+
+ //
+ // The current input is a copy token so we'll get the
+ // copy token into our variable and extract the
+ // length and displacement from the token
+ //
+
+ if (InputPointer+1 >= EndOfCompressedBufferPlus1) {
+
+ *FinalUncompressedChunkSize = (ULONG)InputPointer;
+
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // Now grab the next input byte and extract the
+ // length and displacement from the copy token
+ //
+
+ CopyToken.Bytes[0] = *(InputPointer++);
+ CopyToken.Bytes[1] = *(InputPointer++);
+
+ Displacement = GetLZNT1Displacement(Format, CopyToken);
+ Length = GetLZNT1Length(Format, CopyToken);
+
+ //
+ // At this point we have the length and displacement
+ // from the copy token, now we need to make sure that the
+ // displacement doesn't send us outside the uncompressed buffer
+ //
+
+ if (Displacement > (OutputPointer - UncompressedBuffer)) {
+
+ *FinalUncompressedChunkSize = (ULONG)InputPointer;
+
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // We also need to adjust the length to keep the copy from
+ // overflowing the output buffer
+ //
+
+ if ((OutputPointer + Length) >= EndOfUncompressedBufferPlus1) {
+
+ Length = EndOfUncompressedBufferPlus1 - OutputPointer;
+ }
+
+ //
+ // Now we copy bytes. We cannot use Rtl Move Memory here because
+ // it does the copy backwards from what the LZ algorithm needs.
+ //
+
+ while (Length > 0) {
+
+ *(OutputPointer) = *(OutputPointer-Displacement);
+
+ Length -= 1;
+ OutputPointer += 1;
+ }
+ }
+
+ //
+ // Before we go back to the start of the loop we need to adjust the
+ // flag bit value (it goes from 0, 1, ... 7) and if the flag bit
+ // is back to zero we need to read in the next flag byte. In this
+ // case we are at the end of the input buffer we'll just break out
+ // of the loop because we're done.
+ //
+
+ FlagBit = (FlagBit + 1) % 8;
+
+ if (!FlagBit) {
+
+ if (InputPointer >= EndOfCompressedBufferPlus1) { break; }
+
+ FlagByte = *(InputPointer++);
+ }
+ }
+
+ //
+ // The decompression is done so now set the final uncompressed
+ // chunk size and return success to our caller
+ //
+
+ *FinalUncompressedChunkSize = OutputPointer - UncompressedBuffer;
+
+ return STATUS_SUCCESS;
+}
+#endif // i386
+#endif // _MIPS_
+#endif // _PPC_
+#endif // _ALPHA_
+
+
+//
+// Local support routine
+//
+
+ULONG
+LZNT1FindMatchStandard (
+ IN PUCHAR ZivString,
+ IN PLZNT1_STANDARD_WORKSPACE WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the compression lookup. It locates
+ a match for the ziv within a specified uncompressed buffer.
+
+Arguments:
+
+ ZivString - Supplies a pointer to the Ziv in the uncompressed buffer.
+ The Ziv is the string we want to try and find a match for.
+
+Return Value:
+
+ Returns the length of the match if the match is greater than three
+ characters otherwise return 0.
+
+--*/
+
+{
+ PUCHAR UncompressedBuffer = WorkSpace->UncompressedBuffer;
+ PUCHAR EndOfUncompressedBufferPlus1 = WorkSpace->EndOfUncompressedBufferPlus1;
+ ULONG MaxLength = WorkSpace->MaxLength;
+
+ ULONG Index;
+
+ PUCHAR FirstEntry;
+ ULONG FirstLength;
+
+ PUCHAR SecondEntry;
+ ULONG SecondLength;
+
+ //
+ // First check if the Ziv is within two bytes of the end of
+ // the uncompressed buffer, if so then we can't match
+ // three or more characters
+ //
+
+ Index = ((40543*((((ZivString[0]<<4)^ZivString[1])<<4)^ZivString[2]))>>4) & 0xfff;
+
+ FirstEntry = WorkSpace->IndexPTable[Index][0];
+ FirstLength = 0;
+
+ SecondEntry = WorkSpace->IndexPTable[Index][1];
+ SecondLength = 0;
+
+ //
+ // Check if first entry is good, and if so then get its length
+ //
+
+ if ((FirstEntry >= UncompressedBuffer) && // is it within the uncompressed buffer?
+ (FirstEntry < ZivString) &&
+
+ (FirstEntry[0] == ZivString[0]) && // do at least 3 characters match?
+ (FirstEntry[1] == ZivString[1]) &&
+ (FirstEntry[2] == ZivString[2])) {
+
+ FirstLength = 3;
+
+ while ((FirstLength < MaxLength)
+
+ &&
+
+ (ZivString + FirstLength < EndOfUncompressedBufferPlus1)
+
+ &&
+
+ (ZivString[FirstLength] == FirstEntry[FirstLength])) {
+
+ FirstLength++;
+ }
+ }
+
+ //
+ // Check if second entry is good, and if so then get its length
+ //
+
+ if ((SecondEntry >= UncompressedBuffer) && // is it within the uncompressed buffer?
+ (SecondEntry < ZivString) &&
+
+ (SecondEntry[0] == ZivString[0]) && // do at least 3 characters match?
+ (SecondEntry[1] == ZivString[1]) &&
+ (SecondEntry[2] == ZivString[2])) {
+
+ SecondLength = 3;
+
+ while ((SecondLength < MaxLength)
+
+ &&
+
+ (ZivString + SecondLength< EndOfUncompressedBufferPlus1)
+
+ &&
+
+ (ZivString[SecondLength] == SecondEntry[SecondLength])) {
+
+ SecondLength++;
+ }
+ }
+
+ if ((FirstLength >= SecondLength)) {
+
+ WorkSpace->IndexPTable[Index][1] = FirstEntry;
+ WorkSpace->IndexPTable[Index][0] = ZivString;
+
+ WorkSpace->MatchedString = FirstEntry;
+ return FirstLength;
+ }
+
+ WorkSpace->IndexPTable[Index][1] = FirstEntry;
+ WorkSpace->IndexPTable[Index][0] = ZivString;
+
+ WorkSpace->MatchedString = SecondEntry;
+ return SecondLength;
+}
+
+
+//
+// Local support routine
+//
+
+ULONG
+LZNT1FindMatchMaximum (
+ IN PUCHAR ZivString,
+ IN PLZNT1_MAXIMUM_WORKSPACE WorkSpace
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the compression lookup. It locates
+ a match for the ziv within a specified uncompressed buffer.
+
+ If the matched string is two or more characters long then this
+ routine does not update the lookup state information.
+
+Arguments:
+
+ ZivString - Supplies a pointer to the Ziv in the uncompressed buffer.
+ The Ziv is the string we want to try and find a match for.
+
+Return Value:
+
+ Returns the length of the match if the match is greater than three
+ characters otherwise return 0.
+
+--*/
+
+{
+ PUCHAR UncompressedBuffer = WorkSpace->UncompressedBuffer;
+ PUCHAR EndOfUncompressedBufferPlus1 = WorkSpace->EndOfUncompressedBufferPlus1;
+ ULONG MaxLength = WorkSpace->MaxLength;
+
+ ULONG i;
+ ULONG BestMatchedLength;
+ PUCHAR q;
+
+ //
+ // First check if the Ziv is within two bytes of the end of
+ // the uncompressed buffer, if so then we can't match
+ // three or more characters
+ //
+
+ BestMatchedLength = 0;
+
+ for (q = UncompressedBuffer; q < ZivString; q += 1) {
+
+ i = 0;
+
+ while ((i < MaxLength)
+
+ &&
+
+ (ZivString + i < EndOfUncompressedBufferPlus1)
+
+ &&
+
+ (ZivString[i] == q[i])) {
+
+ i++;
+ }
+
+ if (i >= BestMatchedLength) {
+
+ BestMatchedLength = i;
+ WorkSpace->MatchedString = q;
+ }
+ }
+
+ if (BestMatchedLength < 3) {
+
+ return 0;
+
+ } else {
+
+ return BestMatchedLength;
+ }
+}
+
diff --git a/private/ntos/rtl/message.c b/private/ntos/rtl/message.c
new file mode 100644
index 000000000..825fc95c6
--- /dev/null
+++ b/private/ntos/rtl/message.c
@@ -0,0 +1,460 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ message.c
+
+Abstract:
+
+ Message table resource accessing functions
+
+Author:
+
+ Steve Wood (stevewo) 10-Sep-1991
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "string.h"
+#include "stdio.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlFindMessage)
+#endif
+
+NTSTATUS
+RtlFindMessage(
+ IN PVOID DllHandle,
+ IN ULONG MessageTableId,
+ IN ULONG MessageLanguageId,
+ IN ULONG MessageId,
+ OUT PMESSAGE_RESOURCE_ENTRY *MessageEntry
+ )
+{
+ NTSTATUS Status;
+ ULONG NumberOfBlocks;
+ ULONG EntryIndex;
+ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
+ PMESSAGE_RESOURCE_DATA MessageData;
+ PMESSAGE_RESOURCE_BLOCK MessageBlock;
+ PCHAR s;
+ ULONG ResourceIdPath[ 3 ];
+
+ RTL_PAGED_CODE();
+
+ ResourceIdPath[ 0 ] = MessageTableId;
+ ResourceIdPath[ 1 ] = 1;
+ ResourceIdPath[ 2 ] = MessageLanguageId;
+
+ Status = LdrpSearchResourceSection_U( DllHandle,
+ ResourceIdPath,
+ 3,
+ FALSE,
+ (PVOID *)&ResourceDataEntry
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = LdrpAccessResourceData( DllHandle,
+ ResourceDataEntry,
+ (PVOID *)&MessageData,
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ NumberOfBlocks = MessageData->NumberOfBlocks;
+ MessageBlock = &MessageData->Blocks[ 0 ];
+ while (NumberOfBlocks--) {
+ if (MessageId >= MessageBlock->LowId &&
+ MessageId <= MessageBlock->HighId
+ ) {
+ s = (PCHAR)MessageData + MessageBlock->OffsetToEntries;
+ EntryIndex = MessageId - MessageBlock->LowId;
+ while (EntryIndex--) {
+ s += ((PMESSAGE_RESOURCE_ENTRY)s)->Length;
+ }
+
+ *MessageEntry = (PMESSAGE_RESOURCE_ENTRY)s;
+ return( STATUS_SUCCESS );
+ }
+
+ MessageBlock++;
+ }
+
+ return( STATUS_MESSAGE_NOT_FOUND );
+}
+
+#ifndef NTOS_KERNEL_RUNTIME
+
+#define MAX_INSERTS 200
+
+NTSTATUS
+RtlFormatMessage(
+ IN PWSTR MessageFormat,
+ IN ULONG MaximumWidth OPTIONAL,
+ IN BOOLEAN IgnoreInserts,
+ IN BOOLEAN ArgumentsAreAnsi,
+ IN BOOLEAN ArgumentsAreAnArray,
+ IN va_list *Arguments,
+ OUT PWSTR Buffer,
+ IN ULONG Length,
+ OUT PULONG ReturnLength OPTIONAL
+ )
+{
+ ULONG Column;
+ int cchRemaining, cchWritten;
+ PULONG ArgumentsArray = (PULONG)Arguments;
+ ULONG rgInserts[ MAX_INSERTS ], cSpaces;
+ ULONG MaxInsert, CurInsert;
+ ULONG PrintParameterCount;
+ ULONG PrintParameter1;
+ ULONG PrintParameter2;
+ WCHAR PrintFormatString[ 32 ];
+ BOOLEAN DefaultedFormatString;
+ WCHAR c;
+ PWSTR s, s1;
+ PWSTR lpDst, lpDstBeg, lpDstLastSpace;
+
+ cchRemaining = Length / sizeof( WCHAR );
+ lpDst = Buffer;
+ MaxInsert = 0;
+ lpDstLastSpace = NULL;
+ Column = 0;
+ s = MessageFormat;
+ while (*s != UNICODE_NULL) {
+ if (*s == L'%') {
+ s++;
+ lpDstBeg = lpDst;
+ if (*s >= L'1' && *s <= L'9') {
+ CurInsert = *s++ - L'0';
+ if (*s >= L'0' && *s <= L'9') {
+ CurInsert = (CurInsert * 10) + (*s++ - L'0');
+ if (*s >= L'0' && *s <= L'9') {
+ CurInsert = (CurInsert * 10) + (*s++ - L'0');
+ if (*s >= L'0' && *s <= L'9') {
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+ }
+ CurInsert -= 1;
+
+ PrintParameterCount = 0;
+ if (*s == L'!') {
+ DefaultedFormatString = FALSE;
+ s1 = PrintFormatString;
+ *s1++ = L'%';
+ s++;
+ while (*s != L'!') {
+ if (*s != UNICODE_NULL) {
+ if (s1 >= &PrintFormatString[ 31 ]) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ if (*s == L'*') {
+ if (PrintParameterCount++ > 1) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ *s1++ = *s++;
+ }
+ else {
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ s++;
+ *s1 = UNICODE_NULL;
+ }
+ else {
+ DefaultedFormatString = TRUE;
+ wcscpy( PrintFormatString, L"%s" );
+ s1 = PrintFormatString + wcslen( PrintFormatString );
+ }
+
+ if (IgnoreInserts) {
+ if (!wcscmp( PrintFormatString, L"%s" )) {
+ cchWritten = _snwprintf( lpDst,
+ cchRemaining,
+ L"%%%u",
+ CurInsert+1
+ );
+ }
+ else {
+ cchWritten = _snwprintf( lpDst,
+ cchRemaining,
+ L"%%%u!%s!",
+ CurInsert+1,
+ &PrintFormatString[ 1 ]
+ );
+ }
+ }
+ else
+ if (ARGUMENT_PRESENT( Arguments )) {
+ if ((CurInsert+PrintParameterCount) >= MAX_INSERTS) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ if (ArgumentsAreAnsi) {
+ if (s1[ -1 ] == L'c' && s1[ -2 ] != L'h'
+ && s1[ -2 ] != L'w' && s1[ -2 ] != L'l') {
+ wcscpy( &s1[ -1 ], L"hc" );
+ }
+ else
+ if (s1[ -1 ] == L's' && s1[ -2 ] != L'h'
+ && s1[ -2 ] != L'w' && s1[ -2 ] != L'l') {
+ wcscpy( &s1[ -1 ], L"hs" );
+ }
+ else if (s1[ -1 ] == L'S') {
+ s1[ -1 ] = L's';
+ }
+ else if (s1[ -1 ] == L'C') {
+ s1[ -1 ] = L'c';
+ }
+ }
+
+ while (CurInsert >= MaxInsert) {
+ if (ArgumentsAreAnArray) {
+ rgInserts[ MaxInsert++ ] = *((PULONG)Arguments)++;
+ }
+ else {
+ rgInserts[ MaxInsert++ ] = va_arg(*Arguments, ULONG);
+ }
+ }
+
+ s1 = (PWSTR)rgInserts[ CurInsert ];
+ PrintParameter1 = 0;
+ PrintParameter2 = 0;
+ if (PrintParameterCount > 0) {
+ if (ArgumentsAreAnArray) {
+ PrintParameter1 = rgInserts[ MaxInsert++ ] = *((PULONG)Arguments)++;
+ }
+ else {
+ PrintParameter1 = rgInserts[ MaxInsert++ ] = va_arg( *Arguments, ULONG );
+ }
+
+ if (PrintParameterCount > 1) {
+ if (ArgumentsAreAnArray) {
+ PrintParameter2 = rgInserts[ MaxInsert++ ] = *((PULONG)Arguments)++;
+ }
+ else {
+ PrintParameter2 = rgInserts[ MaxInsert++ ] = va_arg( *Arguments, ULONG );
+ }
+ }
+ }
+
+ cchWritten = _snwprintf( lpDst,
+ cchRemaining,
+ PrintFormatString,
+ s1,
+ PrintParameter1,
+ PrintParameter2
+ );
+ }
+ else {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ if ((cchRemaining -= cchWritten) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ lpDst += cchWritten;
+ }
+ else
+ if (*s == L'0') {
+ break;
+ }
+ else
+ if (!*s) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+ else
+ if (*s == L'r') {
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = L'\r';
+ s++;
+ lpDstBeg = NULL;
+ }
+ else
+ if (*s == L'n') {
+ if ((cchRemaining -= 2) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = L'\r';
+ *lpDst++ = L'\n';
+ s++;
+ lpDstBeg = NULL;
+ }
+ else
+ if (*s == L't') {
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ if (Column % 8) {
+ Column = (Column + 7) & ~7;
+ }
+ else {
+ Column += 8;
+ }
+
+ lpDstLastSpace = lpDst;
+ *lpDst++ = L'\t';
+ s++;
+ }
+ else
+ if (*s == L'b') {
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ lpDstLastSpace = lpDst;
+ *lpDst++ = L' ';
+ s++;
+ }
+ else
+ if (IgnoreInserts) {
+ if ((cchRemaining -= 2) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = L'%';
+ *lpDst++ = *s++;
+ }
+ else {
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = *s++;
+ }
+
+ if (lpDstBeg == NULL) {
+ lpDstLastSpace = NULL;
+ Column = 0;
+ }
+ else {
+ Column += lpDst - lpDstBeg;
+ }
+ }
+ else {
+ c = *s++;
+ if (c == L'\r' || c == L'\n') {
+ if ((c == L'\n' && *s == L'\r') ||
+ (c == L'\r' && *s == L'\n')
+ ) {
+ s++;
+ }
+
+ if (MaximumWidth != 0) {
+ lpDstLastSpace = lpDst;
+ c = L' ';
+ }
+ else {
+ c = L'\n';
+ }
+ }
+
+
+ if (c == L'\n') {
+ if ((cchRemaining -= 2) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = L'\r';
+ *lpDst++ = L'\n';
+ lpDstLastSpace = NULL;
+ Column = 0;
+ }
+ else {
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ if (c == L' ') {
+ lpDstLastSpace = lpDst;
+ }
+
+ *lpDst++ = c;
+ Column += 1;
+ }
+ }
+
+ if (MaximumWidth != 0 &&
+ MaximumWidth != 0xFFFFFFFF &&
+ Column >= MaximumWidth
+ ) {
+ if (lpDstLastSpace != NULL) {
+ lpDstBeg = lpDstLastSpace;
+ while (*lpDstBeg == L' ' || *lpDstBeg == L'\t') {
+ lpDstBeg += 1;
+ if (lpDstBeg == lpDst) {
+ break;
+ }
+ }
+ while (lpDstLastSpace > Buffer) {
+ if (lpDstLastSpace[ -1 ] == L' ' || lpDstLastSpace[ -1 ] == L'\t') {
+ lpDstLastSpace -= 1;
+ }
+ else {
+ break;
+ }
+ }
+
+ cSpaces = lpDstBeg - lpDstLastSpace;
+ if (cSpaces == 1) {
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ if (cSpaces > 2) {
+ cchRemaining += (cSpaces - 2);
+ }
+
+ memmove( lpDstLastSpace + 2,
+ lpDstBeg,
+ (lpDst - lpDstBeg) * sizeof( WCHAR )
+ );
+ *lpDstLastSpace++ = L'\r';
+ *lpDstLastSpace++ = L'\n';
+ Column = lpDst - lpDstBeg;
+ lpDst = lpDstLastSpace + Column;
+ lpDstLastSpace = NULL;
+ }
+ else {
+ if ((cchRemaining -= 2) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = L'\r';
+ *lpDst++ = L'\n';
+ lpDstLastSpace = NULL;
+ Column = 0;
+ }
+ }
+ }
+
+ if ((cchRemaining -= 1) <= 0) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+
+ *lpDst++ = '\0';
+ if ( ARGUMENT_PRESENT(ReturnLength) ) {
+ *ReturnLength = (lpDst - Buffer) * sizeof( WCHAR );
+ }
+ return( STATUS_SUCCESS );
+}
+#endif
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
diff --git a/private/ntos/rtl/mkit.cmd b/private/ntos/rtl/mkit.cmd
new file mode 100644
index 000000000..1faad9671
--- /dev/null
+++ b/private/ntos/rtl/mkit.cmd
@@ -0,0 +1,22 @@
+@echo off
+setlocal
+set _nokrnl=0
+if "%1" == "user" set _nokrnl=1
+if "%1" == "user" cd user
+if "%1" == "user" shift
+set _bldflags=-z
+if NOT "%1" == "" set _bldflags=%1 %2 %3 %4 %5
+build -z -M 4 %_bldflags%
+if ERRORLEVEL 1 goto done
+cd \nt\private\ntos\dll
+build -z %_bldflags%
+if ERRORLEVEL 1 goto done
+if "%_nokrnl%" == "1" goto done
+cd \nt\private\ntos\init
+build -z -M 2
+if ERRORLEVEL 1 goto done
+cd \nt\private\ntos\boot
+build -z -M 4
+:done
+cd ..\rtl
+endlocal
diff --git a/private/ntos/rtl/mp/alpha/sources b/private/ntos/rtl/mp/alpha/sources
new file mode 100644
index 000000000..c7016ebc1
--- /dev/null
+++ b/private/ntos/rtl/mp/alpha/sources
@@ -0,0 +1,14 @@
+ALPHA_SOURCES=..\alpha\capture.s \
+ ..\alpha\context.c \
+ ..\alpha\debugstb.s \
+ ..\alpha\exdsptch.c \
+ ..\alpha\getcalr.c \
+ ..\alpha\largeint.s \
+ ..\alpha\lzntaxp.s \
+ ..\alpha\ntcurteb.s \
+ ..\alpha\mvmem.s \
+ ..\alpha\trampoln.s \
+ ..\alpha\unwindr.c \
+ ..\alpha\chandler.c \
+ ..\alpha\ghandler.c \
+ ..\alpha\xcptmisc.s
diff --git a/private/ntos/rtl/mp/i386/sources b/private/ntos/rtl/mp/i386/sources
new file mode 100644
index 000000000..a609b20e6
--- /dev/null
+++ b/private/ntos/rtl/mp/i386/sources
@@ -0,0 +1,17 @@
+i386_SOURCES=..\i386\context.c \
+ ..\i386\debug3.c \
+ ..\i386\debug2.asm \
+ ..\i386\divlarge.c \
+ ..\i386\exdsptch.c \
+ ..\i386\stkwalk.asm \
+ ..\i386\stringsp.asm \
+ ..\i386\ioaccess.asm \
+ ..\i386\largeint.asm \
+ ..\i386\lzntx86.asm \
+ ..\i386\movemem.asm \
+ ..\i386\ntcurteb.asm \
+ ..\i386\raise.asm \
+ ..\i386\raisests.c \
+ ..\i386\rtldump.c \
+ ..\i386\xcptmisc.asm \
+ ..\i386\halvprnt.c
diff --git a/private/ntos/rtl/mp/makefile b/private/ntos/rtl/mp/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/rtl/mp/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/rtl/mp/makefile.inc b/private/ntos/rtl/mp/makefile.inc
new file mode 100644
index 000000000..f64d258e3
--- /dev/null
+++ b/private/ntos/rtl/mp/makefile.inc
@@ -0,0 +1 @@
+..\error.c: ..\error.h
diff --git a/private/ntos/rtl/mp/mips/sources b/private/ntos/rtl/mp/mips/sources
new file mode 100644
index 000000000..6c6096add
--- /dev/null
+++ b/private/ntos/rtl/mp/mips/sources
@@ -0,0 +1,11 @@
+MIPS_SOURCES=..\chandler.c \
+ ..\mips\context.c \
+ ..\mips\debugstb.s \
+ ..\mips\exdsptch.c \
+ ..\mips\getcalr.c \
+ ..\mips\largeint.s \
+ ..\mips\lzntmips.s \
+ ..\mips\trampoln.s \
+ ..\mips\xcptmisc.s \
+ ..\mips\xxcaptur.s \
+ ..\mips\xxmvmem.s
diff --git a/private/ntos/rtl/mp/ppc/sources b/private/ntos/rtl/mp/ppc/sources
new file mode 100644
index 000000000..933e58555
--- /dev/null
+++ b/private/ntos/rtl/mp/ppc/sources
@@ -0,0 +1,10 @@
+PPC_SOURCES=..\ppc\chandler.c \
+ ..\ppc\context.c \
+ ..\ppc\debugstb.s \
+ ..\ppc\exdsptch.c \
+ ..\ppc\getcalr.c \
+ ..\ppc\largeint.s \
+ ..\ppc\lzntppc.s \
+ ..\ppc\movemem.s \
+ ..\ppc\trampoln.s \
+ ..\ppc\xcptmisc.s
diff --git a/private/ntos/rtl/mp/sources b/private/ntos/rtl/mp/sources
new file mode 100644
index 000000000..294ee0bbb
--- /dev/null
+++ b/private/ntos/rtl/mp/sources
@@ -0,0 +1,85 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=krtl
+
+NT_UP=0
+
+TARGETNAME=ntosrtl
+TARGETPATH=..\..\mpobj
+TARGETTYPE=LIBRARY
+GPSIZE=32
+
+INCLUDES=..;..\..\inc;..\..\..\inc
+
+NTPROFILEINPUT=yes
+
+C_DEFINES=$(C_DEFINES) -D_NTSYSTEM_ -DNTOS_KERNEL_RUNTIME=1
+
+ASM_DEFINES=-DNTOS_KERNEL_RUNTIME=1
+
+SOURCES=..\acledit.c \
+ ..\assert.c \
+ ..\atom.c \
+ ..\bitmap.c \
+ ..\compress.c \
+ ..\cnvint.c \
+ ..\debug.c \
+ ..\eballoc.c \
+ ..\environ.c \
+ ..\error.c \
+ ..\excptdbg.c \
+ ..\gentable.c \
+ ..\gen8dot3.c \
+ ..\heap.c \
+ ..\imagedir.c \
+ ..\checksum.c \
+ ..\ldrrsrc.c \
+ ..\ldrreloc.c \
+ ..\lznt1.c \
+ ..\message.c \
+ ..\nls.c \
+ ..\pctohdr.c \
+ ..\prefix.c \
+ ..\prodtype.c \
+ ..\random.c \
+ ..\registry.c \
+ ..\regutil.c \
+ ..\rtlassig.c \
+ ..\rtldata.c \
+ ..\rtlexec.c \
+ ..\rxact.c \
+ ..\sertl.c \
+ ..\splay.c \
+ ..\string.c \
+ ..\stktrace.c \
+ ..\time.c \
+ ..\nlsxlat.c \
+ ..\eventlog.c \
+ ..\trace.c
+
+UMTYPE=console
+
+NTTARGETFILE0=..\error.c
diff --git a/private/ntos/rtl/mrcf.c b/private/ntos/rtl/mrcf.c
new file mode 100644
index 000000000..bf81bbb56
--- /dev/null
+++ b/private/ntos/rtl/mrcf.c
@@ -0,0 +1,597 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ mrcf.c
+
+Abstract:
+
+ This module implements the Mrcf compression engine.
+
+Author:
+
+ Gary Kimura [GaryKi] 21-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#include <stdio.h>
+
+
+//
+// To decompress/compress a block of data the user needs to
+// provide a work space as an extra parameter to all the exported
+// procedures. That way the routines will not need to use excessive
+// stack space and will still be multithread safe
+//
+
+//
+// Variables for reading and writing bits
+//
+
+typedef struct _MRCF_BIT_IO {
+
+ USHORT abitsBB; // 16-bit buffer being read
+ LONG cbitsBB; // Number of bits left in abitsBB
+
+ PUCHAR pbBB; // Pointer to byte stream being read
+ ULONG cbBB; // Number of bytes left in pbBB
+ ULONG cbBBInitial; // Initial size of pbBB
+
+} MRCF_BIT_IO;
+typedef MRCF_BIT_IO *PMRCF_BIT_IO;
+
+//
+// Maximum back-pointer value, also used to indicate end of compressed stream!
+//
+
+#define wBACKPOINTERMAX (4415)
+
+//
+// MDSIGNATURE - Signature at start of each compressed block
+//
+// This 4-byte signature is used as a check to ensure that we
+// are decompressing data we compressed, and also to indicate
+// which compression method was used.
+//
+// NOTE: A compressed block consists of one or more "chunks", separated
+// by the bitsEND_OF_STREAM pattern.
+//
+// Byte Word
+// ----------- ---------
+// 0 1 2 3 0 1 Meaning
+// -- -- -- -- ---- ---- ----------------
+// 44 53 00 01 5344 0100 MaxCompression
+// 44 53 00 02 5344 0200 StandardCompression
+//
+// NOTE: The *WORD* values are listed to be clear about the
+// byte ordering!
+//
+
+typedef struct _MDSIGNATURE {
+
+ //
+ // Must be MD_STAMP
+ //
+
+ USHORT sigStamp;
+
+ //
+ // mdsSTANDARD or mdsMAX
+ //
+
+ USHORT sigType;
+
+} MDSIGNATURE;
+typedef MDSIGNATURE *PMDSIGNATURE;
+
+#define MD_STAMP 0x5344 // Signature stamp at start of compressed blk
+#define MASK_VALID_mds 0x0300 // All other bits must be zero
+
+
+//
+// Local procedure declarations and macros
+//
+
+#define minimum(a,b) (a < b ? a : b)
+
+//
+// Local procedure prototypes
+//
+
+VOID
+MrcfSetBitBuffer (
+ PUCHAR pb,
+ ULONG cb,
+ PMRCF_BIT_IO BitIo
+ );
+
+VOID
+MrcfFillBitBuffer (
+ PMRCF_BIT_IO BitIo
+ );
+
+USHORT
+MrcfReadBit (
+ PMRCF_BIT_IO BitIo
+ );
+
+USHORT
+MrcfReadNBits (
+ LONG cbits,
+ PMRCF_BIT_IO BitIo
+ );
+
+
+NTSTATUS
+RtlDecompressBufferMrcf (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decompresses a buffer of StandardCompressed or MaxCompressed
+ data.
+
+Arguments:
+
+ UncompressedBuffer - buffer to receive uncompressed data
+
+ UncompressedBufferSize - length of UncompressedBuffer
+
+ NOTE: UncompressedBufferSize must be the EXACT length of the uncompressed
+ data, as Decompress uses this information to detect
+ when decompression is complete. If this value is
+ incorrect, Decompress may crash!
+
+ CompressedBuffer - buffer containing compressed data
+
+ CompressedBufferSize - length of CompressedBuffer
+
+ WorkSpace - pointer to a private work area for use by this operation
+
+Return Value:
+
+ ULONG - Returns the size of the decompressed data in bytes. Returns 0 if
+ there was an error in the decompress.
+
+--*/
+
+{
+ MRCF_BIT_IO WorkSpace;
+
+ ULONG cbMatch; // Length of match string
+ ULONG i; // Index in UncompressedBuffer to receive decoded data
+ ULONG iMatch; // Index in UncompressedBuffer of matched string
+ ULONG k; // Number of bits in length string
+ ULONG off; // Offset from i in UncompressedBuffer of match string
+ USHORT x; // Current bit being examined
+ ULONG y;
+
+ //
+ // verify that compressed data starts with proper signature
+ //
+
+ if (CompressedBufferSize < sizeof(MDSIGNATURE) || // Must have signature
+ ((PMDSIGNATURE)CompressedBuffer)->sigStamp != MD_STAMP || // Stamp must be OK
+ ((PMDSIGNATURE)CompressedBuffer)->sigType & (~MASK_VALID_mds)) { // Type must be OK
+
+ *FinalUncompressedSize = 0;
+ return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+
+ //
+ // Skip over the valid signature
+ //
+
+ CompressedBufferSize -= sizeof(MDSIGNATURE);
+ CompressedBuffer += sizeof(MDSIGNATURE);
+
+ //
+ // Set up for decompress, start filling UncompressedBuffer at front
+ //
+
+ i = 0;
+
+ //
+ // Set statics to save parm passing
+ //
+
+ MrcfSetBitBuffer(CompressedBuffer,CompressedBufferSize,&WorkSpace);
+
+ while (TRUE) {
+
+ y = MrcfReadNBits(2,&WorkSpace);
+
+ //
+ // Check if next 7 bits are a byte
+ // 1 if 128..255 (0x80..0xff), 2 if 0..127 (0x00..0x7f)
+ //
+
+ if (y == 1 || y == 2) {
+
+ ASSERTMSG("Don't exceed expected length ", i<UncompressedBufferSize);
+
+ UncompressedBuffer[i] = (UCHAR)((y == 1 ? 0x80 : 0) | MrcfReadNBits(7,&WorkSpace));
+
+ i++;
+
+ } else {
+
+ //
+ // Have match sequence
+ //
+
+ //
+ // Get the offset
+ //
+
+ if (y == 0) {
+
+ //
+ // next 6 bits are offset
+ //
+
+ off = MrcfReadNBits(6,&WorkSpace);
+
+ ASSERTMSG("offset 0 is invalid ", off != 0);
+
+ } else {
+
+ x = MrcfReadBit(&WorkSpace);
+
+ if (x == 0) {
+
+ //
+ // next 8 bits are offset-64 (0x40)
+ //
+
+ off = MrcfReadNBits(8, &WorkSpace) + 64;
+
+ } else {
+
+ //
+ // next 12 bits are offset-320 (0x140)
+ //
+
+ off = MrcfReadNBits(12, &WorkSpace) + 320;
+
+ if (off == wBACKPOINTERMAX) {
+
+ //
+ // EOS marker
+ //
+
+ if (i >= UncompressedBufferSize) {
+
+ //
+ // Done with entire buffer
+ //
+
+ *FinalUncompressedSize = i;
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // More to do
+ // Done with a 512-byte chunk
+ //
+
+ continue;
+ }
+ }
+ }
+ }
+
+ ASSERTMSG("Don't exceed expected length ", i<UncompressedBufferSize);
+ ASSERTMSG("Cannot match before start of uncoded buffer! ", off <= i);
+
+ //
+ // Get the length - logarithmically encoded
+ //
+
+ for (k=0; (x=MrcfReadBit(&WorkSpace)) == 0; k++) { NOTHING; }
+
+ ASSERT(k <= 8);
+
+ if (k == 0) {
+
+ //
+ // All matches at least 2 chars long
+ //
+
+ cbMatch = 2;
+
+ } else {
+
+ cbMatch = (1 << k) + 1 + MrcfReadNBits(k, &WorkSpace);
+ }
+
+ ASSERTMSG("Don't exceed buffer size ", (i - off + cbMatch - 1) <= UncompressedBufferSize);
+
+ //
+ // Copy the matched string
+ //
+
+ iMatch = i - off;
+
+ while ( (cbMatch > 0) && (i<UncompressedBufferSize) ) {
+
+ UncompressedBuffer[i++] = UncompressedBuffer[iMatch++];
+ cbMatch--;
+ }
+
+ ASSERTMSG("Should have copied it all ", cbMatch == 0);
+ }
+ }
+}
+
+
+//
+// Internal Support Routine
+//
+
+VOID
+MrcfSetBitBuffer (
+ PUCHAR pb,
+ ULONG cb,
+ PMRCF_BIT_IO BitIo
+ )
+
+/*++
+
+Routine Description:
+
+ Set statics with coded buffer pointer and length
+
+Arguments:
+
+ pb - pointer to compressed data buffer
+
+ cb - length of compressed data buffer
+
+ BitIo - Supplies a pointer to the bit buffer statics
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BitIo->pbBB = pb;
+ BitIo->cbBB = cb;
+ BitIo->cbBBInitial = cb;
+ BitIo->cbitsBB = 0;
+ BitIo->abitsBB = 0;
+}
+
+
+//
+// Internal Support Routine
+//
+
+USHORT
+MrcfReadBit (
+ PMRCF_BIT_IO BitIo
+ )
+
+/*++
+
+Routine Description:
+
+ Get next bit from bit buffer
+
+Arguments:
+
+ BitIo - Supplies a pointer to the bit buffer statics
+
+Return Value:
+
+ USHORT - Returns next bit (0 or 1)
+
+--*/
+
+{
+ USHORT bit;
+
+ //
+ // Check if no bits available
+ //
+
+ if ((BitIo->cbitsBB) == 0) {
+
+ MrcfFillBitBuffer(BitIo);
+ }
+
+ //
+ // Decrement the bit count
+ // get the bit, remove it, and return the bit
+ //
+
+ (BitIo->cbitsBB)--;
+ bit = (BitIo->abitsBB) & 1;
+ (BitIo->abitsBB) >>= 1;
+
+ return bit;
+}
+
+
+//
+// Internal Support Routine
+//
+
+USHORT
+MrcfReadNBits (
+ LONG cbits,
+ PMRCF_BIT_IO BitIo
+ )
+
+/*++
+
+Routine Description:
+
+ Get next N bits from bit buffer
+
+Arguments:
+
+ cbits - count of bits to get
+
+ BitIo - Supplies a pointer to the bit buffer statics
+
+Return Value:
+
+ USHORT - Returns next cbits bits.
+
+--*/
+
+{
+ ULONG abits; // Bits to return
+ LONG cbitsPart; // Partial count of bits
+ ULONG cshift; // Shift count
+ ULONG mask; // Mask
+
+ //
+ // Largest number of bits we should read at one time is 12 bits for
+ // a 12-bit offset. The largest length field component that we
+ // read is 8 bits. If this routine were used for some other purpose,
+ // it can support up to 15 (NOT 16) bit reads, due to how the masking
+ // code works.
+ //
+
+ ASSERT(cbits <= 12);
+
+ //
+ // No shift and no bits yet
+ //
+
+ cshift = 0;
+ abits = 0;
+
+ while (cbits > 0) {
+
+ //
+ // If not bits available get some bits
+ //
+
+ if ((BitIo->cbitsBB) == 0) {
+
+ MrcfFillBitBuffer(BitIo);
+ }
+
+ //
+ // Number of bits we can read
+ //
+
+ cbitsPart = minimum((BitIo->cbitsBB), cbits);
+
+ //
+ // Mask for bits we want, extract and store them
+ //
+
+ mask = (1 << cbitsPart) - 1;
+ abits |= ((BitIo->abitsBB) & mask) << cshift;
+
+ //
+ // Remember the next chunk of bits
+ //
+
+ cshift = cbitsPart;
+
+ //
+ // Update bit buffer, move remaining bits down and
+ // update count of bits left
+ //
+
+ (BitIo->abitsBB) >>= cbitsPart;
+ (BitIo->cbitsBB) -= cbitsPart;
+
+ //
+ // Update count of bits left to read
+ //
+
+ cbits -= cbitsPart;
+ }
+
+ //
+ // Return requested bits
+ //
+
+ return (USHORT)abits;
+}
+
+
+//
+// Internal Support Routine
+//
+
+VOID
+MrcfFillBitBuffer (
+ PMRCF_BIT_IO BitIo
+ )
+
+/*++
+
+Routine Description:
+
+ Fill abitsBB from static bit buffer
+
+Arguments:
+
+ BitIo - Supplies a pointer to the bit buffer statics
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ASSERT((BitIo->cbitsBB) == 0);
+
+ switch (BitIo->cbBB) {
+
+ case 0:
+
+ ASSERTMSG("no bits left in coded buffer!", FALSE);
+
+ break;
+
+ case 1:
+
+ //
+ // Get last byte and adjust count
+ //
+
+ BitIo->cbitsBB = 8;
+ BitIo->abitsBB = *(BitIo->pbBB)++;
+ BitIo->cbBB--;
+
+ break;
+
+ default:
+
+ //
+ // Get word and adjust count
+ //
+
+ BitIo->cbitsBB = 16;
+ BitIo->abitsBB = *((USHORT *)(BitIo->pbBB))++;
+ BitIo->cbBB -= 2;
+
+ break;
+ }
+}
+
diff --git a/private/ntos/rtl/nls.c b/private/ntos/rtl/nls.c
new file mode 100644
index 000000000..13602d43f
--- /dev/null
+++ b/private/ntos/rtl/nls.c
@@ -0,0 +1,2429 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ nls.c
+
+Abstract:
+
+ This module implements NLS support functions for NT.
+
+Author:
+
+ Mark Lucovsky (markl) 16-Apr-1991
+
+Environment:
+
+ Kernel or user-mode
+
+Revision History:
+
+ 16-Feb-1993 JulieB Added Upcase Rtl Routines.
+ 08-Mar-1993 JulieB Moved Upcase Macro to ntrtlp.h.
+ 02-Apr-1993 JulieB Fixed RtlAnsiCharToUnicodeChar to use transl. tbls.
+ 02-Apr-1993 JulieB Fixed BUFFER_TOO_SMALL check.
+ 28-May-1993 JulieB Fixed code to properly handle DBCS.
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlAnsiStringToUnicodeString)
+#pragma alloc_text(PAGE,RtlAnsiCharToUnicodeChar)
+#pragma alloc_text(PAGE,RtlOemStringToUnicodeString)
+#pragma alloc_text(PAGE,RtlUnicodeStringToAnsiString)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeStringToAnsiString)
+#pragma alloc_text(PAGE,RtlUnicodeStringToOemString)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeStringToOemString)
+#pragma alloc_text(PAGE,RtlOemStringToCountedUnicodeString)
+#pragma alloc_text(PAGE,RtlUnicodeStringToCountedOemString)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeStringToCountedOemString)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeString)
+#pragma alloc_text(PAGE,RtlDowncaseUnicodeString)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeChar)
+#pragma alloc_text(PAGE,RtlFreeUnicodeString)
+#pragma alloc_text(PAGE,RtlFreeAnsiString)
+#pragma alloc_text(PAGE,RtlFreeOemString)
+#pragma alloc_text(PAGE,RtlCreateUnicodeString)
+#pragma alloc_text(PAGE,RtlEqualDomainName)
+#pragma alloc_text(PAGE,RtlEqualComputerName)
+#pragma alloc_text(PAGE,RtlEqualUnicodeString)
+#pragma alloc_text(PAGE,RtlxUnicodeStringToOemSize)
+#pragma alloc_text(PAGE,RtlxAnsiStringToUnicodeSize)
+#pragma alloc_text(PAGE,RtlxUnicodeStringToAnsiSize)
+#pragma alloc_text(PAGE,RtlxOemStringToUnicodeSize)
+#pragma alloc_text(PAGE,RtlIsTextUnicode)
+#endif
+
+
+
+
+//
+// Global data used for translations.
+//
+
+extern PUSHORT NlsAnsiToUnicodeData; // Ansi CP to Unicode translation table
+extern PUSHORT NlsLeadByteInfo; // Lead byte info for ACP
+
+
+
+
+
+NTSTATUS
+RtlAnsiStringToUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PANSI_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified ansi source string into a
+ Unicode string. The translation is done with respect to the
+ current system locale information.
+
+Arguments:
+
+ DestinationString - Returns a unicode string that is equivalent to
+ the ansi source string. The maximum length field is only
+ set if AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the ansi source string that is to be
+ converted to unicode.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeUnicodeString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG UnicodeLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ UnicodeLength = RtlAnsiStringToUnicodeSize(SourceString);
+ if ( UnicodeLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(UnicodeLength - sizeof(UNICODE_NULL));
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)UnicodeLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(UnicodeLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length >= DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlMultiByteToUnicodeN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ DestinationString->Buffer[Index / sizeof(WCHAR)] = UNICODE_NULL;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+WCHAR
+RtlAnsiCharToUnicodeChar(
+ IN OUT PUCHAR *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;
+ ULONG cbCharSize;
+ NTSTATUS st;
+
+
+ RTL_PAGED_CODE();
+
+
+#if 0
+ UnicodeCharacter = NlsAnsiToUnicodeData[(UCHAR)(**SourceCharacter)];
+ (*SourceCharacter)++;
+ return UnicodeCharacter;
+#endif
+
+
+ //
+ // Translate the ansi character to unicode - this handles DBCS.
+ //
+ cbCharSize = NlsLeadByteInfo[ **SourceCharacter ] ? 2 : 1;
+ st = RtlMultiByteToUnicodeN ( &UnicodeCharacter,
+ sizeof ( WCHAR ),
+ NULL,
+ *SourceCharacter,
+ cbCharSize );
+
+ //
+ // Check for error - The only time this will happen is if there is
+ // a leadbyte without a trail byte.
+ //
+ if ( ! NT_SUCCESS( st ) )
+ {
+ // Use space as default.
+ UnicodeCharacter = 0x0020;
+ }
+
+ //
+ // Advance the source pointer and return the Unicode character.
+ //
+ (*SourceCharacter) += cbCharSize;
+ return UnicodeCharacter;
+}
+
+
+NTSTATUS
+RtlUnicodeStringToAnsiString(
+ OUT PANSI_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ ansi string. The translation is done with respect to the
+ current system locale information.
+
+Arguments:
+
+ DestinationString - Returns an ansi string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned. The maximum length field is only set if
+ AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to be
+ converted to ansi.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeAnsiString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG AnsiLength;
+ ULONG Index;
+ NTSTATUS st;
+ NTSTATUS ReturnStatus = STATUS_SUCCESS;
+
+ RTL_PAGED_CODE();
+
+ AnsiLength = RtlUnicodeStringToAnsiSize(SourceString);
+ if ( AnsiLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(AnsiLength - 1);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)AnsiLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(AnsiLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length >= DestinationString->MaximumLength ) {
+ /*
+ * Return STATUS_BUFFER_OVERFLOW, but translate as much as
+ * will fit into the buffer first. This is the expected
+ * behavior for routines such as GetProfileStringA.
+ * Set the length of the buffer to one less than the maximum
+ * (so that the trail byte of a double byte char is not
+ * overwritten by doing DestinationString->Buffer[Index] = '\0').
+ * RtlUnicodeToMultiByteN is careful not to truncate a
+ * multibyte character.
+ */
+ if (!DestinationString->MaximumLength) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ ReturnStatus = STATUS_BUFFER_OVERFLOW;
+ DestinationString->Length = DestinationString->MaximumLength - 1;
+ }
+ }
+
+ st = RtlUnicodeToMultiByteN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ DestinationString->Buffer[Index] = '\0';
+
+ return ReturnStatus;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeStringToAnsiString(
+ OUT PANSI_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions upper cases the specified unicode source string and then
+ converts it into an ansi string. The translation is done with respect
+ to the current system locale information.
+
+Arguments:
+
+ DestinationString - Returns an ansi string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned. The maximum length field is only set
+ if AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to be
+ converted to upper case ansi.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeAnsiString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG AnsiLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ AnsiLength = RtlUnicodeStringToAnsiSize(SourceString);
+ if ( AnsiLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(AnsiLength - 1);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)AnsiLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(AnsiLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length >= DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlUpcaseUnicodeToMultiByteN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ DestinationString->Buffer[Index] = '\0';
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlOemStringToUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN POEM_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified oem source string into a
+ Unicode string. The translation is done with respect to the
+ installed OEM code page (OCP).
+
+Arguments:
+
+ DestinationString - Returns a unicode string that is equivalent to
+ the oem source string. The maximum length field is only
+ set if AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the oem source string that is to be
+ converted to unicode.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeUnicodeString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG UnicodeLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ UnicodeLength = RtlOemStringToUnicodeSize(SourceString);
+ if ( UnicodeLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(UnicodeLength - sizeof(UNICODE_NULL));
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)UnicodeLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(UnicodeLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length >= DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlOemToUnicodeN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ DestinationString->Buffer[Index / sizeof(WCHAR)] = UNICODE_NULL;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlUnicodeStringToOemString(
+ OUT POEM_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ oem string. The translation is done with respect to the OEM code
+ page (OCP).
+
+Arguments:
+
+ DestinationString - Returns an oem string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned. The maximum length field is only set if
+ AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to be
+ converted to oem.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeAnsiString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG OemLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ OemLength = RtlUnicodeStringToOemSize(SourceString);
+ if ( OemLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(OemLength - 1);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)OemLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(OemLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length >= DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlUnicodeToOemN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ DestinationString->Buffer[Index] = '\0';
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeStringToOemString(
+ OUT POEM_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This function upper cases the specified unicode source string and then
+ converts it into an oem string. The translation is done with respect
+ to the OEM code page (OCP).
+
+Arguments:
+
+ DestinationString - Returns an oem string that is equivalent to the
+ unicode source string. The maximum length field is only set if
+ AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to be
+ converted to oem.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeAnsiString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG OemLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ OemLength = RtlUnicodeStringToOemSize(SourceString);
+ if ( OemLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(OemLength - 1);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)OemLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(OemLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length >= DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlUpcaseUnicodeToOemN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ DestinationString->Buffer[Index] = '\0';
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlOemStringToCountedUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN POEM_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified oem source string into a
+ Unicode string. The translation is done with respect to the
+ installed OEM code page (OCP).
+
+ The destination string is NOT unnaturally null terminated. It is a
+ counted string as counted strings are meant to be.
+
+Arguments:
+
+ DestinationString - Returns a unicode string that is equivalent to
+ the oem source string. The maximum length field is only
+ set if AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the oem source string that is to be
+ converted to unicode.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeUnicodeString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG UnicodeLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ UnicodeLength = RtlOemStringToCountedUnicodeSize(SourceString);
+
+ if ( UnicodeLength == 0 ) {
+
+ DestinationString->Length = 0;
+ DestinationString->MaximumLength = 0;
+ DestinationString->Buffer = NULL;
+
+ return STATUS_SUCCESS;
+ }
+
+ if ( UnicodeLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(UnicodeLength);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)UnicodeLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(UnicodeLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length > DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlOemToUnicodeN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlUnicodeStringToCountedOemString(
+ OUT POEM_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ oem string. The translation is done with respect to the OEM code
+ page (OCP).
+
+ The destination string is NOT unnaturally null terminated. It is a
+ counted string as counted strings are meant to be.
+
+Arguments:
+
+ DestinationString - Returns an oem string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned. The maximum length field is only set if
+ AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to be
+ converted to oem.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeAnsiString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG OemLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ OemLength = RtlUnicodeStringToCountedOemSize(SourceString);
+
+ if ( OemLength == 0 ) {
+
+ DestinationString->Length = 0;
+ DestinationString->MaximumLength = 0;
+ DestinationString->Buffer = NULL;
+
+ return STATUS_SUCCESS;
+ }
+
+ if ( OemLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(OemLength);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)OemLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(OemLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length > DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlUnicodeToOemN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ //
+ // Now do a check here to see if there was really a mapping for all
+ // characters converted.
+ //
+
+ if (NT_SUCCESS(st) &&
+ !RtlpDidUnicodeToOemWork( DestinationString, SourceString )) {
+
+ st = STATUS_UNMAPPABLE_CHARACTER;
+ }
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeStringToCountedOemString(
+ OUT POEM_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions upper cases the specified unicode source string and
+ then converts it into an oem string. The translation is done with
+ respect to the OEM code page (OCP).
+
+ The destination string is NOT unnaturally null terminated. It is a
+ counted string as counted strings are meant to be.
+
+Arguments:
+
+ DestinationString - Returns an oem string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned. The maximum length field is only set
+ if AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to be
+ converted to oem.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeAnsiString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG OemLength;
+ ULONG Index;
+ NTSTATUS st;
+
+ RTL_PAGED_CODE();
+
+ OemLength = RtlUnicodeStringToCountedOemSize(SourceString);
+
+ if ( OemLength == 0 ) {
+
+ DestinationString->Length = 0;
+ DestinationString->MaximumLength = 0;
+ DestinationString->Buffer = NULL;
+
+ return STATUS_SUCCESS;
+ }
+
+ if ( OemLength > MAXUSHORT ) {
+ return STATUS_INVALID_PARAMETER_2;
+ }
+
+ DestinationString->Length = (USHORT)(OemLength);
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = (USHORT)OemLength;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)(OemLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( DestinationString->Length > DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ st = RtlUpcaseUnicodeToOemN(
+ DestinationString->Buffer,
+ DestinationString->Length,
+ &Index,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+
+ //
+ // Now do a check here to see if there was really a mapping for all
+ // characters converted.
+ //
+
+ if (NT_SUCCESS(st) &&
+ !RtlpDidUnicodeToOemWork( DestinationString, SourceString )) {
+
+ st = STATUS_UNMAPPABLE_CHARACTER;
+ }
+
+ if (!NT_SUCCESS(st)) {
+ if ( AllocateDestinationString ) {
+ (RtlFreeStringRoutine)(DestinationString->Buffer);
+ }
+
+ return st;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ upcased unicode string. The translation is done with respect to the
+ current system locale information.
+
+Arguments:
+
+ DestinationString - Returns a unicode string that is the upcased equivalent
+ to the unicode source string. The maximum length field is only set if
+ AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to being
+ upcased.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeUnicodeString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG Index;
+ ULONG StopIndex;
+
+ RTL_PAGED_CODE();
+
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = SourceString->Length;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)((ULONG)DestinationString->MaximumLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( SourceString->Length > DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ StopIndex = ((ULONG)SourceString->Length) / sizeof( WCHAR );
+
+ for (Index = 0; Index < StopIndex; Index++) {
+ DestinationString->Buffer[Index] = (WCHAR)NLS_UPCASE(SourceString->Buffer[Index]);
+ }
+
+ DestinationString->Length = SourceString->Length;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlDowncaseUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into a
+ downcased unicode string. The translation is done with respect to the
+ current system locale information.
+
+Arguments:
+
+ DestinationString - Returns a unicode string that is the downcased
+ equivalent to the unicode source string. The maximum length field
+ is only set if AllocateDestinationString is TRUE.
+
+ SourceString - Supplies the unicode source string that is to being
+ downcased.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeUnicodeString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The operation failed. No storage was allocated and no
+ conversion was done. None.
+
+--*/
+
+{
+ ULONG Index;
+ ULONG StopIndex;
+
+ RTL_PAGED_CODE();
+
+ if ( AllocateDestinationString ) {
+ DestinationString->MaximumLength = SourceString->Length;
+ DestinationString->Buffer = (RtlAllocateStringRoutine)((ULONG)DestinationString->MaximumLength);
+ if ( !DestinationString->Buffer ) {
+ return STATUS_NO_MEMORY;
+ }
+ }
+ else {
+ if ( SourceString->Length > DestinationString->MaximumLength ) {
+ return STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ StopIndex = ((ULONG)SourceString->Length) / sizeof( WCHAR );
+
+ for (Index = 0; Index < StopIndex; Index++) {
+ DestinationString->Buffer[Index] = (WCHAR)NLS_DOWNCASE(SourceString->Buffer[Index]);
+ }
+
+ DestinationString->Length = SourceString->Length;
+
+ return STATUS_SUCCESS;
+}
+
+
+WCHAR
+RtlUpcaseUnicodeChar(
+ IN WCHAR SourceCharacter
+ )
+
+/*++
+
+Routine Description:
+
+ This function translates the specified unicode character to its
+ equivalent upcased unicode chararacter. The purpose for this routine
+ is to allow for character by character upcase translation. The
+ translation is done with respect to the current system locale
+ information.
+
+
+Arguments:
+
+ SourceCharacter - Supplies the unicode character to be upcased.
+
+Return Value:
+
+ Returns the upcased unicode equivalent of the specified input character.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Note that this needs to reference the translation table !
+ //
+
+ return (WCHAR)NLS_UPCASE(SourceCharacter);
+}
+
+
+VOID
+RtlFreeUnicodeString(
+ IN OUT PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This API is used to free storage allocated by
+ RtlAnsiStringToUnicodeString. Note that only UnicodeString->Buffer
+ is free'd by this routine.
+
+Arguments:
+
+ UnicodeString - Supplies the address of the unicode string whose
+ buffer was previously allocated by RtlAnsiStringToUnicodeString.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if (UnicodeString->Buffer) { (RtlFreeStringRoutine)(UnicodeString->Buffer); }
+}
+
+
+VOID
+RtlFreeAnsiString(
+ IN OUT PANSI_STRING AnsiString
+ )
+
+/*++
+
+Routine Description:
+
+ This API is used to free storage allocated by
+ RtlUnicodeStringToAnsiString. Note that only AnsiString->Buffer
+ is free'd by this routine.
+
+Arguments:
+
+ AnsiString - Supplies the address of the ansi string whose buffer
+ was previously allocated by RtlUnicodeStringToAnsiString.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if (AnsiString->Buffer) { (RtlFreeStringRoutine)(AnsiString->Buffer); }
+}
+
+
+VOID
+RtlFreeOemString(
+ IN OUT POEM_STRING OemString
+ )
+
+/*++
+
+Routine Description:
+
+ This API is used to free storage allocated by
+ RtlUnicodeStringToOemString. Note that only OemString->Buffer
+ is free'd by this routine.
+
+Arguments:
+
+ OemString - Supplies the address of the oem string whose buffer
+ was previously allocated by RtlUnicodeStringToOemString.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if (OemString->Buffer) {(RtlFreeStringRoutine)(OemString->Buffer);}
+}
+
+
+ULONG
+RtlxUnicodeStringToAnsiSize(
+ IN PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This function computes the number of bytes required to store
+ a NULL terminated ansi string that is equivalent to the specified
+ unicode string. If an ansi string can not be formed, the return value
+ is 0.
+
+Arguments:
+
+ UnicodeString - Supplies a unicode string whose equivalent size as
+ an ansi string is to be calculated.
+
+Return Value:
+
+ 0 - The operation failed, the unicode string can not be translated
+ into ansi using the current system locale therefore no storage
+ is needed for the ansi string.
+
+ !0 - The operation was successful. The return value specifies the
+ number of bytes required to hold an NULL terminated ansi string
+ equivalent to the specified unicode string.
+
+--*/
+
+{
+ ULONG cbMultiByteString;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Get the size of the string - this call handles DBCS.
+ //
+ RtlUnicodeToMultiByteSize( &cbMultiByteString,
+ UnicodeString->Buffer,
+ UnicodeString->Length );
+
+ //
+ // Return the size in bytes.
+ //
+ return (cbMultiByteString + 1);
+}
+
+
+ULONG
+RtlxUnicodeStringToOemSize(
+ IN PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This function computes the number of bytes required to store
+ a NULL terminated oem string that is equivalent to the specified
+ unicode string. If an oem string can not be formed, the return value
+ is 0.
+
+Arguments:
+
+ UnicodeString - Supplies a unicode string whose equivalent size as
+ an oem string is to be calculated.
+
+Return Value:
+
+ 0 - The operation failed, the unicode string can not be translated
+ into oem using the OEM code page therefore no storage is
+ needed for the oem string.
+
+ !0 - The operation was successful. The return value specifies the
+ number of bytes required to hold an NULL terminated oem string
+ equivalent to the specified unicode string.
+
+--*/
+
+{
+ ULONG cbMultiByteString;
+
+ RTL_PAGED_CODE();
+
+ //
+ // LATER: Define an RtlUnicodeToOemSize.
+ // In the Japanese version, it's safe to call
+ // RtlUnicodeToMultiByteSize because the Ansi code page
+ // and the OEM code page are the same.
+ //
+
+ //
+ // Get the size of the string - this call handles DBCS.
+ //
+ RtlUnicodeToMultiByteSize( &cbMultiByteString,
+ UnicodeString->Buffer,
+ UnicodeString->Length );
+
+ //
+ // Return the size in bytes.
+ //
+ return (cbMultiByteString + 1);
+}
+
+
+ULONG
+RtlxAnsiStringToUnicodeSize(
+ IN PANSI_STRING AnsiString
+ )
+
+/*++
+
+Routine Description:
+
+ This function computes the number of bytes required to store a NULL
+ terminated unicode string that is equivalent to the specified ansi
+ string.
+
+Arguments:
+
+ AnsiString - Supplies an ansi string whose equivalent size as a
+ unicode string is to be calculated. The ansi string is
+ interpreted relative to the current system locale.
+
+Return Value:
+
+ The return value specifies the number of bytes required to hold a
+ NULL terminated unicode string equivalent to the specified ansi
+ string.
+
+--*/
+
+{
+ ULONG cbConverted;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Get the size of the string - this call handles DBCS.
+ //
+ RtlMultiByteToUnicodeSize( &cbConverted ,
+ AnsiString->Buffer,
+ AnsiString->Length );
+
+ //
+ // Return the size in bytes.
+ //
+ return ( cbConverted + sizeof(UNICODE_NULL) );
+}
+
+
+ULONG
+RtlxOemStringToUnicodeSize(
+ IN POEM_STRING OemString
+ )
+
+/*++
+
+Routine Description:
+
+ This function computes the number of bytes required to store a NULL
+ terminated unicode string that is equivalent to the specified oem
+ string.
+
+Arguments:
+
+ OemString - Supplies an oem string whose equivalent size as a
+ unicode string is to be calculated. The oem string is
+ interpreted relative to the current oem code page (OCP).
+
+Return Value:
+
+ The return value specifies the number of bytes required to hold a
+ NULL terminated unicode string equivalent to the specified oem
+ string.
+
+--*/
+
+{
+ ULONG cbConverted;
+
+ RTL_PAGED_CODE();
+
+ //
+ // LATER: Define an RtlOemToUnicodeSize.
+ // In the Japanese version, it's safe to call
+ // RtlMultiByteToUnicodeSize because the Ansi code page
+ // and the OEM code page are the same.
+ //
+
+ //
+ // Get the size of the string - this call handles DBCS.
+ //
+ RtlMultiByteToUnicodeSize( &cbConverted,
+ OemString->Buffer,
+ OemString->Length );
+
+ //
+ // Return the size in bytes.
+ //
+ return ( cbConverted + sizeof(UNICODE_NULL) );
+}
+
+
+LONG
+RtlCompareUnicodeString(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2,
+ IN BOOLEAN CaseInSensitive
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlCompareUnicodeString function compares two counted strings. The
+ return value indicates if the strings are equal or String1 is less than
+ String2 or String1 is greater than String2.
+
+ The CaseInSensitive parameter specifies if case is to be ignored when
+ doing the comparison.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+ CaseInsensitive - TRUE if case should be ignored when doing the
+ comparison.
+
+Return Value:
+
+ Signed value that gives the results of the comparison:
+
+ Zero - String1 equals String2
+
+ < Zero - String1 less than String2
+
+ > Zero - String1 greater than String2
+
+
+--*/
+
+{
+
+ PWCHAR s1, s2, Limit;
+ LONG n1, n2;
+ WCHAR c1, c2;
+
+ s1 = String1->Buffer;
+ s2 = String2->Buffer;
+ n1 = String1->Length;
+ n2 = String2->Length;
+
+ ASSERT((n1 & 1) == 0);
+ ASSERT((n2 & 1) == 0);
+ ASSERT(!(((((ULONG)s1 & 1) != 0) || (((ULONG)s2 & 1) != 0)) && (n1 != 0) && (n2 != 0)));
+
+ Limit = (PWCHAR)((PCHAR)s1 + (n1 <= n2 ? n1 : n2));
+ if (CaseInSensitive) {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+
+ //
+ // Note that this needs to reference the translation table!
+ //
+
+ c1 = NLS_UPCASE(c1);
+ c2 = NLS_UPCASE(c2);
+ if (c1 != c2) {
+ return (LONG)(c1) - (LONG)(c2);
+ }
+ }
+ }
+
+ } else {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+ return (LONG)(c1) - (LONG)(c2);
+ }
+ }
+ }
+
+ return n1 - n2;
+}
+
+
+BOOLEAN
+RtlEqualUnicodeString(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2,
+ IN BOOLEAN CaseInSensitive
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlEqualUnicodeString function compares two counted unicode strings for
+ equality.
+
+ The CaseInSensitive parameter specifies if case is to be ignored when
+ doing the comparison.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+ CaseInsensitive - TRUE if case should be ignored when doing the
+ comparison.
+
+Return Value:
+
+ Boolean value that is TRUE if String1 equals String2 and FALSE otherwise.
+
+--*/
+
+{
+
+ PWCHAR s1, s2, Limit;
+ LONG n1, n2;
+ WCHAR c1, c2;
+
+ RTL_PAGED_CODE();
+
+ n1 = String1->Length;
+ n2 = String2->Length;
+
+ ASSERT((n1 & 1) == 0);
+ ASSERT((n2 & 1) == 0);
+
+ if (n1 == n2) {
+ s1 = String1->Buffer;
+ s2 = String2->Buffer;
+
+ ASSERT(!(((((ULONG)s1 & 1) != 0) || (((ULONG)s2 & 1) != 0)) && (n1 != 0) && (n2 != 0)));
+
+ Limit = (PWCHAR)((PCHAR)s1 + n1);
+ if (CaseInSensitive) {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if ((c1 != c2) && (NLS_UPCASE(c1) != NLS_UPCASE(c2))) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+
+ } else {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ } else {
+ return FALSE;
+ }
+}
+
+
+BOOLEAN
+RtlPrefixUnicodeString(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2,
+ IN BOOLEAN CaseInSensitive
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlPrefixUnicodeString function determines if the String1
+ counted string parameter is a prefix of the String2 counted string
+ parameter.
+
+ The CaseInSensitive parameter specifies if case is to be ignored when
+ doing the comparison.
+
+Arguments:
+
+ String1 - Pointer to the first unicode string.
+
+ String2 - Pointer to the second unicode string.
+
+ CaseInsensitive - TRUE if case should be ignored when doing the
+ comparison.
+
+Return Value:
+
+ Boolean value that is TRUE if String1 equals a prefix of String2 and
+ FALSE otherwise.
+
+--*/
+
+{
+ PWSTR s1, s2;
+ ULONG n;
+ WCHAR c1, c2;
+
+ s1 = String1->Buffer;
+ s2 = String2->Buffer;
+ n = String1->Length;
+ if (String2->Length < n) {
+ return( FALSE );
+ }
+
+ n = n / sizeof(c1);
+ if (CaseInSensitive) {
+ while (n) {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ if ((c1 != c2) && (NLS_UPCASE(c1) != NLS_UPCASE(c2))) {
+ return( FALSE );
+ }
+
+ n--;
+ }
+ }
+ else {
+ while (n) {
+ if (*s1++ != *s2++) {
+ return( FALSE );
+ }
+
+ n--;
+ }
+ }
+
+ return TRUE;
+}
+
+
+VOID
+RtlCopyUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlCopyString function copies the SourceString to the
+ DestinationString. If SourceString is not specified, then
+ the Length field of DestinationString is set to zero. The
+ MaximumLength and Buffer fields of DestinationString are not
+ modified by this function.
+
+ The number of bytes copied from the SourceString is either the
+ Length of SourceString or the MaximumLength of DestinationString,
+ whichever is smaller.
+
+Arguments:
+
+ DestinationString - Pointer to the destination string.
+
+ SourceString - Optional pointer to the source string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ UNALIGNED WCHAR *src, *dst;
+ ULONG n;
+
+ if (ARGUMENT_PRESENT(SourceString)) {
+ dst = DestinationString->Buffer;
+ src = SourceString->Buffer;
+ n = SourceString->Length;
+ if ((USHORT)n > DestinationString->MaximumLength) {
+ n = DestinationString->MaximumLength;
+ }
+
+ DestinationString->Length = (USHORT)n;
+ RtlCopyMemory(dst, src, n);
+ if (DestinationString->Length < DestinationString->MaximumLength) {
+ dst[n / sizeof(WCHAR)] = UNICODE_NULL;
+ }
+
+ } else {
+ DestinationString->Length = 0;
+ }
+
+ return;
+}
+
+
+NTSTATUS
+RtlAppendUnicodeToString (
+ IN PUNICODE_STRING Destination,
+ IN PWSTR Source OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine appends the supplied UNICODE string to an existing
+ PUNICODE_STRING.
+
+ It will copy bytes from the Source PSZ to the destination PSTRING up to
+ the destinations PUNICODE_STRING->MaximumLength field.
+
+Arguments:
+
+ IN PUNICODE_STRING Destination, - Supplies a pointer to the destination
+ string
+ IN PWSTR Source - Supplies the string to append to the destination
+
+Return Value:
+
+ STATUS_SUCCESS - The source string was successfully appended to the
+ destination counted string.
+
+ STATUS_BUFFER_TOO_SMALL - The destination string length was not big
+ enough to allow the source string to be appended. The Destination
+ string length is not updated.
+
+--*/
+
+{
+ USHORT n;
+ UNALIGNED WCHAR *dst;
+
+ if (ARGUMENT_PRESENT( Source )) {
+ UNICODE_STRING UniSource;
+
+ RtlInitUnicodeString(&UniSource, Source);
+
+ n = UniSource.Length;
+
+ if ((n + Destination->Length) > Destination->MaximumLength) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ dst = &Destination->Buffer[ (Destination->Length / sizeof( WCHAR )) ];
+ RtlMoveMemory( dst, Source, n );
+
+ Destination->Length += n;
+
+ if (Destination->Length < Destination->MaximumLength) {
+ dst[ n / sizeof( WCHAR ) ] = UNICODE_NULL;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlAppendUnicodeStringToString (
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING Source
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will concatinate two PSTRINGs together. It will copy
+ bytes from the source up to the MaximumLength of the destination.
+
+Arguments:
+
+ IN PSTRING Destination, - Supplies the destination string
+ IN PSTRING Source - Supplies the source for the string copy
+
+Return Value:
+
+ STATUS_SUCCESS - The source string was successfully appended to the
+ destination counted string.
+
+ STATUS_BUFFER_TOO_SMALL - The destination string length was not big
+ enough to allow the source string to be appended. The Destination
+ string length is not updated.
+
+--*/
+
+{
+ USHORT n = Source->Length;
+ UNALIGNED WCHAR *dst;
+
+ if (n) {
+ if ((n + Destination->Length) > Destination->MaximumLength) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ dst = &Destination->Buffer[ (Destination->Length / sizeof( WCHAR )) ];
+ RtlMoveMemory( dst, Source->Buffer, n );
+
+ Destination->Length += n;
+
+ if (Destination->Length < Destination->MaximumLength) {
+ dst[ n / sizeof( WCHAR ) ] = UNICODE_NULL;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+BOOLEAN
+RtlCreateUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PCWSTR SourceString
+ )
+{
+ ULONG cb;
+
+ RTL_PAGED_CODE();
+
+ cb = (wcslen( SourceString ) + 1) * sizeof( WCHAR );
+ DestinationString->Buffer = (RtlAllocateStringRoutine)( cb );
+ if (DestinationString->Buffer) {
+ RtlMoveMemory( DestinationString->Buffer, SourceString, cb );
+ DestinationString->MaximumLength = (USHORT)cb;
+ DestinationString->Length = (USHORT)(cb - sizeof( UNICODE_NULL ));
+ return( TRUE );
+ }
+ else {
+ return( FALSE );
+ }
+}
+
+
+BOOLEAN
+RtlEqualDomainName(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlEqualDomainName function compares two domain names for equality.
+
+ The comparison is a case insensitive comparison of the OEM equivalent
+ strings.
+
+ The domain name is not validated for length nor invalid characters.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+Return Value:
+
+ Boolean value that is TRUE if String1 equals String2 and FALSE otherwise.
+
+--*/
+
+{
+ NTSTATUS Status;
+ BOOLEAN ReturnValue = FALSE;
+ OEM_STRING OemString1;
+ OEM_STRING OemString2;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Upper case and convert the first string to OEM
+ //
+
+ Status = RtlUpcaseUnicodeStringToOemString( &OemString1,
+ String1,
+ TRUE ); // Allocate Dest
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Upper case and convert the second string to OEM
+ //
+
+ Status = RtlUpcaseUnicodeStringToOemString( &OemString2,
+ String2,
+ TRUE ); // Allocate Dest
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Do a case insensitive comparison.
+ //
+
+ ReturnValue = RtlEqualString( &OemString1,
+ &OemString2,
+ FALSE );
+
+ RtlFreeOemString( &OemString2 );
+ }
+
+ RtlFreeOemString( &OemString1 );
+ }
+
+ return ReturnValue;
+}
+
+
+
+BOOLEAN
+RtlEqualComputerName(
+ IN PUNICODE_STRING String1,
+ IN PUNICODE_STRING String2
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlEqualComputerName function compares two computer names for equality.
+
+ The comparison is a case insensitive comparison of the OEM equivalent
+ strings.
+
+ The domain name is not validated for length nor invalid characters.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+Return Value:
+
+ Boolean value that is TRUE if String1 equals String2 and FALSE otherwise.
+
+--*/
+
+{
+ return RtlEqualDomainName( String1, String2 );
+}
+
+/**
+
+
+**/
+
+#define UNICODE_FFFF 0xFFFF
+#define REVERSE_BYTE_ORDER_MARK 0xFFFE
+#define BYTE_ORDER_MARK 0xFEFF
+
+#define PARAGRAPH_SEPARATOR 0x2029
+#define LINE_SEPARATOR 0x2028
+
+#define UNICODE_TAB 0x0009
+#define UNICODE_LF 0x000A
+#define UNICODE_CR 0x000D
+#define UNICODE_SPACE 0x0020
+#define UNICODE_CJK_SPACE 0x3000
+
+#define UNICODE_R_TAB 0x0900
+#define UNICODE_R_LF 0x0A00
+#define UNICODE_R_CR 0x0D00
+#define UNICODE_R_SPACE 0x2000
+#define UNICODE_R_CJK_SPACE 0x0030 /* Ambiguous - same as ASCII '0' */
+
+#define ASCII_CRLF 0x0A0D
+
+#define __max(a,b) (((a) > (b)) ? (a) : (b))
+#define __min(a,b) (((a) < (b)) ? (a) : (b))
+
+
+BOOLEAN
+RtlIsTextUnicode(
+ IN PVOID Buffer,
+ IN ULONG Size,
+ IN OUT PULONG Result OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ IsTextUnicode performs a series of inexpensive heuristic checks
+ on a buffer in order to verify that it contains Unicode data.
+
+
+ [[ need to fix this section, see at the end ]]
+
+ Found Return Result
+
+ BOM TRUE BOM
+ RBOM FALSE RBOM
+ FFFF FALSE Binary
+ NULL FALSE Binary
+ null TRUE null bytes
+ ASCII_CRLF FALSE CRLF
+ UNICODE_TAB etc. TRUE Zero Ext Controls
+ UNICODE_TAB_R FALSE Reversed Controls
+ UNICODE_ZW etc. TRUE Unicode specials
+
+ 1/3 as little variation in hi-byte as in lo byte: TRUE Correl
+ 3/1 or worse " FALSE AntiCorrel
+
+Arguments:
+
+ Buffer - pointer to buffer containing text to examine.
+
+ Size - size of buffer in bytes. At most 256 characters in this will
+ be examined. If the size is less than the size of a unicode
+ character, then this function returns FALSE.
+
+ Result - optional pointer to a flag word that contains additional information
+ about the reason for the return value. If specified, this value on
+ input is a mask that is used to limit the factors this routine uses
+ to make it decision. On output, this flag word is set to contain
+ those flags that were used to make its decision.
+
+Return Value:
+
+ Boolean value that is TRUE if Buffer contains unicode characters.
+
+--*/
+{
+ UNALIGNED WCHAR *lpBuff = Buffer;
+ PCHAR lpb = Buffer;
+ ULONG iBOM = 0;
+ ULONG iCR = 0;
+ ULONG iLF = 0;
+ ULONG iTAB = 0;
+ ULONG iSPACE = 0;
+ ULONG iCJK_SPACE = 0;
+ ULONG iFFFF = 0;
+ ULONG iPS = 0;
+ ULONG iLS = 0;
+
+ ULONG iRBOM = 0;
+ ULONG iR_CR = 0;
+ ULONG iR_LF = 0;
+ ULONG iR_TAB = 0;
+ ULONG iR_SPACE = 0;
+
+ ULONG iNull = 0;
+ ULONG iUNULL = 0;
+ ULONG iCRLF = 0;
+ ULONG iTmp;
+ ULONG LastLo = 0;
+ ULONG LastHi = 0;
+ ULONG iHi, iLo;
+ ULONG HiDiff = 0;
+ ULONG LoDiff = 0;
+ ULONG cLeadByte = 0;
+ ULONG cWeird = 0;
+
+ ULONG iResult = 0;
+
+ ULONG iMaxTmp = __min(256, Size / sizeof(WCHAR));
+
+ if (Size < 2 ) {
+ if (ARGUMENT_PRESENT( Result )) {
+ *Result = IS_TEXT_UNICODE_ASCII16 | IS_TEXT_UNICODE_CONTROLS;
+ }
+
+ return FALSE;
+ }
+
+
+ // Check at most 256 wide character, collect various statistics
+ for (iTmp = 0; iTmp < iMaxTmp; iTmp++) {
+ switch (lpBuff[iTmp]) {
+ case BYTE_ORDER_MARK:
+ iBOM++;
+ break;
+ case PARAGRAPH_SEPARATOR:
+ iPS++;
+ break;
+ case LINE_SEPARATOR:
+ iLS++;
+ break;
+ case UNICODE_LF:
+ iLF++;
+ break;
+ case UNICODE_TAB:
+ iTAB++;
+ break;
+ case UNICODE_SPACE:
+ iSPACE++;
+ break;
+ case UNICODE_CJK_SPACE:
+ iCJK_SPACE++;
+ break;
+ case UNICODE_CR:
+ iCR++;
+ break;
+
+ // The following codes are expected to show up in
+ // byte reversed files
+ case REVERSE_BYTE_ORDER_MARK:
+ iRBOM++;
+ break;
+ case UNICODE_R_LF:
+ iR_LF++;
+ break;
+ case UNICODE_R_TAB:
+ iR_TAB++;
+ break;
+ case UNICODE_R_CR:
+ iR_CR++;
+ break;
+ case UNICODE_R_SPACE:
+ iR_SPACE++;
+ break;
+
+ // The following codes are illegal and should never occur
+ case UNICODE_FFFF:
+ iFFFF++;
+ break;
+ case UNICODE_NULL:
+ iUNULL++;
+ break;
+
+ // The following is not currently a Unicode character
+ // but is expected to show up accidentally when reading
+ // in ASCII files which use CRLF on a little endian machine
+ case ASCII_CRLF:
+ iCRLF++;
+ break; /* little endian */
+ }
+
+ // Collect statistics on the fluctuations of high bytes
+ // versus low bytes
+
+ iHi = HIBYTE (lpBuff[iTmp]);
+ iLo = LOBYTE (lpBuff[iTmp]);
+
+ // Count cr/lf and lf/cr that cross two words
+ if ((iLo == '\r' && LastHi == '\n') ||
+ (iLo == '\n' && LastHi == '\r')) {
+ cWeird++;
+ }
+
+ iNull += (iHi ? 0 : 1) + (iLo ? 0 : 1); /* count Null bytes */
+
+ HiDiff += __max( iHi, LastHi ) - __min( LastHi, iHi );
+ LoDiff += __max( iLo, LastLo ) - __min( LastLo, iLo );
+
+ LastLo = iLo;
+ LastHi = iHi;
+ }
+
+ // Count cr/lf and lf/cr that cross two words
+ if ((iLo == '\r' && LastHi == '\n') ||
+ (iLo == '\n' && LastHi == '\r')) {
+ cWeird++;
+ }
+
+ if (iHi == '\0') /* don't count the last null */
+ iNull--;
+ if (iHi == 26) /* count ^Z at end as weird */
+ cWeird++;
+
+ iMaxTmp = __min(256 * sizeof(WCHAR), Size);
+ if (NlsMbCodePageTag) {
+ for (iTmp = 0; iTmp < iMaxTmp; iTmp++) {
+ if (NlsLeadByteInfo[lpb[iTmp]]) {
+ cLeadByte++;
+ iTmp++; /* should check for trailing-byte range */
+ }
+ }
+ }
+
+ // sift the statistical evidence
+ if (LoDiff < 127 && HiDiff == 0) {
+ iResult |= IS_TEXT_UNICODE_ASCII16; /* likely 16-bit ASCII */
+ }
+
+ if (HiDiff && LoDiff == 0) {
+ iResult |= IS_TEXT_UNICODE_REVERSE_ASCII16; /* reverse 16-bit ASCII */
+ }
+
+ // Use leadbyte info to weight statistics.
+ if (!NlsMbCodePageTag || cLeadByte == 0 ||
+ !ARGUMENT_PRESENT(Result) || !(*Result & IS_TEXT_UNICODE_DBCS_LEADBYTE)) {
+ iHi = 3;
+ }
+ else {
+ // A ratio of cLeadByte:cb of 1:2 ==> dbcs
+ // Very crude - should have a nice eq.
+ iHi = __min(256, Size/sizeof(WCHAR)) / 2;
+ if (cLeadByte < (iHi-1) / 3) {
+ iHi = 3;
+ }
+ else if (cLeadByte < (2 * (iHi-1)) / 3) {
+ iHi = 2;
+ }
+ else {
+ iHi = 1;
+ }
+ iResult |= IS_TEXT_UNICODE_DBCS_LEADBYTE;
+ }
+
+ if (iHi * HiDiff < LoDiff) {
+ iResult |= IS_TEXT_UNICODE_STATISTICS;
+ }
+
+ if (iHi * LoDiff < HiDiff) {
+ iResult |= IS_TEXT_UNICODE_REVERSE_STATISTICS;
+ }
+
+ //
+ // Any control codes widened to 16 bits? Any Unicode character
+ // which contain one byte in the control code range?
+ //
+
+ if (iCR + iLF + iTAB + iSPACE + iCJK_SPACE /*+iPS+iLS*/) {
+ iResult |= IS_TEXT_UNICODE_CONTROLS;
+ }
+
+ if (iR_LF + iR_CR + iR_TAB + iR_SPACE) {
+ iResult |= IS_TEXT_UNICODE_REVERSE_CONTROLS;
+ }
+
+ //
+ // Any characters that are illegal for Unicode?
+ //
+
+ if ((iRBOM + iFFFF + iUNULL + iCRLF) != 0 ||
+ (cWeird != 0 && cWeird >= iMaxTmp/40 )) {
+ iResult |= IS_TEXT_UNICODE_ILLEGAL_CHARS;
+ }
+
+ //
+ // Odd buffer length cannot be Unicode
+ //
+
+ if (Size & 1) {
+ iResult |= IS_TEXT_UNICODE_ODD_LENGTH;
+ }
+
+ //
+ // Any NULL bytes? (Illegal in ANSI)
+ //
+ if (iNull) {
+ iResult |= IS_TEXT_UNICODE_NULL_BYTES;
+ }
+
+ //
+ // POSITIVE evidence, BOM or RBOM used as signature
+ //
+
+ if (*lpBuff == BYTE_ORDER_MARK) {
+ iResult |= IS_TEXT_UNICODE_SIGNATURE;
+ }
+ else if (*lpBuff == REVERSE_BYTE_ORDER_MARK) {
+ iResult |= IS_TEXT_UNICODE_REVERSE_SIGNATURE;
+ }
+
+ //
+ // limit to desired categories if requested.
+ //
+
+ if (ARGUMENT_PRESENT( Result )) {
+ iResult &= *Result;
+ *Result = iResult;
+ }
+
+ //
+ // There are four separate conclusions:
+ //
+ // 1: The file APPEARS to be Unicode AU
+ // 2: The file CANNOT be Unicode CU
+ // 3: The file CANNOT be ANSI CA
+ //
+ //
+ // This gives the following possible results
+ //
+ // CU
+ // + -
+ //
+ // AU AU
+ // + - + -
+ // -------- --------
+ // CA +| 0 0 2 3
+ // |
+ // -| 1 1 4 5
+ //
+ //
+ // Note that there are only 6 really different cases, not 8.
+ //
+ // 0 - This must be a binary file
+ // 1 - ANSI file
+ // 2 - Unicode file (High probability)
+ // 3 - Unicode file (more than 50% chance)
+ // 5 - No evidence for Unicode (ANSI is default)
+ //
+ // The whole thing is more complicated if we allow the assumption
+ // of reverse polarity input. At this point we have a simplistic
+ // model: some of the reverse Unicode evidence is very strong,
+ // we ignore most weak evidence except statistics. If this kind of
+ // strong evidence is found together with Unicode evidence, it means
+ // its likely NOT Text at all. Furthermore if a REVERSE_BYTE_ORDER_MARK
+ // is found, it precludes normal Unicode. If both byte order marks are
+ // found it's not Unicode.
+ //
+
+ //
+ // Unicode signature : uncontested signature outweighs reverse evidence
+ //
+
+ if ((iResult & IS_TEXT_UNICODE_SIGNATURE) &&
+ !(iResult & (IS_TEXT_UNICODE_NOT_UNICODE_MASK&(~IS_TEXT_UNICODE_DBCS_LEADBYTE)))
+ ) {
+ return TRUE;
+ }
+
+ //
+ // If we have conflicting evidence, it's not Unicode
+ //
+
+ if (iResult & IS_TEXT_UNICODE_REVERSE_MASK) {
+ return FALSE;
+ }
+
+ //
+ // Statistical and other results (cases 2 and 3)
+ //
+
+ if (!(iResult & IS_TEXT_UNICODE_NOT_UNICODE_MASK) &&
+ ((iResult & IS_TEXT_UNICODE_NOT_ASCII_MASK) ||
+ (iResult & IS_TEXT_UNICODE_UNICODE_MASK)
+ )
+ ) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/private/ntos/rtl/nlsxlat.c b/private/ntos/rtl/nlsxlat.c
new file mode 100644
index 000000000..dd6649ae6
--- /dev/null
+++ b/private/ntos/rtl/nlsxlat.c
@@ -0,0 +1,2511 @@
+/****************************** Module Header ******************************\
+* Module Name: nlsxlat.c
+*
+* Copyright (c) 1985-91, Microsoft Corporation
+*
+* This modules contains the private routines for character translation:
+* 8-bit <=> Unicode.
+*
+* History:
+* 03-Jan-1992 gregoryw
+* 16-Feb-1993 JulieB Added Upcase Routines & Macros.
+* 17-Feb-1993 JulieB Fixed Tables; Fixed DBCS Code.
+* 08-Mar-1993 JulieB Moved Upcase Macro to ntrtlp.h.
+\***************************************************************************/
+
+#include "ntrtlp.h"
+
+
+NTSTATUS
+RtlConsoleMultiByteToUnicodeN(
+ OUT PWCH UnicodeString,
+ IN ULONG MaxBytesInUnicodeString,
+ OUT PULONG BytesInUnicodeString OPTIONAL,
+ IN PCH MultiByteString,
+ IN ULONG BytesInMultiByteString,
+ OUT PULONG pdwSpecialChar );
+
+VOID
+RtlpInitUpcaseTable(
+ IN PUSHORT TableBase,
+ OUT PNLSTABLEINFO CodePageTable
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlConsoleMultiByteToUnicodeN)
+#pragma alloc_text(PAGE,RtlMultiByteToUnicodeN)
+#pragma alloc_text(PAGE,RtlOemToUnicodeN)
+#pragma alloc_text(PAGE,RtlUnicodeToMultiByteN)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeToMultiByteN)
+#pragma alloc_text(PAGE,RtlUnicodeToOemN)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeToOemN)
+#pragma alloc_text(PAGE,RtlpDidUnicodeToOemWork)
+#pragma alloc_text(PAGE,RtlCustomCPToUnicodeN)
+#pragma alloc_text(PAGE,RtlUnicodeToCustomCPN)
+#pragma alloc_text(PAGE,RtlUpcaseUnicodeToCustomCPN)
+#pragma alloc_text(PAGE,RtlInitCodePageTable)
+#pragma alloc_text(PAGE,RtlpInitUpcaseTable)
+#pragma alloc_text(PAGE,RtlInitNlsTables)
+#pragma alloc_text(PAGE,RtlResetRtlTranslations)
+#pragma alloc_text(PAGE,RtlMultiByteToUnicodeSize)
+#pragma alloc_text(PAGE,RtlUnicodeToMultiByteSize)
+#pragma alloc_text(PAGE,RtlGetDefaultCodePage)
+#endif
+
+
+
+//
+// Various defines and convenient macros for data access
+//
+
+#define DBCS_TABLE_SIZE 256
+
+
+/*
+ * Global data used by the translation routines.
+ *
+ */
+
+//
+// Upcase and Lowercase data
+//
+PUSHORT Nls844UnicodeUpcaseTable;
+PUSHORT Nls844UnicodeLowercaseTable;
+
+//
+// ACP related data
+//
+USHORT NlsLeadByteInfoTable[DBCS_TABLE_SIZE]; // Lead byte info. for ACP
+USHORT NlsAnsiCodePage; // Default ANSI code page
+USHORT NlsOemCodePage; // Default OEM code page
+PUSHORT NlsLeadByteInfo = NlsLeadByteInfoTable;
+PUSHORT NlsMbAnsiCodePageTables; // Multibyte to Unicode translation tables
+PUSHORT NlsAnsiToUnicodeData; // Ansi CP to Unicode translation table
+PCH NlsUnicodeToAnsiData; // Unicode to Ansi CP translation table
+PUSHORT NlsUnicodeToMbAnsiData; // Unicode to Multibyte Ansi CP translation table
+BOOLEAN NlsMbCodePageTag = FALSE; // TRUE -> Multibyte ACP, FALSE -> Singlebyte ACP
+
+//
+// OEM related data
+//
+USHORT NlsOemLeadByteInfoTable[DBCS_TABLE_SIZE]; // Lead byte info. for 0CP
+PUSHORT NlsOemLeadByteInfo = NlsOemLeadByteInfoTable;
+PUSHORT NlsMbOemCodePageTables; // OEM Multibyte to Unicode translation tables
+PUSHORT NlsOemToUnicodeData; // Oem CP to Unicode translation table
+PCH NlsUnicodeToOemData; // Unicode to Oem CP translation table
+PUSHORT NlsUnicodeToMbOemData; // Unicode to Multibyte Oem CP translation table
+BOOLEAN NlsMbOemCodePageTag = FALSE; // TRUE -> Multibyte OCP, FALSE -> Singlebyte OCP
+
+//
+// Default info taken from data files
+//
+USHORT UnicodeDefaultChar;
+
+USHORT OemDefaultChar;
+USHORT OemTransUniDefaultChar;
+
+//
+// Default info NOT taken from data files
+//
+USHORT UnicodeNull = 0x0000;
+
+
+
+NTSTATUS
+RtlConsoleMultiByteToUnicodeN(
+ OUT PWCH UnicodeString,
+ IN ULONG MaxBytesInUnicodeString,
+ OUT PULONG BytesInUnicodeString OPTIONAL,
+ IN PCH MultiByteString,
+ IN ULONG BytesInMultiByteString,
+ OUT PULONG pdwSpecialChar )
+
+/*++
+
+Routine Description:
+
+ This function is a superset of MultiByteToUnicode for the
+ console. It works just like the other, except it will detect
+ if any characters were under 0x20.
+
+ This functions converts the specified ansi source string into a
+ Unicode string. The translation is done with respect to the
+ ANSI Code Page (ACP) installed at boot time. Single byte characters
+ in the range 0x00 - 0x7f are simply zero extended as a performance
+ enhancement. In some far eastern code pages 0x5c is defined as the
+ Yen sign. For system translation we always want to consider 0x5c
+ to be the backslash character. We get this for free by zero extending.
+
+ NOTE: This routine only supports precomposed Unicode characters.
+
+Arguments:
+
+ UnicodeString - Returns a unicode string that is equivalent to
+ the ansi source string.
+
+ MaxBytesInUnicodeString - Supplies the maximum number of bytes to be
+ written to UnicodeString. If this causes UnicodeString to be a
+ truncated equivalent of MultiByteString, no error condition results.
+
+ BytesInUnicodeString - Returns the number of bytes in the returned
+ unicode string pointed to by UnicodeString.
+
+ MultiByteString - Supplies the ansi source string that is to be
+ converted to unicode.
+
+ BytesInMultiByteString - The number of bytes in the string pointed to
+ by MultiByteString.
+
+ pdwSpecialChar - will be zero if non detected, else it will contain the
+ approximate index (can be off by 32).
+
+Return Value:
+
+ SUCCESS - The conversion was successful.
+
+
+--*/
+
+{
+ ULONG LoopCount;
+ PUSHORT TranslateTable;
+ ULONG MaxCharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ *pdwSpecialChar = 0;
+
+ if (!NlsMbCodePageTag) {
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (MaxCharsInUnicodeString < BytesInMultiByteString) ?
+ MaxCharsInUnicodeString : BytesInMultiByteString;
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = LoopCount * sizeof(WCHAR);
+
+ TranslateTable = NlsAnsiToUnicodeData; // used to help the mips compiler
+
+ quick_copy:
+ switch( LoopCount ) {
+ default:
+ if ((UCHAR)MultiByteString[0x1F] < 0x20) goto bad_case;
+ UnicodeString[0x1F] = TranslateTable[(UCHAR)MultiByteString[0x1F]];
+ case 0x1F:
+ if ((UCHAR)MultiByteString[0x1E] < 0x20) goto bad_case;
+ UnicodeString[0x1E] = TranslateTable[(UCHAR)MultiByteString[0x1E]];
+ case 0x1E:
+ if ((UCHAR)MultiByteString[0x1D] < 0x20) goto bad_case;
+ UnicodeString[0x1D] = TranslateTable[(UCHAR)MultiByteString[0x1D]];
+ case 0x1D:
+ if ((UCHAR)MultiByteString[0x1C] < 0x20) goto bad_case;
+ UnicodeString[0x1C] = TranslateTable[(UCHAR)MultiByteString[0x1C]];
+ case 0x1C:
+ if ((UCHAR)MultiByteString[0x1B] < 0x20) goto bad_case;
+ UnicodeString[0x1B] = TranslateTable[(UCHAR)MultiByteString[0x1B]];
+ case 0x1B:
+ if ((UCHAR)MultiByteString[0x1A] < 0x20) goto bad_case;
+ UnicodeString[0x1A] = TranslateTable[(UCHAR)MultiByteString[0x1A]];
+ case 0x1A:
+ if ((UCHAR)MultiByteString[0x19] < 0x20) goto bad_case;
+ UnicodeString[0x19] = TranslateTable[(UCHAR)MultiByteString[0x19]];
+ case 0x19:
+ if ((UCHAR)MultiByteString[0x18] < 0x20) goto bad_case;
+ UnicodeString[0x18] = TranslateTable[(UCHAR)MultiByteString[0x18]];
+ case 0x18:
+ if ((UCHAR)MultiByteString[0x17] < 0x20) goto bad_case;
+ UnicodeString[0x17] = TranslateTable[(UCHAR)MultiByteString[0x17]];
+ case 0x17:
+ if ((UCHAR)MultiByteString[0x16] < 0x20) goto bad_case;
+ UnicodeString[0x16] = TranslateTable[(UCHAR)MultiByteString[0x16]];
+ case 0x16:
+ if ((UCHAR)MultiByteString[0x15] < 0x20) goto bad_case;
+ UnicodeString[0x15] = TranslateTable[(UCHAR)MultiByteString[0x15]];
+ case 0x15:
+ if ((UCHAR)MultiByteString[0x14] < 0x20) goto bad_case;
+ UnicodeString[0x14] = TranslateTable[(UCHAR)MultiByteString[0x14]];
+ case 0x14:
+ if ((UCHAR)MultiByteString[0x13] < 0x20) goto bad_case;
+ UnicodeString[0x13] = TranslateTable[(UCHAR)MultiByteString[0x13]];
+ case 0x13:
+ if ((UCHAR)MultiByteString[0x12] < 0x20) goto bad_case;
+ UnicodeString[0x12] = TranslateTable[(UCHAR)MultiByteString[0x12]];
+ case 0x12:
+ if ((UCHAR)MultiByteString[0x11] < 0x20) goto bad_case;
+ UnicodeString[0x11] = TranslateTable[(UCHAR)MultiByteString[0x11]];
+ case 0x11:
+ if ((UCHAR)MultiByteString[0x10] < 0x20) goto bad_case;
+ UnicodeString[0x10] = TranslateTable[(UCHAR)MultiByteString[0x10]];
+ case 0x10:
+ if ((UCHAR)MultiByteString[0x0F] < 0x20) goto bad_case;
+ UnicodeString[0x0F] = TranslateTable[(UCHAR)MultiByteString[0x0F]];
+ case 0x0F:
+ if ((UCHAR)MultiByteString[0x0E] < 0x20) goto bad_case;
+ UnicodeString[0x0E] = TranslateTable[(UCHAR)MultiByteString[0x0E]];
+ case 0x0E:
+ if ((UCHAR)MultiByteString[0x0D] < 0x20) goto bad_case;
+ UnicodeString[0x0D] = TranslateTable[(UCHAR)MultiByteString[0x0D]];
+ case 0x0D:
+ if ((UCHAR)MultiByteString[0x0C] < 0x20) goto bad_case;
+ UnicodeString[0x0C] = TranslateTable[(UCHAR)MultiByteString[0x0C]];
+ case 0x0C:
+ if ((UCHAR)MultiByteString[0x0B] < 0x20) goto bad_case;
+ UnicodeString[0x0B] = TranslateTable[(UCHAR)MultiByteString[0x0B]];
+ case 0x0B:
+ if ((UCHAR)MultiByteString[0x0A] < 0x20) goto bad_case;
+ UnicodeString[0x0A] = TranslateTable[(UCHAR)MultiByteString[0x0A]];
+ case 0x0A:
+ if ((UCHAR)MultiByteString[0x09] < 0x20) goto bad_case;
+ UnicodeString[0x09] = TranslateTable[(UCHAR)MultiByteString[0x09]];
+ case 0x09:
+ if ((UCHAR)MultiByteString[0x08] < 0x20) goto bad_case;
+ UnicodeString[0x08] = TranslateTable[(UCHAR)MultiByteString[0x08]];
+ case 0x08:
+ if ((UCHAR)MultiByteString[0x07] < 0x20) goto bad_case;
+ UnicodeString[0x07] = TranslateTable[(UCHAR)MultiByteString[0x07]];
+ case 0x07:
+ if ((UCHAR)MultiByteString[0x06] < 0x20) goto bad_case;
+ UnicodeString[0x06] = TranslateTable[(UCHAR)MultiByteString[0x06]];
+ case 0x06:
+ if ((UCHAR)MultiByteString[0x05] < 0x20) goto bad_case;
+ UnicodeString[0x05] = TranslateTable[(UCHAR)MultiByteString[0x05]];
+ case 0x05:
+ if ((UCHAR)MultiByteString[0x04] < 0x20) goto bad_case;
+ UnicodeString[0x04] = TranslateTable[(UCHAR)MultiByteString[0x04]];
+ case 0x04:
+ if ((UCHAR)MultiByteString[0x03] < 0x20) goto bad_case;
+ UnicodeString[0x03] = TranslateTable[(UCHAR)MultiByteString[0x03]];
+ case 0x03:
+ if ((UCHAR)MultiByteString[0x02] < 0x20) goto bad_case;
+ UnicodeString[0x02] = TranslateTable[(UCHAR)MultiByteString[0x02]];
+ case 0x02:
+ if ((UCHAR)MultiByteString[0x01] < 0x20) goto bad_case;
+ UnicodeString[0x01] = TranslateTable[(UCHAR)MultiByteString[0x01]];
+ case 0x01:
+ if ((UCHAR)MultiByteString[0x00] < 0x20) goto bad_case;
+ UnicodeString[0x00] = TranslateTable[(UCHAR)MultiByteString[0x00]];
+ case 0x00:
+ ;
+ }
+
+ if ( LoopCount > 0x20 ) {
+ LoopCount -= 0x20;
+ UnicodeString += 0x20;
+ MultiByteString += 0x20;
+
+ goto quick_copy;
+ }
+ /* end of copy... */
+ } else {
+ register USHORT Entry;
+
+ PWCH UnicodeStringAnchor = UnicodeString;
+ TranslateTable = (PUSHORT)NlsMbAnsiCodePageTables;
+
+ // yea, this line is duplicated, but the compiler can handle opt
+ // better by moving it around
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+
+ //
+ // The ACP is a multibyte code page. Check each character
+ // to see if it is a lead byte before doing the translation.
+ //
+ while (MaxCharsInUnicodeString && BytesInMultiByteString) {
+ MaxCharsInUnicodeString--;
+ BytesInMultiByteString--;
+ if (NlsLeadByteInfo[*(PUCHAR)MultiByteString]) {
+ //
+ // Lead byte - Make sure there is a trail byte. If not,
+ // pass back a space rather than an error. Some 3.x
+ // applications pass incorrect strings and don't expect
+ // to get an error.
+ //
+ if (BytesInMultiByteString == 0)
+ {
+ *UnicodeString++ = UnicodeNull;
+ break;
+ }
+
+ //
+ // Get the unicode character.
+ //
+ Entry = NlsLeadByteInfo[*(PUCHAR)MultiByteString++];
+ *UnicodeString = (WCHAR)TranslateTable[ Entry + *(PUCHAR)MultiByteString++ ];
+ UnicodeString++;
+
+ //
+ // Decrement count of bytes in multibyte string to account
+ // for the double byte character.
+ //
+ BytesInMultiByteString--;
+ } else {
+ //
+ // Single byte character.
+ //
+ if ((UCHAR)MultiByteString[0x00] < 0x20)
+ *pdwSpecialChar = 1;
+ *UnicodeString++ = NlsAnsiToUnicodeData[*(PUCHAR)MultiByteString++];
+ }
+ }
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = (ULONG)((PCH)UnicodeString - (PCH)UnicodeStringAnchor);
+ }
+
+ return STATUS_SUCCESS;
+
+ bad_case:
+ //
+ // this is a low probability case, so we optimized the loop. If have a
+ // special char, finish trans and notify caller.
+ //
+ *pdwSpecialChar = 1;
+ return RtlMultiByteToUnicodeN(UnicodeString, MaxBytesInUnicodeString,
+ NULL, MultiByteString, LoopCount);
+}
+
+
+NTSTATUS
+RtlMultiByteToUnicodeN(
+ OUT PWCH UnicodeString,
+ IN ULONG MaxBytesInUnicodeString,
+ OUT PULONG BytesInUnicodeString OPTIONAL,
+ IN PCH MultiByteString,
+ IN ULONG BytesInMultiByteString)
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified ansi source string into a
+ Unicode string. The translation is done with respect to the
+ ANSI Code Page (ACP) installed at boot time. Single byte characters
+ in the range 0x00 - 0x7f are simply zero extended as a performance
+ enhancement. In some far eastern code pages 0x5c is defined as the
+ Yen sign. For system translation we always want to consider 0x5c
+ to be the backslash character. We get this for free by zero extending.
+
+ NOTE: This routine only supports precomposed Unicode characters.
+
+Arguments:
+
+ UnicodeString - Returns a unicode string that is equivalent to
+ the ansi source string.
+
+ MaxBytesInUnicodeString - Supplies the maximum number of bytes to be
+ written to UnicodeString. If this causes UnicodeString to be a
+ truncated equivalent of MultiByteString, no error condition results.
+
+ BytesInUnicodeString - Returns the number of bytes in the returned
+ unicode string pointed to by UnicodeString.
+
+ MultiByteString - Supplies the ansi source string that is to be
+ converted to unicode. For single-byte character sets, this address
+ CAN be the same as UnicodeString.
+
+ BytesInMultiByteString - The number of bytes in the string pointed to
+ by MultiByteString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful.
+
+
+--*/
+
+{
+ ULONG LoopCount;
+ ULONG TmpCount;
+ PUSHORT TranslateTable;
+ ULONG MaxCharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ if (!NlsMbCodePageTag) {
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (MaxCharsInUnicodeString < BytesInMultiByteString) ?
+ MaxCharsInUnicodeString : BytesInMultiByteString;
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = LoopCount * sizeof(WCHAR);
+
+ TranslateTable = NlsAnsiToUnicodeData; // used to help the mips compiler
+
+ TmpCount = LoopCount & 0x1F;
+ UnicodeString += (LoopCount - TmpCount);
+ MultiByteString += (LoopCount - TmpCount);
+ quick_copy:
+ switch( TmpCount ) {
+ default:
+ UnicodeString[0x1F] = TranslateTable[(UCHAR)MultiByteString[0x1F]];
+ case 0x1F:
+ UnicodeString[0x1E] = TranslateTable[(UCHAR)MultiByteString[0x1E]];
+ case 0x1E:
+ UnicodeString[0x1D] = TranslateTable[(UCHAR)MultiByteString[0x1D]];
+ case 0x1D:
+ UnicodeString[0x1C] = TranslateTable[(UCHAR)MultiByteString[0x1C]];
+ case 0x1C:
+ UnicodeString[0x1B] = TranslateTable[(UCHAR)MultiByteString[0x1B]];
+ case 0x1B:
+ UnicodeString[0x1A] = TranslateTable[(UCHAR)MultiByteString[0x1A]];
+ case 0x1A:
+ UnicodeString[0x19] = TranslateTable[(UCHAR)MultiByteString[0x19]];
+ case 0x19:
+ UnicodeString[0x18] = TranslateTable[(UCHAR)MultiByteString[0x18]];
+ case 0x18:
+ UnicodeString[0x17] = TranslateTable[(UCHAR)MultiByteString[0x17]];
+ case 0x17:
+ UnicodeString[0x16] = TranslateTable[(UCHAR)MultiByteString[0x16]];
+ case 0x16:
+ UnicodeString[0x15] = TranslateTable[(UCHAR)MultiByteString[0x15]];
+ case 0x15:
+ UnicodeString[0x14] = TranslateTable[(UCHAR)MultiByteString[0x14]];
+ case 0x14:
+ UnicodeString[0x13] = TranslateTable[(UCHAR)MultiByteString[0x13]];
+ case 0x13:
+ UnicodeString[0x12] = TranslateTable[(UCHAR)MultiByteString[0x12]];
+ case 0x12:
+ UnicodeString[0x11] = TranslateTable[(UCHAR)MultiByteString[0x11]];
+ case 0x11:
+ UnicodeString[0x10] = TranslateTable[(UCHAR)MultiByteString[0x10]];
+ case 0x10:
+ UnicodeString[0x0F] = TranslateTable[(UCHAR)MultiByteString[0x0F]];
+ case 0x0F:
+ UnicodeString[0x0E] = TranslateTable[(UCHAR)MultiByteString[0x0E]];
+ case 0x0E:
+ UnicodeString[0x0D] = TranslateTable[(UCHAR)MultiByteString[0x0D]];
+ case 0x0D:
+ UnicodeString[0x0C] = TranslateTable[(UCHAR)MultiByteString[0x0C]];
+ case 0x0C:
+ UnicodeString[0x0B] = TranslateTable[(UCHAR)MultiByteString[0x0B]];
+ case 0x0B:
+ UnicodeString[0x0A] = TranslateTable[(UCHAR)MultiByteString[0x0A]];
+ case 0x0A:
+ UnicodeString[0x09] = TranslateTable[(UCHAR)MultiByteString[0x09]];
+ case 0x09:
+ UnicodeString[0x08] = TranslateTable[(UCHAR)MultiByteString[0x08]];
+ case 0x08:
+ UnicodeString[0x07] = TranslateTable[(UCHAR)MultiByteString[0x07]];
+ case 0x07:
+ UnicodeString[0x06] = TranslateTable[(UCHAR)MultiByteString[0x06]];
+ case 0x06:
+ UnicodeString[0x05] = TranslateTable[(UCHAR)MultiByteString[0x05]];
+ case 0x05:
+ UnicodeString[0x04] = TranslateTable[(UCHAR)MultiByteString[0x04]];
+ case 0x04:
+ UnicodeString[0x03] = TranslateTable[(UCHAR)MultiByteString[0x03]];
+ case 0x03:
+ UnicodeString[0x02] = TranslateTable[(UCHAR)MultiByteString[0x02]];
+ case 0x02:
+ UnicodeString[0x01] = TranslateTable[(UCHAR)MultiByteString[0x01]];
+ case 0x01:
+ UnicodeString[0x00] = TranslateTable[(UCHAR)MultiByteString[0x00]];
+ case 0x00:
+ ;
+ }
+
+ if ( LoopCount >= 0x20 ) {
+ TmpCount = 0x20;
+ LoopCount -= 0x20;
+ UnicodeString -= 0x20;
+ MultiByteString -= 0x20;
+
+ goto quick_copy;
+ }
+ /* end of copy... */
+ } else {
+ register USHORT Entry;
+ PWCH UnicodeStringAnchor = UnicodeString;
+ TranslateTable = (PUSHORT)NlsMbAnsiCodePageTables;
+
+ // yea, this line is duplicated, but the compiler can handle opt
+ // better buy moving it around
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+
+ //
+ // The ACP is a multibyte code page. Check each character
+ // to see if it is a lead byte before doing the translation.
+ //
+ while (MaxCharsInUnicodeString && BytesInMultiByteString) {
+ MaxCharsInUnicodeString--;
+ BytesInMultiByteString--;
+ if (NlsLeadByteInfo[*(PUCHAR)MultiByteString]) {
+ //
+ // Lead byte - Make sure there is a trail byte. If not,
+ // pass back a space rather than an error. Some 3.x
+ // applications pass incorrect strings and don't expect
+ // to get an error.
+ //
+ if (BytesInMultiByteString == 0)
+ {
+ *UnicodeString++ = UnicodeNull;
+ break;
+ }
+
+ //
+ // Get the unicode character.
+ //
+ Entry = NlsLeadByteInfo[*(PUCHAR)MultiByteString++];
+ *UnicodeString = (WCHAR)TranslateTable[ Entry + *(PUCHAR)MultiByteString++ ];
+ UnicodeString++;
+
+ //
+ // Decrement count of bytes in multibyte string to account
+ // for the double byte character.
+ //
+ BytesInMultiByteString--;
+ } else {
+ //
+ // Single byte character.
+ //
+ *UnicodeString++ = NlsAnsiToUnicodeData[*(PUCHAR)MultiByteString++];
+ }
+ }
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = (ULONG)((PCH)UnicodeString - (PCH)UnicodeStringAnchor);
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlOemToUnicodeN(
+ OUT PWCH UnicodeString,
+ IN ULONG MaxBytesInUnicodeString,
+ OUT PULONG BytesInUnicodeString OPTIONAL,
+ IN PCH OemString,
+ IN ULONG BytesInOemString)
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified oem source string into a
+ Unicode string. The translation is done with respect to the
+ OEM Code Page (OCP) installed at boot time. Single byte characters
+ in the range 0x00 - 0x7f are simply zero extended as a performance
+ enhancement. In some far eastern code pages 0x5c is defined as the
+ Yen sign. For system translation we always want to consider 0x5c
+ to be the backslash character. We get this for free by zero extending.
+
+ NOTE: This routine only supports precomposed Unicode characters.
+
+Arguments:
+
+ UnicodeString - Returns a unicode string that is equivalent to
+ the oem source string.
+
+ MaxBytesInUnicodeString - Supplies the maximum number of bytes to be
+ written to UnicodeString. If this causes UnicodeString to be a
+ truncated equivalent of OemString, no error condition results.
+
+ BytesInUnicodeString - Returns the number of bytes in the returned
+ unicode string pointed to by UnicodeString.
+
+ OemString - Supplies the oem source string that is to be
+ converted to unicode.
+
+ BytesInOemString - The number of bytes in the string pointed to
+ by OemString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_ILLEGAL_CHARACTER - The final Oem character was illegal
+
+ STATUS_BUFFER_OVERFLOW - MaxBytesInUnicodeString was not enough to hold
+ the whole Oem string. It was converted correct to the point though.
+
+--*/
+
+{
+ ULONG LoopCount;
+ PUSHORT TranslateTable;
+ ULONG MaxCharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ if (!NlsMbOemCodePageTag) {
+ //
+ // The OCP is a single byte code page.
+ //
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (MaxCharsInUnicodeString < BytesInOemString) ?
+ MaxCharsInUnicodeString : BytesInOemString;
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = LoopCount * sizeof(WCHAR);
+
+
+ TranslateTable = NlsOemToUnicodeData; // used to help the mips compiler
+
+ quick_copy:
+ switch( LoopCount ) {
+ default:
+ UnicodeString[0x0F] = TranslateTable[(UCHAR)OemString[0x0F]];
+ case 0x0F:
+ UnicodeString[0x0E] = TranslateTable[(UCHAR)OemString[0x0E]];
+ case 0x0E:
+ UnicodeString[0x0D] = TranslateTable[(UCHAR)OemString[0x0D]];
+ case 0x0D:
+ UnicodeString[0x0C] = TranslateTable[(UCHAR)OemString[0x0C]];
+ case 0x0C:
+ UnicodeString[0x0B] = TranslateTable[(UCHAR)OemString[0x0B]];
+ case 0x0B:
+ UnicodeString[0x0A] = TranslateTable[(UCHAR)OemString[0x0A]];
+ case 0x0A:
+ UnicodeString[0x09] = TranslateTable[(UCHAR)OemString[0x09]];
+ case 0x09:
+ UnicodeString[0x08] = TranslateTable[(UCHAR)OemString[0x08]];
+ case 0x08:
+ UnicodeString[0x07] = TranslateTable[(UCHAR)OemString[0x07]];
+ case 0x07:
+ UnicodeString[0x06] = TranslateTable[(UCHAR)OemString[0x06]];
+ case 0x06:
+ UnicodeString[0x05] = TranslateTable[(UCHAR)OemString[0x05]];
+ case 0x05:
+ UnicodeString[0x04] = TranslateTable[(UCHAR)OemString[0x04]];
+ case 0x04:
+ UnicodeString[0x03] = TranslateTable[(UCHAR)OemString[0x03]];
+ case 0x03:
+ UnicodeString[0x02] = TranslateTable[(UCHAR)OemString[0x02]];
+ case 0x02:
+ UnicodeString[0x01] = TranslateTable[(UCHAR)OemString[0x01]];
+ case 0x01:
+ UnicodeString[0x00] = TranslateTable[(UCHAR)OemString[0x00]];
+ case 0x00:
+ ;
+ }
+
+ if ( LoopCount > 0x10 ) {
+ LoopCount -= 0x10;
+ OemString += 0x10;
+ UnicodeString += 0x10;
+
+ goto quick_copy;
+ }
+ /* end of copy... */
+ } else {
+ register USHORT Entry;
+ PWCH UnicodeStringAnchor = UnicodeString;
+
+ //
+ // The OCP is a multibyte code page. Check each character
+ // to see if it is a lead byte before doing the translation.
+ //
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+ TranslateTable = (PUSHORT)NlsMbOemCodePageTables;
+
+ while (MaxCharsInUnicodeString && BytesInOemString) {
+ MaxCharsInUnicodeString--;
+ BytesInOemString--;
+ if (NlsOemLeadByteInfo[*(PUCHAR)OemString]) {
+ //
+ // Lead byte - Make sure there is a trail byte. If not,
+ // pass back a space rather than an error. Some 3.x
+ // applications pass incorrect strings and don't expect
+ // to get an error.
+ //
+ if (BytesInOemString == 0)
+ {
+ *UnicodeString++ = UnicodeNull;
+ break;
+ }
+
+ //
+ // Get the unicode character.
+ //
+ Entry = NlsOemLeadByteInfo[*(PUCHAR)OemString++];
+ *UnicodeString = TranslateTable[ Entry + *(PUCHAR)OemString++ ];
+ UnicodeString++;
+
+ //
+ // Decrement count of bytes in oem string to account
+ // for the double byte character.
+ //
+ BytesInOemString--;
+ } else {
+ //
+ // Single byte character.
+ //
+ *UnicodeString++ = NlsOemToUnicodeData[*(PUCHAR)OemString++];
+ }
+ }
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = (ULONG)((PCH)UnicodeString - (PCH)UnicodeStringAnchor);
+ }
+
+ //
+ // Check if we were able to use all of the source Oem String
+ //
+ return (BytesInOemString <= MaxCharsInUnicodeString) ?
+ STATUS_SUCCESS :
+ STATUS_BUFFER_OVERFLOW;
+}
+
+
+NTSTATUS
+RtlMultiByteToUnicodeSize(
+ OUT PULONG BytesInUnicodeString,
+ IN PCH MultiByteString,
+ IN ULONG BytesInMultiByteString)
+
+/*++
+
+Routine Description:
+
+ This functions determines how many bytes would be needed to represent
+ the specified ANSI source string in Unicode string (not counting the
+ null terminator)
+ The translation is done with respect to the ANSI Code Page (ACP) installed
+ at boot time. Single byte characters in the range 0x00 - 0x7f are simply
+ zero extended as a performance enhancement. In some far eastern code pages
+ 0x5c is defined as the Yen sign. For system translation we always want to
+ consider 0x5c to be the backslash character. We get this for free by zero
+ extending.
+
+ NOTE: This routine only supports precomposed Unicode characters.
+
+Arguments:
+
+ BytesInUnicodeString - Returns the number of bytes a Unicode translation
+ of the ANSI string pointed to by MultiByteString would contain.
+
+ MultiByteString - Supplies the ansi source string whose Unicode length
+ is to be calculated.
+
+ BytesInMultiByteString - The number of bytes in the string pointed to
+ by MultiByteString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+
+--*/
+
+{
+ ULONG cbUnicode = 0;
+
+ RTL_PAGED_CODE();
+
+ if (NlsMbCodePageTag) {
+ //
+ // The ACP is a multibyte code page. Check each character
+ // to see if it is a lead byte before doing the translation.
+ //
+ while (BytesInMultiByteString--) {
+ if (NlsLeadByteInfo[*(PUCHAR)MultiByteString++]) {
+ //
+ // Lead byte - translate the trail byte using the table
+ // that corresponds to this lead byte. NOTE: make sure
+ // we have a trail byte to convert.
+ //
+ if (BytesInMultiByteString == 0) {
+ //
+ // RtlMultibyteToUnicodeN() uses the unicode
+ // default character if the last multibyte
+ // character is a lead byte.
+ //
+ cbUnicode += sizeof(WCHAR);
+ break;
+ } else {
+ BytesInMultiByteString--;
+ MultiByteString++;
+ }
+ }
+ cbUnicode += sizeof(WCHAR);
+ }
+ *BytesInUnicodeString = cbUnicode;
+ } else {
+ //
+ // The ACP is a single byte code page.
+ //
+ *BytesInUnicodeString = BytesInMultiByteString * sizeof(WCHAR);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUnicodeToMultiByteSize(
+ OUT PULONG BytesInMultiByteString,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions determines how many bytes would be needed to represent
+ the specified Unicode source string as an ANSI string (not counting the
+ null terminator)
+
+Arguments:
+
+ BytesInMultiByteString - Returns the number of bytes an ANSI translation
+ of the Unicode string pointed to by UnicodeString would contain.
+
+ UnicodeString - Supplies the unicode source string whose ANSI length
+ is to be calculated.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed to by
+ UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ !SUCCESS - The conversion failed. A unicode character was encountered
+ that has no translation for the current ANSI Code Page (ACP).
+
+--*/
+
+{
+ ULONG cbMultiByte = 0;
+ ULONG CharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ /*
+ * convert from bytes to chars for easier loop handling.
+ */
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ if (NlsMbCodePageTag) {
+ USHORT MbChar;
+
+ while (CharsInUnicodeString--) {
+ MbChar = NlsUnicodeToMbAnsiData[ *UnicodeString++ ];
+ if (HIBYTE(MbChar) == 0) {
+ cbMultiByte++ ;
+ } else {
+ cbMultiByte += 2;
+ }
+ }
+ *BytesInMultiByteString = cbMultiByte;
+ }
+ else {
+ *BytesInMultiByteString = CharsInUnicodeString;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUnicodeToMultiByteN(
+ OUT PCH MultiByteString,
+ IN ULONG MaxBytesInMultiByteString,
+ OUT PULONG BytesInMultiByteString OPTIONAL,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ ansi string. The translation is done with respect to the
+ ANSI Code Page (ACP) loaded at boot time.
+
+Arguments:
+
+ MultiByteString - Returns an ansi string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned.
+
+ MaxBytesInMultiByteString - Supplies the maximum number of bytes to be
+ written to MultiByteString. If this causes MultiByteString to be a
+ truncated equivalent of UnicodeString, no error condition results.
+
+ BytesInMultiByteString - Returns the number of bytes in the returned
+ ansi string pointed to by MultiByteString.
+
+ UnicodeString - Supplies the unicode source string that is to be
+ converted to ansi.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed to by
+ UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+--*/
+
+{
+ ULONG TmpCount;
+ ULONG LoopCount;
+ PCH TranslateTable;
+ ULONG CharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Convert Unicode byte count to character count. Byte count of
+ // multibyte string is equivalent to character count.
+ //
+ if (!NlsMbCodePageTag) {
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (CharsInUnicodeString < MaxBytesInMultiByteString) ?
+ CharsInUnicodeString : MaxBytesInMultiByteString;
+
+ if (ARGUMENT_PRESENT(BytesInMultiByteString))
+ *BytesInMultiByteString = LoopCount;
+
+ TranslateTable = NlsUnicodeToAnsiData; // used to help the mips compiler
+
+ TmpCount = LoopCount & 0x0F;
+ UnicodeString += TmpCount;
+ MultiByteString += TmpCount;
+
+ do
+ {
+ switch( TmpCount ) {
+ default:
+ UnicodeString += 0x10;
+ MultiByteString += 0x10;
+
+ MultiByteString[-0x10] = TranslateTable[UnicodeString[-0x10]];
+ case 0x0F:
+ MultiByteString[-0x0F] = TranslateTable[UnicodeString[-0x0F]];
+ case 0x0E:
+ MultiByteString[-0x0E] = TranslateTable[UnicodeString[-0x0E]];
+ case 0x0D:
+ MultiByteString[-0x0D] = TranslateTable[UnicodeString[-0x0D]];
+ case 0x0C:
+ MultiByteString[-0x0C] = TranslateTable[UnicodeString[-0x0C]];
+ case 0x0B:
+ MultiByteString[-0x0B] = TranslateTable[UnicodeString[-0x0B]];
+ case 0x0A:
+ MultiByteString[-0x0A] = TranslateTable[UnicodeString[-0x0A]];
+ case 0x09:
+ MultiByteString[-0x09] = TranslateTable[UnicodeString[-0x09]];
+ case 0x08:
+ MultiByteString[-0x08] = TranslateTable[UnicodeString[-0x08]];
+ case 0x07:
+ MultiByteString[-0x07] = TranslateTable[UnicodeString[-0x07]];
+ case 0x06:
+ MultiByteString[-0x06] = TranslateTable[UnicodeString[-0x06]];
+ case 0x05:
+ MultiByteString[-0x05] = TranslateTable[UnicodeString[-0x05]];
+ case 0x04:
+ MultiByteString[-0x04] = TranslateTable[UnicodeString[-0x04]];
+ case 0x03:
+ MultiByteString[-0x03] = TranslateTable[UnicodeString[-0x03]];
+ case 0x02:
+ MultiByteString[-0x02] = TranslateTable[UnicodeString[-0x02]];
+ case 0x01:
+ MultiByteString[-0x01] = TranslateTable[UnicodeString[-0x01]];
+ case 0x00:
+ ;
+ }
+
+ LoopCount -= TmpCount;
+ TmpCount = 0x10;
+ } while ( LoopCount > 0 );
+
+ /* end of copy... */
+ } else {
+ USHORT MbChar;
+ PCH MultiByteStringAnchor = MultiByteString;
+
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ while ( CharsInUnicodeString && MaxBytesInMultiByteString ) {
+
+ MbChar = NlsUnicodeToMbAnsiData[ *UnicodeString++ ];
+ if (HIBYTE(MbChar) != 0) {
+ //
+ // Need at least 2 bytes to copy a double byte char.
+ // Don't want to truncate in the middle of a DBCS char.
+ //
+ if (MaxBytesInMultiByteString-- < 2) {
+ break;
+ }
+ *MultiByteString++ = HIBYTE(MbChar); // lead byte
+ }
+ *MultiByteString++ = LOBYTE(MbChar);
+ MaxBytesInMultiByteString--;
+
+ CharsInUnicodeString--;
+ }
+
+ if (ARGUMENT_PRESENT(BytesInMultiByteString))
+ *BytesInMultiByteString = (ULONG)(MultiByteString - MultiByteStringAnchor);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeToMultiByteN(
+ OUT PCH MultiByteString,
+ IN ULONG MaxBytesInMultiByteString,
+ OUT PULONG BytesInMultiByteString OPTIONAL,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions upper cases the specified unicode source string and
+ converts it into an ansi string. The translation is done with respect
+ to the ANSI Code Page (ACP) loaded at boot time.
+
+Arguments:
+
+ MultiByteString - Returns an ansi string that is equivalent to the
+ upper case of the unicode source string. If the translation can
+ not be done, an error is returned.
+
+ MaxBytesInMultiByteString - Supplies the maximum number of bytes to be
+ written to MultiByteString. If this causes MultiByteString to be a
+ truncated equivalent of UnicodeString, no error condition results.
+
+ BytesInMultiByteString - Returns the number of bytes in the returned
+ ansi string pointed to by MultiByteString.
+
+ UnicodeString - Supplies the unicode source string that is to be
+ converted to ansi.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed to by
+ UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+--*/
+
+{
+ ULONG TmpCount;
+ ULONG LoopCount;
+ PCH TranslateTable;
+ ULONG CharsInUnicodeString;
+ UCHAR SbChar;
+ WCHAR UnicodeChar;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Convert Unicode byte count to character count. Byte count of
+ // multibyte string is equivalent to character count.
+ //
+ if (!NlsMbCodePageTag) {
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (CharsInUnicodeString < MaxBytesInMultiByteString) ?
+ CharsInUnicodeString : MaxBytesInMultiByteString;
+
+ if (ARGUMENT_PRESENT(BytesInMultiByteString))
+ *BytesInMultiByteString = LoopCount;
+
+ TranslateTable = NlsUnicodeToAnsiData; // used to help the mips compiler
+
+ TmpCount = LoopCount & 0x0F;
+ UnicodeString += TmpCount;
+ MultiByteString += TmpCount;
+
+ do
+ {
+ //
+ // Convert to ANSI and back to Unicode before upper casing
+ // to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+ switch( TmpCount ) {
+ default:
+ UnicodeString += 0x10;
+ MultiByteString += 0x10;
+
+ SbChar = TranslateTable[UnicodeString[-0x10]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x10] = TranslateTable[UnicodeChar];
+ case 0x0F:
+ SbChar = TranslateTable[UnicodeString[-0x0F]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x0F] = TranslateTable[UnicodeChar];
+ case 0x0E:
+ SbChar = TranslateTable[UnicodeString[-0x0E]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x0E] = TranslateTable[UnicodeChar];
+ case 0x0D:
+ SbChar = TranslateTable[UnicodeString[-0x0D]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x0D] = TranslateTable[UnicodeChar];
+ case 0x0C:
+ SbChar = TranslateTable[UnicodeString[-0x0C]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x0C] = TranslateTable[UnicodeChar];
+ case 0x0B:
+ SbChar = TranslateTable[UnicodeString[-0x0B]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x0B] = TranslateTable[UnicodeChar];
+ case 0x0A:
+ SbChar = TranslateTable[UnicodeString[-0x0A]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x0A] = TranslateTable[UnicodeChar];
+ case 0x09:
+ SbChar = TranslateTable[UnicodeString[-0x09]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x09] = TranslateTable[UnicodeChar];
+ case 0x08:
+ SbChar = TranslateTable[UnicodeString[-0x08]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x08] = TranslateTable[UnicodeChar];
+ case 0x07:
+ SbChar = TranslateTable[UnicodeString[-0x07]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x07] = TranslateTable[UnicodeChar];
+ case 0x06:
+ SbChar = TranslateTable[UnicodeString[-0x06]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x06] = TranslateTable[UnicodeChar];
+ case 0x05:
+ SbChar = TranslateTable[UnicodeString[-0x05]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x05] = TranslateTable[UnicodeChar];
+ case 0x04:
+ SbChar = TranslateTable[UnicodeString[-0x04]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x04] = TranslateTable[UnicodeChar];
+ case 0x03:
+ SbChar = TranslateTable[UnicodeString[-0x03]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x03] = TranslateTable[UnicodeChar];
+ case 0x02:
+ SbChar = TranslateTable[UnicodeString[-0x02]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x02] = TranslateTable[UnicodeChar];
+ case 0x01:
+ SbChar = TranslateTable[UnicodeString[-0x01]];
+ UnicodeChar = NlsAnsiToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MultiByteString[-0x01] = TranslateTable[UnicodeChar];
+ case 0x00:
+ ;
+ }
+
+ LoopCount -= TmpCount;
+ TmpCount = 0x10;
+ } while ( LoopCount > 0 );
+
+ /* end of copy... */
+ } else {
+ USHORT MbChar;
+ register USHORT Entry;
+ PCH MultiByteStringAnchor = MultiByteString;
+
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ while ( CharsInUnicodeString && MaxBytesInMultiByteString ) {
+ //
+ // Convert to ANSI and back to Unicode before upper casing
+ // to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+ MbChar = NlsUnicodeToMbAnsiData[ *UnicodeString++ ];
+ if ( NlsLeadByteInfo[HIBYTE(MbChar)] ) {
+ //
+ // Lead byte - translate the trail byte using the table
+ // that corresponds to this lead byte.
+ //
+ Entry = NlsLeadByteInfo[HIBYTE(MbChar)];
+ UnicodeChar = (WCHAR)NlsMbAnsiCodePageTables[ Entry + LOBYTE(MbChar) ];
+ } else {
+ //
+ // Single byte character.
+ //
+ UnicodeChar = NlsAnsiToUnicodeData[LOBYTE(MbChar)];
+ }
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MbChar = NlsUnicodeToMbAnsiData[UnicodeChar];
+
+ if (HIBYTE(MbChar) != 0) {
+ //
+ // Need at least 2 bytes to copy a double byte char.
+ // Don't want to truncate in the middle of a DBCS char.
+ //
+ if (MaxBytesInMultiByteString-- < 2) {
+ break;
+ }
+ *MultiByteString++ = HIBYTE(MbChar); // lead byte
+ }
+ *MultiByteString++ = LOBYTE(MbChar);
+ MaxBytesInMultiByteString--;
+
+ CharsInUnicodeString--;
+ }
+
+ if (ARGUMENT_PRESENT(BytesInMultiByteString))
+ *BytesInMultiByteString = (ULONG)(MultiByteString - MultiByteStringAnchor);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlUnicodeToOemN(
+ OUT PCH OemString,
+ IN ULONG MaxBytesInOemString,
+ OUT PULONG BytesInOemString OPTIONAL,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ oem string. The translation is done with respect to the OEM Code
+ Page (OCP) loaded at boot time.
+
+Arguments:
+
+ OemString - Returns an oem string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned.
+
+ MaxBytesInOemString - Supplies the maximum number of bytes to be
+ written to OemString. If this causes OemString to be a
+ truncated equivalent of UnicodeString, no error condition results.
+
+ BytesInOemString - Returns the number of bytes in the returned
+ oem string pointed to by OemString.
+
+ UnicodeString - Supplies the unicode source string that is to be
+ converted to oem.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed to by
+ UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_BUFFER_OVERFLOW - MaxBytesInUnicodeString was not enough to hold
+ the whole Oem string. It was converted correct to the point though.
+
+--*/
+
+{
+ ULONG TmpCount;
+ ULONG LoopCount;
+ PCH TranslateTable;
+ ULONG CharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Convert Unicode byte count to character count. Byte count of
+ // multibyte string is equivalent to character count.
+ //
+ if (!NlsMbOemCodePageTag) {
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (CharsInUnicodeString < MaxBytesInOemString) ?
+ CharsInUnicodeString : MaxBytesInOemString;
+
+ if (ARGUMENT_PRESENT(BytesInOemString))
+ *BytesInOemString = LoopCount;
+
+ TranslateTable = NlsUnicodeToOemData; // used to help the mips compiler
+
+ TmpCount = LoopCount & 0x0F;
+ UnicodeString += TmpCount;
+ OemString += TmpCount;
+
+ do
+ {
+ switch( TmpCount ) {
+ default:
+ UnicodeString += 0x10;
+ OemString += 0x10;
+
+ OemString[-0x10] = TranslateTable[UnicodeString[-0x10]];
+ case 0x0F:
+ OemString[-0x0F] = TranslateTable[UnicodeString[-0x0F]];
+ case 0x0E:
+ OemString[-0x0E] = TranslateTable[UnicodeString[-0x0E]];
+ case 0x0D:
+ OemString[-0x0D] = TranslateTable[UnicodeString[-0x0D]];
+ case 0x0C:
+ OemString[-0x0C] = TranslateTable[UnicodeString[-0x0C]];
+ case 0x0B:
+ OemString[-0x0B] = TranslateTable[UnicodeString[-0x0B]];
+ case 0x0A:
+ OemString[-0x0A] = TranslateTable[UnicodeString[-0x0A]];
+ case 0x09:
+ OemString[-0x09] = TranslateTable[UnicodeString[-0x09]];
+ case 0x08:
+ OemString[-0x08] = TranslateTable[UnicodeString[-0x08]];
+ case 0x07:
+ OemString[-0x07] = TranslateTable[UnicodeString[-0x07]];
+ case 0x06:
+ OemString[-0x06] = TranslateTable[UnicodeString[-0x06]];
+ case 0x05:
+ OemString[-0x05] = TranslateTable[UnicodeString[-0x05]];
+ case 0x04:
+ OemString[-0x04] = TranslateTable[UnicodeString[-0x04]];
+ case 0x03:
+ OemString[-0x03] = TranslateTable[UnicodeString[-0x03]];
+ case 0x02:
+ OemString[-0x02] = TranslateTable[UnicodeString[-0x02]];
+ case 0x01:
+ OemString[-0x01] = TranslateTable[UnicodeString[-0x01]];
+ case 0x00:
+ ;
+ }
+
+ LoopCount -= TmpCount;
+ TmpCount = 0x10;
+ } while ( LoopCount > 0 );
+
+ /* end of copy... */
+ } else {
+ register USHORT MbChar;
+ PCH OemStringAnchor = OemString;
+
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ while ( CharsInUnicodeString && MaxBytesInOemString ) {
+
+ MbChar = NlsUnicodeToMbOemData[ *UnicodeString++ ];
+ if (HIBYTE(MbChar) != 0) {
+ //
+ // Need at least 2 bytes to copy a double byte char.
+ // Don't want to truncate in the middle of a DBCS char.
+ //
+ if (MaxBytesInOemString-- < 2) {
+ break;
+ }
+ *OemString++ = HIBYTE(MbChar); // lead byte
+ }
+ *OemString++ = LOBYTE(MbChar);
+ MaxBytesInOemString--;
+
+ CharsInUnicodeString--;
+ }
+
+ if (ARGUMENT_PRESENT(BytesInOemString))
+ *BytesInOemString = (ULONG)(OemString - OemStringAnchor);
+ }
+
+ //
+ // Check if we were able to use all of the source Unicode String
+ //
+ return ( CharsInUnicodeString <= MaxBytesInOemString ) ?
+ STATUS_SUCCESS :
+ STATUS_BUFFER_OVERFLOW;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeToOemN(
+ OUT PCH OemString,
+ IN ULONG MaxBytesInOemString,
+ OUT PULONG BytesInOemString OPTIONAL,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions upper cases the specified unicode source string and
+ converts it into an oem string. The translation is done with respect
+ to the OEM Code Page (OCP) loaded at boot time.
+
+Arguments:
+
+ OemString - Returns an oem string that is equivalent to the upper
+ case of the unicode source string. If the translation can not
+ be done, an error is returned.
+
+ MaxBytesInOemString - Supplies the maximum number of bytes to be
+ written to OemString. If this causes OemString to be a
+ truncated equivalent of UnicodeString, no error condition results.
+
+ BytesInOemString - Returns the number of bytes in the returned
+ oem string pointed to by OemString.
+
+ UnicodeString - Supplies the unicode source string that is to be
+ converted to oem.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed
+ to by UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_BUFFER_OVERFLOW - MaxBytesInUnicodeString was not enough to
+ hold the whole Oem string. It was converted correctly to that
+ point, though.
+
+--*/
+
+{
+ ULONG TmpCount;
+ ULONG LoopCount;
+ PCH TranslateTable;
+ ULONG CharsInUnicodeString;
+ UCHAR SbChar;
+ WCHAR UnicodeChar;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Convert Unicode byte count to character count. Byte count of
+ // multibyte string is equivalent to character count.
+ //
+ if (!NlsMbOemCodePageTag) {
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (CharsInUnicodeString < MaxBytesInOemString) ?
+ CharsInUnicodeString : MaxBytesInOemString;
+
+ if (ARGUMENT_PRESENT(BytesInOemString))
+ *BytesInOemString = LoopCount;
+
+ TranslateTable = NlsUnicodeToOemData; // used to help the mips compiler
+
+ TmpCount = LoopCount & 0x0F;
+ UnicodeString += TmpCount;
+ OemString += TmpCount;
+
+ do
+ {
+ //
+ // Convert to OEM and back to Unicode before upper casing
+ // to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+ switch( TmpCount ) {
+ default:
+ UnicodeString += 0x10;
+ OemString += 0x10;
+
+ SbChar = TranslateTable[UnicodeString[-0x10]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x10] = TranslateTable[UnicodeChar];
+ case 0x0F:
+ SbChar = TranslateTable[UnicodeString[-0x0F]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x0F] = TranslateTable[UnicodeChar];
+ case 0x0E:
+ SbChar = TranslateTable[UnicodeString[-0x0E]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x0E] = TranslateTable[UnicodeChar];
+ case 0x0D:
+ SbChar = TranslateTable[UnicodeString[-0x0D]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x0D] = TranslateTable[UnicodeChar];
+ case 0x0C:
+ SbChar = TranslateTable[UnicodeString[-0x0C]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x0C] = TranslateTable[UnicodeChar];
+ case 0x0B:
+ SbChar = TranslateTable[UnicodeString[-0x0B]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x0B] = TranslateTable[UnicodeChar];
+ case 0x0A:
+ SbChar = TranslateTable[UnicodeString[-0x0A]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x0A] = TranslateTable[UnicodeChar];
+ case 0x09:
+ SbChar = TranslateTable[UnicodeString[-0x09]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x09] = TranslateTable[UnicodeChar];
+ case 0x08:
+ SbChar = TranslateTable[UnicodeString[-0x08]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x08] = TranslateTable[UnicodeChar];
+ case 0x07:
+ SbChar = TranslateTable[UnicodeString[-0x07]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x07] = TranslateTable[UnicodeChar];
+ case 0x06:
+ SbChar = TranslateTable[UnicodeString[-0x06]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x06] = TranslateTable[UnicodeChar];
+ case 0x05:
+ SbChar = TranslateTable[UnicodeString[-0x05]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x05] = TranslateTable[UnicodeChar];
+ case 0x04:
+ SbChar = TranslateTable[UnicodeString[-0x04]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x04] = TranslateTable[UnicodeChar];
+ case 0x03:
+ SbChar = TranslateTable[UnicodeString[-0x03]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x03] = TranslateTable[UnicodeChar];
+ case 0x02:
+ SbChar = TranslateTable[UnicodeString[-0x02]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x02] = TranslateTable[UnicodeChar];
+ case 0x01:
+ SbChar = TranslateTable[UnicodeString[-0x01]];
+ UnicodeChar = NlsOemToUnicodeData[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ OemString[-0x01] = TranslateTable[UnicodeChar];
+ case 0x00:
+ ;
+ }
+
+ LoopCount -= TmpCount;
+ TmpCount = 0x10;
+ } while ( LoopCount > 0 );
+
+ /* end of copy... */
+ } else {
+ USHORT MbChar;
+ register USHORT Entry;
+ PCH OemStringAnchor = OemString;
+
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ while ( CharsInUnicodeString && MaxBytesInOemString ) {
+ //
+ // Convert to OEM and back to Unicode before upper casing
+ // to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+ MbChar = NlsUnicodeToMbOemData[ *UnicodeString++ ];
+ if (NlsOemLeadByteInfo[HIBYTE(MbChar)]) {
+ //
+ // Lead byte - translate the trail byte using the table
+ // that corresponds to this lead byte.
+ //
+ Entry = NlsOemLeadByteInfo[HIBYTE(MbChar)];
+ UnicodeChar = (WCHAR)NlsMbOemCodePageTables[ Entry + LOBYTE(MbChar) ];
+ } else {
+ //
+ // Single byte character.
+ //
+ UnicodeChar = NlsOemToUnicodeData[LOBYTE(MbChar)];
+ }
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MbChar = NlsUnicodeToMbOemData[UnicodeChar];
+
+ if (HIBYTE(MbChar) != 0) {
+ //
+ // Need at least 2 bytes to copy a double byte char.
+ // Don't want to truncate in the middle of a DBCS char.
+ //
+ if (MaxBytesInOemString-- < 2) {
+ break;
+ }
+ *OemString++ = HIBYTE(MbChar); // lead byte
+ }
+ *OemString++ = LOBYTE(MbChar);
+ MaxBytesInOemString--;
+
+ CharsInUnicodeString--;
+ }
+
+ if (ARGUMENT_PRESENT(BytesInOemString))
+ *BytesInOemString = (ULONG)(OemString - OemStringAnchor);
+ }
+
+ //
+ // Check if we were able to use all of the source Unicode String
+ //
+ return ( CharsInUnicodeString <= MaxBytesInOemString ) ?
+ STATUS_SUCCESS :
+ STATUS_BUFFER_OVERFLOW;
+}
+
+BOOLEAN
+RtlpDidUnicodeToOemWork(
+ IN POEM_STRING OemString,
+ IN PUNICODE_STRING UnicodeString
+ )
+
+/*++
+
+Routine Description:
+
+ This function looks for the default character in the Oem string, making
+ sure it was not a correct translation from the Unicode source string.
+
+ This allows us to test whether or not a translation was really successful.
+
+Arguments:
+
+ OemString - The result of conversion from the unicode string.
+
+ UnicodeString - The source of the Oem string.
+
+Return Value:
+
+ TRUE if the Unicode to Oem translation caused no default characters to be
+ inserted. FALSE otherwise.
+
+--*/
+
+{
+ ULONG OemOffset;
+ BOOLEAN Result = TRUE;
+
+ RTL_PAGED_CODE();
+
+ if (!NlsMbOemCodePageTag) {
+
+ for (OemOffset = 0;
+ OemOffset < OemString->Length;
+ OemOffset += 1) {
+
+ if ((OemString->Buffer[OemOffset] == (UCHAR)OemDefaultChar) &&
+ (UnicodeString->Buffer[OemOffset] != OemTransUniDefaultChar)) {
+
+ Result = FALSE;
+ break;
+ }
+ }
+
+ } else {
+
+ ULONG UnicodeOffset;
+
+ for (OemOffset = 0, UnicodeOffset = 0;
+ OemOffset < OemString->Length;
+ OemOffset += 1, UnicodeOffset += 1) {
+
+ //
+ // If we landed on a DBCS character handle it accordingly
+ //
+
+ if (NlsOemLeadByteInfo[(UCHAR)OemString->Buffer[OemOffset]]) {
+
+ USHORT DbcsChar;
+
+ ASSERT( OemOffset + 1 < OemString->Length );
+
+ DbcsChar = NlsOemLeadByteInfo[(UCHAR)OemString->Buffer[OemOffset]] +
+ OemString->Buffer[++OemOffset];
+
+ if ((DbcsChar == OemDefaultChar) &&
+ (UnicodeString->Buffer[UnicodeOffset] != OemTransUniDefaultChar)) {
+
+ Result = FALSE;
+ break;
+ }
+
+ continue;
+ }
+
+ if ((OemString->Buffer[OemOffset] == (UCHAR)OemDefaultChar) &&
+ (UnicodeString->Buffer[UnicodeOffset] != OemTransUniDefaultChar)) {
+
+ Result = FALSE;
+ break;
+ }
+ }
+ }
+
+ return Result;
+}
+
+
+NTSTATUS
+RtlCustomCPToUnicodeN(
+ IN PCPTABLEINFO CustomCP,
+ OUT PWCH UnicodeString,
+ IN ULONG MaxBytesInUnicodeString,
+ OUT PULONG BytesInUnicodeString OPTIONAL,
+ IN PCH CustomCPString,
+ IN ULONG BytesInCustomCPString)
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified CustomCP source string into a
+ Unicode string. The translation is done with respect to the
+ CustomCP Code Page specified. Single byte characters
+ in the range 0x00 - 0x7f are simply zero extended as a performance
+ enhancement. In some far eastern code pages 0x5c is defined as the
+ Yen sign. For system translation we always want to consider 0x5c
+ to be the backslash character. We get this for free by zero extending.
+
+ NOTE: This routine only supports precomposed Unicode characters.
+
+Arguments:
+
+ CustomCP - Supplies the address of the code page that translations
+ are done relative to
+
+ UnicodeString - Returns a unicode string that is equivalent to
+ the CustomCP source string.
+
+ MaxBytesInUnicodeString - Supplies the maximum number of bytes to be
+ written to UnicodeString. If this causes UnicodeString to be a
+ truncated equivalent of CustomCPString, no error condition results.
+
+ BytesInUnicodeString - Returns the number of bytes in the returned
+ unicode string pointed to by UnicodeString.
+
+ CustomCPString - Supplies the CustomCP source string that is to be
+ converted to unicode.
+
+ BytesInCustomCPString - The number of bytes in the string pointed to
+ by CustomCPString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_ILLEGAL_CHARACTER - The final CustomCP character was illegal
+
+ STATUS_BUFFER_OVERFLOW - MaxBytesInUnicodeString was not enough to hold
+ the whole CustomCP string. It was converted correct to the point though.
+
+--*/
+
+{
+ ULONG LoopCount;
+ PUSHORT TranslateTable;
+ ULONG MaxCharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ if (!(CustomCP->DBCSCodePage)) {
+ //
+ // The Custom CP is a single byte code page.
+ //
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (MaxCharsInUnicodeString < BytesInCustomCPString) ?
+ MaxCharsInUnicodeString : BytesInCustomCPString;
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = LoopCount * sizeof(WCHAR);
+
+
+ TranslateTable = CustomCP->MultiByteTable;
+
+ quick_copy:
+ switch( LoopCount ) {
+ default:
+ UnicodeString[0x0F] = TranslateTable[(UCHAR)CustomCPString[0x0F]];
+ case 0x0F:
+ UnicodeString[0x0E] = TranslateTable[(UCHAR)CustomCPString[0x0E]];
+ case 0x0E:
+ UnicodeString[0x0D] = TranslateTable[(UCHAR)CustomCPString[0x0D]];
+ case 0x0D:
+ UnicodeString[0x0C] = TranslateTable[(UCHAR)CustomCPString[0x0C]];
+ case 0x0C:
+ UnicodeString[0x0B] = TranslateTable[(UCHAR)CustomCPString[0x0B]];
+ case 0x0B:
+ UnicodeString[0x0A] = TranslateTable[(UCHAR)CustomCPString[0x0A]];
+ case 0x0A:
+ UnicodeString[0x09] = TranslateTable[(UCHAR)CustomCPString[0x09]];
+ case 0x09:
+ UnicodeString[0x08] = TranslateTable[(UCHAR)CustomCPString[0x08]];
+ case 0x08:
+ UnicodeString[0x07] = TranslateTable[(UCHAR)CustomCPString[0x07]];
+ case 0x07:
+ UnicodeString[0x06] = TranslateTable[(UCHAR)CustomCPString[0x06]];
+ case 0x06:
+ UnicodeString[0x05] = TranslateTable[(UCHAR)CustomCPString[0x05]];
+ case 0x05:
+ UnicodeString[0x04] = TranslateTable[(UCHAR)CustomCPString[0x04]];
+ case 0x04:
+ UnicodeString[0x03] = TranslateTable[(UCHAR)CustomCPString[0x03]];
+ case 0x03:
+ UnicodeString[0x02] = TranslateTable[(UCHAR)CustomCPString[0x02]];
+ case 0x02:
+ UnicodeString[0x01] = TranslateTable[(UCHAR)CustomCPString[0x01]];
+ case 0x01:
+ UnicodeString[0x00] = TranslateTable[(UCHAR)CustomCPString[0x00]];
+ case 0x00:
+ ;
+ }
+
+ if ( LoopCount > 0x10 ) {
+ LoopCount -= 0x10;
+ CustomCPString += 0x10;
+ UnicodeString += 0x10;
+
+ goto quick_copy;
+ }
+ /* end of copy... */
+ } else {
+ register USHORT Entry;
+ PWCH UnicodeStringAnchor = UnicodeString;
+ PUSHORT NlsCustomLeadByteInfo = CustomCP->DBCSOffsets;
+
+ //
+ // The CP is a multibyte code page. Check each character
+ // to see if it is a lead byte before doing the translation.
+ //
+ MaxCharsInUnicodeString = MaxBytesInUnicodeString / sizeof(WCHAR);
+ TranslateTable = (PUSHORT)(CustomCP->DBCSOffsets);
+
+ while (MaxCharsInUnicodeString && BytesInCustomCPString) {
+ MaxCharsInUnicodeString--;
+ BytesInCustomCPString--;
+ if (NlsCustomLeadByteInfo[*(PUCHAR)CustomCPString]) {
+ //
+ // Lead byte - Make sure there is a trail byte. If not,
+ // pass back a space rather than an error. Some 3.x
+ // applications pass incorrect strings and don't expect
+ // to get an error.
+ //
+ if (BytesInCustomCPString == 0)
+ {
+ *UnicodeString++ = UnicodeNull;
+ break;
+ }
+
+ //
+ // Get the unicode character.
+ //
+ Entry = NlsCustomLeadByteInfo[*(PUCHAR)CustomCPString++];
+ *UnicodeString = TranslateTable[ Entry + *(PUCHAR)CustomCPString++ ];
+ UnicodeString++;
+
+ //
+ // Decrement count of bytes in multibyte string to account
+ // for the double byte character.
+ //
+ BytesInCustomCPString--;
+ } else {
+ //
+ // Single byte character.
+ //
+ *UnicodeString++ = (CustomCP->MultiByteTable)[*(PUCHAR)CustomCPString++];
+ }
+ }
+
+ if (ARGUMENT_PRESENT(BytesInUnicodeString))
+ *BytesInUnicodeString = (ULONG)((PCH)UnicodeString - (PCH)UnicodeStringAnchor);
+ }
+
+ //
+ // Check if we were able to use all of the source CustomCP String
+ //
+ return ( BytesInCustomCPString <= MaxCharsInUnicodeString ) ?
+ STATUS_SUCCESS :
+ STATUS_BUFFER_OVERFLOW;
+}
+
+
+NTSTATUS
+RtlUnicodeToCustomCPN(
+ IN PCPTABLEINFO CustomCP,
+ OUT PCH CustomCPString,
+ IN ULONG MaxBytesInCustomCPString,
+ OUT PULONG BytesInCustomCPString OPTIONAL,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions converts the specified unicode source string into an
+ CustomCP string. The translation is done with respect to the
+ CustomCP Code Page specified by CustomCp.
+
+Arguments:
+
+ CustomCP - Supplies the address of the code page that translations
+ are done relative to
+
+ CustomCPString - Returns an CustomCP string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned.
+
+ MaxBytesInCustomCPString - Supplies the maximum number of bytes to be
+ written to CustomCPString. If this causes CustomCPString to be a
+ truncated equivalent of UnicodeString, no error condition results.
+
+ BytesInCustomCPString - Returns the number of bytes in the returned
+ CustomCP string pointed to by CustomCPString.
+
+ UnicodeString - Supplies the unicode source string that is to be
+ converted to CustomCP.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed to by
+ UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_BUFFER_OVERFLOW - MaxBytesInUnicodeString was not enough to hold
+ the whole CustomCP string. It was converted correct to the point though.
+
+--*/
+
+{
+ ULONG TmpCount;
+ ULONG LoopCount;
+ PCH TranslateTable;
+ PUSHORT WideTranslateTable;
+ ULONG CharsInUnicodeString;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Convert Unicode byte count to character count. Byte count of
+ // multibyte string is equivalent to character count.
+ //
+ if (!(CustomCP->DBCSCodePage)) {
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (CharsInUnicodeString < MaxBytesInCustomCPString) ?
+ CharsInUnicodeString : MaxBytesInCustomCPString;
+
+ if (ARGUMENT_PRESENT(BytesInCustomCPString))
+ *BytesInCustomCPString = LoopCount;
+
+ TranslateTable = CustomCP->WideCharTable;
+
+ TmpCount = LoopCount & 0x0F;
+ UnicodeString += TmpCount;
+ CustomCPString += TmpCount;
+
+ do
+ {
+ switch( TmpCount ) {
+ default:
+ UnicodeString += 0x10;
+ CustomCPString += 0x10;
+
+ CustomCPString[-0x10] = TranslateTable[UnicodeString[-0x10]];
+ case 0x0F:
+ CustomCPString[-0x0F] = TranslateTable[UnicodeString[-0x0F]];
+ case 0x0E:
+ CustomCPString[-0x0E] = TranslateTable[UnicodeString[-0x0E]];
+ case 0x0D:
+ CustomCPString[-0x0D] = TranslateTable[UnicodeString[-0x0D]];
+ case 0x0C:
+ CustomCPString[-0x0C] = TranslateTable[UnicodeString[-0x0C]];
+ case 0x0B:
+ CustomCPString[-0x0B] = TranslateTable[UnicodeString[-0x0B]];
+ case 0x0A:
+ CustomCPString[-0x0A] = TranslateTable[UnicodeString[-0x0A]];
+ case 0x09:
+ CustomCPString[-0x09] = TranslateTable[UnicodeString[-0x09]];
+ case 0x08:
+ CustomCPString[-0x08] = TranslateTable[UnicodeString[-0x08]];
+ case 0x07:
+ CustomCPString[-0x07] = TranslateTable[UnicodeString[-0x07]];
+ case 0x06:
+ CustomCPString[-0x06] = TranslateTable[UnicodeString[-0x06]];
+ case 0x05:
+ CustomCPString[-0x05] = TranslateTable[UnicodeString[-0x05]];
+ case 0x04:
+ CustomCPString[-0x04] = TranslateTable[UnicodeString[-0x04]];
+ case 0x03:
+ CustomCPString[-0x03] = TranslateTable[UnicodeString[-0x03]];
+ case 0x02:
+ CustomCPString[-0x02] = TranslateTable[UnicodeString[-0x02]];
+ case 0x01:
+ CustomCPString[-0x01] = TranslateTable[UnicodeString[-0x01]];
+ case 0x00:
+ ;
+ }
+
+ LoopCount -= TmpCount;
+ TmpCount = 0x10;
+ } while ( LoopCount > 0 );
+
+ /* end of copy... */
+ } else {
+ USHORT MbChar;
+ PCH CustomCPStringAnchor = CustomCPString;
+
+ WideTranslateTable = CustomCP->WideCharTable;
+
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ while (CharsInUnicodeString && MaxBytesInCustomCPString) {
+
+ MbChar = WideTranslateTable[ *UnicodeString++ ];
+ if (HIBYTE(MbChar) != 0) {
+ //
+ // Need at least 2 bytes to copy a double byte char.
+ // Don't want to truncate in the middle of a DBCS char.
+ //
+ if (MaxBytesInCustomCPString-- < 2) {
+ break;
+ }
+ *CustomCPString++ = HIBYTE(MbChar); // lead byte
+ }
+ *CustomCPString++ = LOBYTE(MbChar);
+ MaxBytesInCustomCPString--;
+
+ CharsInUnicodeString--;
+ }
+
+ if (ARGUMENT_PRESENT(BytesInCustomCPString))
+ *BytesInCustomCPString = (ULONG)(CustomCPString - CustomCPStringAnchor);
+ }
+
+ //
+ // Check if we were able to use all of the source Unicode String
+ //
+ return ( CharsInUnicodeString <= MaxBytesInCustomCPString ) ?
+ STATUS_SUCCESS :
+ STATUS_BUFFER_OVERFLOW;
+}
+
+
+NTSTATUS
+RtlUpcaseUnicodeToCustomCPN(
+ IN PCPTABLEINFO CustomCP,
+ OUT PCH CustomCPString,
+ IN ULONG MaxBytesInCustomCPString,
+ OUT PULONG BytesInCustomCPString OPTIONAL,
+ IN PWCH UnicodeString,
+ IN ULONG BytesInUnicodeString)
+
+/*++
+
+Routine Description:
+
+ This functions upper cases the specified unicode source string and
+ converts it into a CustomCP string. The translation is done with
+ respect to the CustomCP Code Page specified by CustomCp.
+
+Arguments:
+
+ CustomCP - Supplies the address of the code page that translations
+ are done relative to
+
+ CustomCPString - Returns an CustomCP string that is equivalent to the
+ unicode source string. If the translation can not be done,
+ an error is returned.
+
+ MaxBytesInCustomCPString - Supplies the maximum number of bytes to be
+ written to CustomCPString. If this causes CustomCPString to be a
+ truncated equivalent of UnicodeString, no error condition results.
+
+ BytesInCustomCPString - Returns the number of bytes in the returned
+ CustomCP string pointed to by CustomCPString.
+
+ UnicodeString - Supplies the unicode source string that is to be
+ converted to CustomCP.
+
+ BytesInUnicodeString - The number of bytes in the the string pointed
+ to by UnicodeString.
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_BUFFER_OVERFLOW - MaxBytesInUnicodeString was not enough to
+ hold the whole CustomCP string. It was converted correctly to
+ that point, though.
+
+--*/
+
+{
+ ULONG TmpCount;
+ ULONG LoopCount;
+ PCH TranslateTable;
+ PUSHORT WideTranslateTable;
+ ULONG CharsInUnicodeString;
+ UCHAR SbChar;
+ WCHAR UnicodeChar;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Convert Unicode byte count to character count. Byte count of
+ // multibyte string is equivalent to character count.
+ //
+ if (!(CustomCP->DBCSCodePage)) {
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ LoopCount = (CharsInUnicodeString < MaxBytesInCustomCPString) ?
+ CharsInUnicodeString : MaxBytesInCustomCPString;
+
+ if (ARGUMENT_PRESENT(BytesInCustomCPString))
+ *BytesInCustomCPString = LoopCount;
+
+ TranslateTable = CustomCP->WideCharTable;
+
+ TmpCount = LoopCount & 0x0F;
+ UnicodeString += TmpCount;
+ CustomCPString += TmpCount;
+
+ do
+ {
+ //
+ // Convert to Single Byte and back to Unicode before upper
+ // casing to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+ switch( TmpCount ) {
+ default:
+ UnicodeString += 0x10;
+ CustomCPString += 0x10;
+
+ SbChar = TranslateTable[UnicodeString[-0x10]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x10] = TranslateTable[UnicodeChar];
+ case 0x0F:
+ SbChar = TranslateTable[UnicodeString[-0x0F]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x0F] = TranslateTable[UnicodeChar];
+ case 0x0E:
+ SbChar = TranslateTable[UnicodeString[-0x0E]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x0E] = TranslateTable[UnicodeChar];
+ case 0x0D:
+ SbChar = TranslateTable[UnicodeString[-0x0D]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x0D] = TranslateTable[UnicodeChar];
+ case 0x0C:
+ SbChar = TranslateTable[UnicodeString[-0x0C]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x0C] = TranslateTable[UnicodeChar];
+ case 0x0B:
+ SbChar = TranslateTable[UnicodeString[-0x0B]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x0B] = TranslateTable[UnicodeChar];
+ case 0x0A:
+ SbChar = TranslateTable[UnicodeString[-0x0A]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x0A] = TranslateTable[UnicodeChar];
+ case 0x09:
+ SbChar = TranslateTable[UnicodeString[-0x09]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x09] = TranslateTable[UnicodeChar];
+ case 0x08:
+ SbChar = TranslateTable[UnicodeString[-0x08]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x08] = TranslateTable[UnicodeChar];
+ case 0x07:
+ SbChar = TranslateTable[UnicodeString[-0x07]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x07] = TranslateTable[UnicodeChar];
+ case 0x06:
+ SbChar = TranslateTable[UnicodeString[-0x06]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x06] = TranslateTable[UnicodeChar];
+ case 0x05:
+ SbChar = TranslateTable[UnicodeString[-0x05]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x05] = TranslateTable[UnicodeChar];
+ case 0x04:
+ SbChar = TranslateTable[UnicodeString[-0x04]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x04] = TranslateTable[UnicodeChar];
+ case 0x03:
+ SbChar = TranslateTable[UnicodeString[-0x03]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x03] = TranslateTable[UnicodeChar];
+ case 0x02:
+ SbChar = TranslateTable[UnicodeString[-0x02]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x02] = TranslateTable[UnicodeChar];
+ case 0x01:
+ SbChar = TranslateTable[UnicodeString[-0x01]];
+ UnicodeChar = (CustomCP->MultiByteTable)[SbChar];
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ CustomCPString[-0x01] = TranslateTable[UnicodeChar];
+ case 0x00:
+ ;
+ }
+
+ LoopCount -= TmpCount;
+ TmpCount = 0x10;
+ } while ( LoopCount > 0 );
+
+ /* end of copy... */
+ } else {
+ USHORT MbChar;
+ register USHORT Entry;
+ PCH CustomCPStringAnchor = CustomCPString;
+ PUSHORT NlsCustomLeadByteInfo = CustomCP->DBCSOffsets;
+
+ WideTranslateTable = CustomCP->WideCharTable;
+
+ CharsInUnicodeString = BytesInUnicodeString / sizeof(WCHAR);
+
+ while ( CharsInUnicodeString && MaxBytesInCustomCPString ) {
+ //
+ // Convert to Single Byte and back to Unicode before upper
+ // casing to ensure the visual best fits are converted and
+ // upper cased properly.
+ //
+ MbChar = WideTranslateTable[ *UnicodeString++ ];
+ if (NlsCustomLeadByteInfo[HIBYTE(MbChar)]) {
+ //
+ // Lead byte - translate the trail byte using the table
+ // that corresponds to this lead byte.
+ //
+ Entry = NlsCustomLeadByteInfo[HIBYTE(MbChar)];
+ UnicodeChar = NlsCustomLeadByteInfo[ Entry + LOBYTE(MbChar) ];
+ } else {
+ //
+ // Single byte character.
+ //
+ UnicodeChar = (CustomCP->MultiByteTable)[LOBYTE(MbChar)];
+ }
+ UnicodeChar = (WCHAR)NLS_UPCASE(UnicodeChar);
+ MbChar = WideTranslateTable[UnicodeChar];
+
+ if (HIBYTE(MbChar) != 0) {
+ //
+ // Need at least 2 bytes to copy a double byte char.
+ // Don't want to truncate in the middle of a DBCS char.
+ //
+ if (MaxBytesInCustomCPString-- < 2) {
+ break;
+ }
+ *CustomCPString++ = HIBYTE(MbChar); // lead byte
+ }
+ *CustomCPString++ = LOBYTE(MbChar);
+ MaxBytesInCustomCPString--;
+
+ CharsInUnicodeString--;
+ }
+
+ if (ARGUMENT_PRESENT(BytesInCustomCPString))
+ *BytesInCustomCPString = (ULONG)(CustomCPString - CustomCPStringAnchor);
+ }
+
+ //
+ // Check if we were able to use all of the source Unicode String
+ //
+ return ( CharsInUnicodeString <= MaxBytesInCustomCPString ) ?
+ STATUS_SUCCESS :
+ STATUS_BUFFER_OVERFLOW;
+}
+
+#define MB_TBL_SIZE 256 /* size of MB tables */
+#define GLYPH_TBL_SIZE MB_TBL_SIZE /* size of GLYPH tables */
+#define DBCS_TBL_SIZE 256 /* size of DBCS tables */
+#define GLYPH_HEADER 1 /* size of GLYPH table header */
+#define DBCS_HEADER 1 /* size of DBCS table header */
+#define LANG_HEADER 1 /* size of LANGUAGE file header */
+#define UP_HEADER 1 /* size of UPPERCASE table header */
+#define LO_HEADER 1 /* size of LOWERCASE table header */
+
+VOID
+RtlInitCodePageTable(
+ IN PUSHORT TableBase,
+ OUT PCPTABLEINFO CodePageTable
+ )
+{
+ USHORT offMB;
+ USHORT offWC;
+ PUSHORT pGlyph;
+ PUSHORT pRange;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Get the offsets.
+ //
+
+ offMB = TableBase[0];
+ offWC = offMB + TableBase[offMB];
+
+
+ //
+ // Attach Code Page Info to CP hash node.
+ //
+
+ CodePageTable->CodePage = TableBase[1];
+ CodePageTable->MaximumCharacterSize = TableBase[2];
+ CodePageTable->DefaultChar = TableBase[3]; // default character (MB)
+ CodePageTable->UniDefaultChar = TableBase[4]; // default character (Unicode)
+ CodePageTable->TransDefaultChar = TableBase[5]; // trans of default char (Unicode)
+ CodePageTable->TransUniDefaultChar = TableBase[6]; // trans of Uni default char (MB)
+ RtlMoveMemory(
+ &CodePageTable->LeadByte,
+ &TableBase[7],
+ MAXIMUM_LEADBYTES
+ );
+ CodePageTable->MultiByteTable = (TableBase + offMB + 1);
+
+ pGlyph = CodePageTable->MultiByteTable + MB_TBL_SIZE;
+
+ if (pGlyph[0] != 0) {
+ pRange = CodePageTable->DBCSRanges = pGlyph + GLYPH_HEADER + GLYPH_TBL_SIZE;
+ }
+ else {
+ pRange = CodePageTable->DBCSRanges = pGlyph + GLYPH_HEADER;
+ }
+
+ //
+ // Attach DBCS information to CP hash node.
+ //
+
+ if (pRange[0] > 0) {
+ CodePageTable->DBCSOffsets = pRange + DBCS_HEADER;
+ CodePageTable->DBCSCodePage = 1;
+ }
+ else {
+ CodePageTable->DBCSCodePage = 0;
+ CodePageTable->DBCSOffsets = NULL;
+ }
+
+ CodePageTable->WideCharTable = (TableBase + offWC + 1);
+}
+
+
+VOID
+RtlpInitUpcaseTable(
+ IN PUSHORT TableBase,
+ OUT PNLSTABLEINFO CodePageTable
+ )
+{
+ USHORT offUP;
+ USHORT offLO;
+
+ //
+ // Get the offsets.
+ //
+
+ offUP = LANG_HEADER;
+ offLO = offUP + TableBase[offUP];
+
+ CodePageTable->UpperCaseTable = TableBase + offUP + UP_HEADER;
+ CodePageTable->LowerCaseTable = TableBase + offLO + LO_HEADER;
+}
+
+
+VOID
+RtlInitNlsTables(
+ IN PUSHORT AnsiNlsBase,
+ IN PUSHORT OemNlsBase,
+ IN PUSHORT LanguageNlsBase,
+ OUT PNLSTABLEINFO TableInfo
+ )
+{
+ RTL_PAGED_CODE();
+
+ RtlInitCodePageTable(AnsiNlsBase,&TableInfo->AnsiTableInfo);
+ RtlInitCodePageTable(OemNlsBase,&TableInfo->OemTableInfo);
+ RtlpInitUpcaseTable(LanguageNlsBase,TableInfo);
+}
+
+
+VOID
+RtlResetRtlTranslations(
+ PNLSTABLEINFO TableInfo
+ )
+{
+ RTL_PAGED_CODE();
+
+ if ( TableInfo->AnsiTableInfo.DBCSCodePage ) {
+ RtlMoveMemory(NlsLeadByteInfo,TableInfo->AnsiTableInfo.DBCSOffsets,DBCS_TBL_SIZE*sizeof(USHORT));
+ }
+ else {
+ RtlZeroMemory(NlsLeadByteInfo,DBCS_TBL_SIZE*sizeof(USHORT));
+ }
+
+ NlsMbAnsiCodePageTables = (PUSHORT)TableInfo->AnsiTableInfo.DBCSOffsets;
+
+ NlsAnsiToUnicodeData = TableInfo->AnsiTableInfo.MultiByteTable;
+ NlsUnicodeToAnsiData = (PCH)TableInfo->AnsiTableInfo.WideCharTable;
+ NlsUnicodeToMbAnsiData = (PUSHORT)TableInfo->AnsiTableInfo.WideCharTable;
+ NlsMbCodePageTag = TableInfo->AnsiTableInfo.DBCSCodePage ? TRUE : FALSE;
+ NlsAnsiCodePage = TableInfo->AnsiTableInfo.CodePage;
+
+ if ( TableInfo->OemTableInfo.DBCSCodePage ) {
+ RtlMoveMemory(NlsOemLeadByteInfo,TableInfo->OemTableInfo.DBCSOffsets,DBCS_TBL_SIZE*sizeof(USHORT));
+ }
+ else {
+ RtlZeroMemory(NlsOemLeadByteInfo,DBCS_TBL_SIZE*sizeof(USHORT));
+ }
+
+ NlsMbOemCodePageTables = (PUSHORT)TableInfo->OemTableInfo.DBCSOffsets;
+
+ NlsOemToUnicodeData = TableInfo->OemTableInfo.MultiByteTable;
+ NlsUnicodeToOemData = (PCH)TableInfo->OemTableInfo.WideCharTable;
+ NlsUnicodeToMbOemData = (PUSHORT)TableInfo->OemTableInfo.WideCharTable;
+ NlsMbOemCodePageTag = TableInfo->OemTableInfo.DBCSCodePage ? TRUE : FALSE;
+ NlsOemCodePage = TableInfo->OemTableInfo.CodePage;
+ OemDefaultChar = TableInfo->OemTableInfo.DefaultChar;
+ OemTransUniDefaultChar = TableInfo->OemTableInfo.TransDefaultChar;
+
+ Nls844UnicodeUpcaseTable = TableInfo->UpperCaseTable;
+ Nls844UnicodeLowercaseTable = TableInfo->LowerCaseTable;
+ UnicodeDefaultChar = TableInfo->AnsiTableInfo.UniDefaultChar;
+}
+
+void
+RtlGetDefaultCodePage(
+ OUT PUSHORT AnsiCodePage,
+ OUT PUSHORT OemCodePage
+ )
+{
+ RTL_PAGED_CODE();
+ *AnsiCodePage = NlsAnsiCodePage;
+ *OemCodePage = NlsOemCodePage;
+}
+
+
+
+
+
diff --git a/private/ntos/rtl/ntrtlp.h b/private/ntos/rtl/ntrtlp.h
new file mode 100644
index 000000000..c7494467d
--- /dev/null
+++ b/private/ntos/rtl/ntrtlp.h
@@ -0,0 +1,409 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ntrtlp.h
+
+Abstract:
+
+ Include file for NT runtime routines that are callable by both
+ kernel mode code in the executive and user mode code in various
+ NT subsystems, but which are private interfaces.
+
+Author:
+
+ David N. Cutler (davec) 15-Aug-1989
+
+Environment:
+
+ These routines are statically linked in the caller's executable and
+ are callable in either kernel mode or user mode.
+
+Revision History:
+
+--*/
+
+#ifndef _NTRTLP_
+#define _NTRTLP_
+#include <ntos.h>
+#include <nturtl.h>
+#include <zwapi.h>
+
+#ifdef _X86_
+#include "i386\ntrtl386.h"
+#endif
+
+#ifdef _MIPS_
+#include "mips\ntrtlmip.h"
+#endif
+
+#ifdef _PPC_
+#include "ppc\ntrtlppc.h"
+#endif
+
+#ifdef _ALPHA_
+#include "alpha\ntrtlalp.h"
+#endif
+
+#ifdef BLDR_KERNEL_RUNTIME
+#undef try
+#define try if(1)
+#undef except
+#define except(a) else if (0)
+#undef finally
+#define finally if (1)
+#undef GetExceptionCode
+#define GetExceptionCode() 1
+#define finally if (1)
+#endif
+
+#include "string.h"
+#include "wchar.h"
+
+//
+// Machine state reporting. See machine specific includes for more.
+//
+
+VOID
+RtlpGetStackLimits (
+ OUT PULONG LowLimit,
+ OUT PULONG HighLimit
+ );
+
+LONG
+LdrpCompareResourceNames(
+ IN ULONG ResourceName,
+ IN PIMAGE_RESOURCE_DIRECTORY ResourceDirectory,
+ IN PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirectoryEntry
+ );
+
+NTSTATUS
+LdrpSearchResourceSection(
+ IN PVOID DllHandle,
+ IN PULONG ResourceIdPath,
+ IN ULONG ResourceIdPathLength,
+ IN BOOLEAN FindDirectoryEntry,
+ OUT PVOID *ResourceDirectoryOrData
+ );
+
+LONG
+LdrpCompareResourceNames_U(
+ IN ULONG ResourceName,
+ IN PIMAGE_RESOURCE_DIRECTORY ResourceDirectory,
+ IN PIMAGE_RESOURCE_DIRECTORY_ENTRY ResourceDirectoryEntry
+ );
+
+NTSTATUS
+LdrpSearchResourceSection_U(
+ IN PVOID DllHandle,
+ IN PULONG ResourceIdPath,
+ IN ULONG ResourceIdPathLength,
+ IN BOOLEAN FindDirectoryEntry,
+ OUT PVOID *ResourceDirectoryOrData
+ );
+
+NTSTATUS
+LdrpAccessResourceData(
+ IN PVOID DllHandle,
+ IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
+ OUT PVOID *Address OPTIONAL,
+ OUT PULONG Size OPTIONAL
+ );
+
+VOID
+RtlpAnsiPszToUnicodePsz(
+ IN PCHAR AnsiString,
+ IN WCHAR *UnicodeString,
+ IN USHORT AnsiStringLength
+ );
+
+BOOLEAN
+RtlpDidUnicodeToOemWork(
+ IN POEM_STRING OemString,
+ IN PUNICODE_STRING UnicodeString
+ );
+
+extern CCHAR RtlpBitsClearAnywhere[256];
+extern CCHAR RtlpBitsClearLow[256];
+extern CCHAR RtlpBitsClearHigh[256];
+extern CCHAR RtlpBitsClearTotal[256];
+
+//
+// Macro that tells how many contiguous bits are set (i.e., 1) in
+// a byte
+//
+
+#define RtlpBitSetAnywhere( Byte ) RtlpBitsClearAnywhere[ (~(Byte) & 0xFF) ]
+
+
+//
+// Macro that tells how many contiguous LOW order bits are set
+// (i.e., 1) in a byte
+//
+
+#define RtlpBitsSetLow( Byte ) RtlpBitsClearLow[ (~(Byte) & 0xFF) ]
+
+
+//
+// Macro that tells how many contiguous HIGH order bits are set
+// (i.e., 1) in a byte
+//
+
+#define RtlpBitsSetHigh( Byte ) RtlpBitsClearHigh[ (~(Byte) & 0xFF) ]
+
+
+//
+// Macro that tells how many set bits (i.e., 1) there are in a byte
+//
+
+#define RtlpBitsSetTotal( Byte ) RtlpBitsClearTotal[ (~(Byte) & 0xFF) ]
+
+
+
+//
+// Upcase data table
+//
+
+extern PUSHORT Nls844UnicodeUpcaseTable;
+extern PUSHORT Nls844UnicodeLowercaseTable;
+
+
+//
+// Macros for Upper Casing a Unicode Code Point.
+//
+
+
+#define LOBYTE(w) ((UCHAR)((w)))
+#define HIBYTE(w) ((UCHAR)(((USHORT)((w)) >> 8) & 0xFF))
+
+#define GET8(w) ((ULONG)(((w) >> 8) & 0xff))
+#define GETHI4(w) ((ULONG)(((w) >> 4) & 0xf))
+#define GETLO4(w) ((ULONG)((w) & 0xf))
+
+/***************************************************************************\
+* TRAVERSE844W
+*
+* Traverses the 8:4:4 translation table for the given wide character. It
+* returns the final value of the 8:4:4 table, which is a WORD in length.
+*
+* Broken Down Version:
+* --------------------
+* Incr = pTable[GET8(wch)];
+* Incr = pTable[Incr + GETHI4(wch)];
+* Value = pTable[Incr + GETLO4(wch)];
+*
+* DEFINED AS A MACRO.
+*
+* 05-31-91 JulieB Created.
+\***************************************************************************/
+
+#define TRAVERSE844W(pTable, wch) \
+ ( (pTable)[(pTable)[(pTable)[GET8((wch))] + GETHI4((wch))] + GETLO4((wch))] )
+
+//
+// NLS_UPCASE - Based on julieb's macros in nls.h
+//
+// We will have this upcase macro quickly shortcircuit out if the value
+// is within the normal ANSI range (i.e., < 127). We actually won't bother
+// with the 5 values above 'z' because they won't happen very often and
+// coding it this way lets us get out after 1 compare for value less than
+// 'a' and 2 compares for lowercase a-z.
+//
+
+#define NLS_UPCASE(wch) ( \
+ ((wch) < 'a' ? \
+ (wch) \
+ : \
+ ((wch) <= 'z' ? \
+ (wch) - ('a'-'A') \
+ : \
+ ((WCHAR)((wch) + TRAVERSE844W(Nls844UnicodeUpcaseTable,(wch)))) \
+ ) \
+ ) \
+)
+
+#define NLS_DOWNCASE(wch) ( \
+ ((wch) < 'A' ? \
+ (wch) \
+ : \
+ ((wch) <= 'Z' ? \
+ (wch) + ('a'-'A') \
+ : \
+ ((WCHAR)((wch) + TRAVERSE844W(Nls844UnicodeLowercaseTable,(wch)))) \
+ ) \
+ ) \
+)
+
+#if DBG && defined(NTOS_KERNEL_RUNTIME)
+#define RTL_PAGED_CODE() PAGED_CODE()
+#else
+#define RTL_PAGED_CODE()
+#endif
+
+
+//
+// The follow definition is used to support the Rtl compression engine
+// Every compression format that NT supports will need to supply
+// these set of routines in order to be called by NtRtl.
+//
+
+typedef NTSTATUS (*PRTL_COMPRESS_WORKSPACE_SIZE) (
+ IN USHORT CompressionEngine,
+ OUT PULONG CompressBufferWorkSpaceSize,
+ OUT PULONG CompressFragmentWorkSpaceSize
+ );
+
+typedef NTSTATUS (*PRTL_COMPRESS_BUFFER) (
+ IN USHORT CompressionEngine,
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG UncompressedChunkSize,
+ OUT PULONG FinalCompressedSize,
+ IN PVOID WorkSpace
+ );
+
+typedef NTSTATUS (*PRTL_DECOMPRESS_BUFFER) (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ );
+
+typedef NTSTATUS (*PRTL_DECOMPRESS_FRAGMENT) (
+ OUT PUCHAR UncompressedFragment,
+ IN ULONG UncompressedFragmentSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG FragmentOffset,
+ OUT PULONG FinalUncompressedSize,
+ IN PVOID WorkSpace
+ );
+
+typedef NTSTATUS (*PRTL_DESCRIBE_CHUNK) (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ OUT PULONG ChunkSize
+ );
+
+typedef NTSTATUS (*PRTL_RESERVE_CHUNK) (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ IN ULONG ChunkSize
+ );
+
+//
+// Here is the declarations of the LZNT1 routines
+//
+
+NTSTATUS
+RtlCompressWorkSpaceSizeLZNT1 (
+ IN USHORT CompressionEngine,
+ OUT PULONG CompressBufferWorkSpaceSize,
+ OUT PULONG CompressFragmentWorkSpaceSize
+ );
+
+NTSTATUS
+RtlCompressBufferLZNT1 (
+ IN USHORT CompressionEngine,
+ IN PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ OUT PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG UncompressedChunkSize,
+ OUT PULONG FinalCompressedSize,
+ IN PVOID WorkSpace
+ );
+
+NTSTATUS
+RtlDecompressBufferLZNT1 (
+ OUT PUCHAR UncompressedBuffer,
+ IN ULONG UncompressedBufferSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ OUT PULONG FinalUncompressedSize
+ );
+
+NTSTATUS
+RtlDecompressFragmentLZNT1 (
+ OUT PUCHAR UncompressedFragment,
+ IN ULONG UncompressedFragmentSize,
+ IN PUCHAR CompressedBuffer,
+ IN ULONG CompressedBufferSize,
+ IN ULONG FragmentOffset,
+ OUT PULONG FinalUncompressedSize,
+ IN PVOID WorkSpace
+ );
+
+NTSTATUS
+RtlDescribeChunkLZNT1 (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ OUT PULONG ChunkSize
+ );
+
+NTSTATUS
+RtlReserveChunkLZNT1 (
+ IN OUT PUCHAR *CompressedBuffer,
+ IN PUCHAR EndOfCompressedBufferPlus1,
+ OUT PUCHAR *ChunkBuffer,
+ IN ULONG ChunkSize
+ );
+
+//
+// Define procedure prototypes for architecture specific debug support routines.
+//
+
+NTSTATUS
+DebugPrint(
+ IN PSTRING Output
+ );
+
+ULONG
+DebugPrompt(
+ IN PSTRING Output,
+ IN PSTRING Input
+ );
+
+#if defined(NTOS_KERNEL_RUNTIME)
+
+VOID
+DebugLoadImageSymbols(
+ IN PSTRING FileName,
+ IN PKD_SYMBOLS_INFO SymbolInfo
+ );
+
+VOID
+DebugUnLoadImageSymbols(
+ IN PSTRING FileName,
+ IN PKD_SYMBOLS_INFO SymbolInfo
+ );
+
+#endif // defined(NTOS_KERNEL_RUNTIME)
+
+#endif // _NTRTLP_
+
+//
+// Procedure prototype for exception logging routines.
+
+ULONG
+RtlpLogExceptionHandler(
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord,
+ IN ULONG ControlPc,
+ IN PVOID HandlerData,
+ IN ULONG Size
+ );
+
+VOID
+RtlpLogLastExceptionDisposition(
+ IN ULONG LogIndex,
+ IN EXCEPTION_DISPOSITION Disposition
+ );
diff --git a/private/ntos/rtl/pctohdr.c b/private/ntos/rtl/pctohdr.c
new file mode 100644
index 000000000..4e4b30867
--- /dev/null
+++ b/private/ntos/rtl/pctohdr.c
@@ -0,0 +1,245 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ pctohdr.c
+
+Abstract:
+
+ This module implements code to locate the file header for an image or
+ dll given a PC value that lies within the image.
+
+ N.B. This routine is conditionalized for user mode and kernel mode.
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Environment:
+
+ User Mode or Kernel Mode
+
+Revision History:
+
+--*/
+
+#if defined(NTOS_KERNEL_RUNTIME)
+#include "ntos.h"
+#else
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#endif
+
+#if DBG && !defined(NTOS_KERNEL_RUNTIME)
+extern PVOID NtDllBase; // defined in ntos\dll\ldrinit.c
+#endif
+
+
+PVOID
+RtlPcToFileHeader(
+ IN PVOID PcValue,
+ OUT PVOID *BaseOfImage
+ )
+
+/*++
+
+Routine Description:
+
+ This function returns the base of an image that contains the
+ specified PcValue. An image contains the PcValue if the PcValue
+ is within the ImageBase, and the ImageBase plus the size of the
+ virtual image.
+
+Arguments:
+
+ PcValue - Supplies a PcValue. All of the modules mapped into the
+ calling processes address space are scanned to compute which
+ module contains the PcValue.
+
+ BaseOfImage - Returns the base address for the image containing the
+ PcValue. This value must be added to any relative addresses in
+ the headers to locate portions of the image.
+
+Return Value:
+
+ NULL - No image was found that contains the PcValue.
+
+ NON-NULL - Returns the base address of the image that contain the
+ PcValue.
+
+--*/
+
+{
+
+#if defined(NTOS_KERNEL_RUNTIME)
+
+ extern LIST_ENTRY PsLoadedModuleList;
+ extern KSPIN_LOCK PsLoadedModuleSpinLock;
+
+ PVOID Base;
+ ULONG Bounds;
+ PLDR_DATA_TABLE_ENTRY Entry;
+ PLIST_ENTRY Next;
+ KIRQL OldIrql;
+
+ //
+ // Acquire the loaded module list spinlock and scan the list for the
+ // specified PC value if the list has been initialized.
+ //
+
+ ExAcquireSpinLock(&PsLoadedModuleSpinLock, &OldIrql);
+ Next = PsLoadedModuleList.Flink;
+ if (Next != NULL) {
+ while (Next != &PsLoadedModuleList) {
+ Entry = CONTAINING_RECORD(Next,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ Next = Next->Flink;
+ Base = Entry->DllBase;
+ Bounds = (ULONG)Base + Entry->SizeOfImage;
+ if (((ULONG)PcValue >= (ULONG)Base) && ((ULONG)PcValue < Bounds)) {
+ ExReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
+ *BaseOfImage = Base;
+ return Base;
+ }
+ }
+ }
+
+ //
+ // Release the loaded module list spin lock and return NULL.
+ //
+
+ ExReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
+ *BaseOfImage = NULL;
+ return NULL;
+
+#else
+
+ PVOID Base;
+ ULONG Bounds;
+ PLDR_DATA_TABLE_ENTRY Entry;
+ PLIST_ENTRY ModuleListHead;
+ PLIST_ENTRY Next;
+ PIMAGE_NT_HEADERS NtHeaders;
+ PPEB Peb;
+ PTEB Teb;
+ MEMORY_BASIC_INFORMATION MemInfo;
+ NTSTATUS st;
+
+ //
+ // Acquire the Loader lock for the current process and scan the loaded
+ // module list for the specified PC value if all the data structures
+ // have been initialized.
+ //
+
+ if ( !RtlTryEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock) ) {
+
+ //
+ // We could not get the loader lock, so call the system to find the image that
+ // contains this pc
+ //
+
+ st = NtQueryVirtualMemory(
+ NtCurrentProcess(),
+ PcValue,
+ MemoryBasicInformation,
+ (PVOID)&MemInfo,
+ sizeof(MemInfo),
+ NULL
+ );
+ if ( !NT_SUCCESS(st) ) {
+ MemInfo.AllocationBase = NULL;;
+ }
+ else {
+ if ( MemInfo.Type == MEM_IMAGE ) {
+ try {
+ *BaseOfImage = MemInfo.AllocationBase;
+ }
+ except (EXCEPTION_EXECUTE_HANDLER) {
+ MemInfo.AllocationBase = NULL;
+ }
+ }
+ else {
+ MemInfo.AllocationBase = NULL;;
+ }
+ }
+ return MemInfo.AllocationBase;
+ }
+
+ try {
+ Teb = NtCurrentTeb();
+ if (Teb != NULL) {
+ Peb = Teb->ProcessEnvironmentBlock;
+ if (Peb->Ldr != NULL) {
+ ModuleListHead = &Peb->Ldr->InLoadOrderModuleList;
+ Next = ModuleListHead->Flink;
+ if (Next != NULL) {
+ while (Next != ModuleListHead) {
+ Entry = CONTAINING_RECORD(Next,
+ LDR_DATA_TABLE_ENTRY,
+ InLoadOrderLinks);
+
+ Next = Next->Flink;
+ Base = Entry->DllBase;
+ NtHeaders = RtlImageNtHeader(Base);
+ Bounds = (ULONG)Base + NtHeaders->OptionalHeader.SizeOfImage;
+ if (((ULONG)PcValue >= (ULONG)Base) && ((ULONG)PcValue < Bounds)) {
+ RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
+ *BaseOfImage = Base;
+ return Base;
+ }
+ }
+ }
+
+#if DBG
+
+ } else {
+
+ //
+ // ( Peb->Ldr == NULL )
+ //
+ // If called during process intialization before the Ldr
+ // module list has been setup, code executing must be in
+ // NTDLL module. If NtDllBase is non-NULL and the PcValue
+ // falls into the NTDLL range, return a valid Base. This
+ // allows DbgPrint's during LdrpInitializeProcess to work
+ // on RISC machines. Since we really only need DbgPrints
+ // on DBG builds, only defined this code for DBG to keep
+ // retail code smaller.
+ //
+
+ if ( NtDllBase != NULL ) {
+ Base = NtDllBase;
+ NtHeaders = RtlImageNtHeader( Base );
+ Bounds = (ULONG)Base + NtHeaders->OptionalHeader.SizeOfImage;
+ if (((ULONG)PcValue >= (ULONG)Base) && ((ULONG)PcValue < Bounds)) {
+ RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
+ *BaseOfImage = Base;
+ return Base;
+ }
+ }
+
+#endif // DBG
+
+ }
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ NOTHING;
+ }
+
+ //
+ // Release the Loader lock for the current process a return NULL.
+ //
+
+ RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
+ *BaseOfImage = NULL;
+ return NULL;
+
+#endif
+
+}
diff --git a/private/ntos/rtl/ppc/chandler.c b/private/ntos/rtl/ppc/chandler.c
new file mode 100644
index 000000000..56fafe34d
--- /dev/null
+++ b/private/ntos/rtl/ppc/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->Iar;
+ 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/ppc/chkstk.s b/private/ntos/rtl/ppc/chkstk.s
new file mode 100644
index 000000000..67910e07c
--- /dev/null
+++ b/private/ntos/rtl/ppc/chkstk.s
@@ -0,0 +1,202 @@
+// TITLE("C-Runtime Stack Checking")
+//++
+//
+// Copyright (c) 1993,1994 IBM Corporation
+//
+// Module Name:
+//
+// chkstk.s
+//
+// Abstract:
+//
+// This module implements runtime stack checking.
+//
+// Author:
+//
+// Mark D. Johnson
+//
+// Environment:
+//
+// User/Kernel mode.
+//
+// Revision History:
+//
+//--
+
+#include <ksppc.h>
+
+
+ SBTTL("Check Stack")
+//++
+//
+// VOID _RtlCheckStack(in ULONG n_alloc)
+//
+// 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.
+//
+// Two entry points are supported. The first/standard entry point
+// calls for n_alloc to be passed as single argument (in r.3). In
+// the second case, the single argument is the negative of the amount
+// requested (the amount by which to decrement the stack pointer), and
+// is passed in r.12.
+//
+// Registers r.0 and r.11 are modified.
+//
+// Arguments:
+//
+// n_alloc(r.3/r.12) - Supplies the size of the allocation on the stack.
+// With standard entry, passed in r.3. In alternate
+// entry, passed in r.12. In the latter case (r.12)
+// the value supplied is the quanitity by which to
+// decrement r.sp (a negative value).
+//
+// Return Value:
+//
+// None.
+//
+// Assumptions:
+//
+// The value of Teb_Ptr is provided in r.13.
+//
+// The bottom of stack "bot" (r.11) is multiple of PAGE_SIZE.
+//
+
+//
+// low
+// :
+// | :
+// | |
+// | |
+// |.......|<--- r.sp + r.12 - (PAGE_SIZE-1)
+// | ^ |
+// | |< -|- - - - - - always < PAGE_SIZE
+// | v |
+// +-------+<--- bot - m*PAGE_SIZE
+// | |
+// | : |
+// | |
+// +-------+<--- r.sp + r.12
+// | |
+// | |
+// | |
+// | : |
+// | : |
+// | : |
+// | : |
+// | |
+// | |
+// | |
+// | |
+// +=======+<--- bottom of stack "bot" (r.11)
+// | |
+// | |
+// | |
+// | : |
+// | : |
+// | : |
+// | |
+// | |
+// +-------+<--- r.sp
+// | |
+// | |
+// | : |
+// | :
+// | : high
+// : m is count for add'l pages to commit
+//
+//
+// m:=max(0,{bot-[(r.sp+r.12)-(PAGE_SIZE-1)]}/PAGE_SIZE)
+// =max(0,bot-[r.12-(PAGE_SIZE-1)+r.sp])/PAGE_SIZE
+//
+// Operands in expression [r.12-(PAGE_SIZE-1)+r.sp) are known upon entry
+// to routine. This intermediate quantity is calculated early in r.0.
+
+
+ .text
+ .align 5
+
+ // want entry for _RtlCheckStack aligned on a 2**5-1 byte boundary so that
+ // more commonly used (internal) entry _RtlCheckStack.12 is aligned on
+ // 2**5 byte (cache-line) boundary ... and most often used code fits in
+ // single cache-line
+
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+
+ or r.11, r.11, r.11; or r.11, r.11, r.11
+ or r.11, r.11, r.11
+
+ LEAF_ENTRY(_RtlCheckStack)
+
+ neg r.12,r.3
+
+ ALTERNATE_ENTRY(_RtlCheckStack.12)
+
+ // r.13 contains the TEB pointer
+
+ lwz r.11,TeStackLimit(r.13) // Get low stack address
+
+ // r.11 contains "bot" of stack
+
+ subi r.0,r.12,(PAGE_SIZE-1)
+ add r.0,r.0,r.sp
+
+ // r.0 is desired "bot"
+
+ sub r.0,r.11,r.0
+ srawi. r.0,r.0,PAGE_SHIFT // Number pages to add/commit
+ blelr // if <=0, return
+
+ mtctr r.0
+
+PageLoop:
+
+ // Attempt to commit pages beyond the limit
+
+ lwzu r.0,-PAGE_SIZE(r.11)
+ bdnz PageLoop
+
+
+ LEAF_EXIT(_RtlCheckStack)
+
+
+
+ .debug$S
+ .ualong 1
+
+ .uashort 52
+ .uashort 0x205 // S_GPROC32
+ .ualong 0
+ .ualong 0
+ .ualong 0
+ .ualong _RtlCheckStack.end-.._RtlCheckStack
+ .ualong 0
+ .ualong _RtlCheckStack.end-.._RtlCheckStack
+ .ualong [secoff].._RtlCheckStack
+ .uashort [secnum].._RtlCheckStack
+ .uashort 0x1000
+ .byte 0x00
+ .byte 16, ".._RtlCheckStack"
+
+ .uashort 29
+ .uashort 0x209 // S_LABEL32
+ .ualong [secoff].._RtlCheckStack.12
+ .uashort [secnum].._RtlCheckStack.12
+ .byte 0
+ .byte 19, ".._RtlCheckStack.12"
+
+ .uashort 2, 0x6 // S_END
diff --git a/private/ntos/rtl/ppc/context.c b/private/ntos/rtl/ppc/context.c
new file mode 100644
index 000000000..022a0e688
--- /dev/null
+++ b/private/ntos/rtl/ppc/context.c
@@ -0,0 +1,240 @@
+/*++
+
+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
+ Rick Simpson, Peter Johnston Conversion to PowerPC 11/5/93
+
+ Revise for MIPS environment.
+
+--*/
+
+#include <ntos.h>
+#define _KXPPC_C_HEADER_
+#include <kxppc.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 and floating registers to contain zeroes.
+ //
+
+ RtlZeroMemory(Context, sizeof(CONTEXT));
+
+ //
+ // Initialize the control registers.
+ //
+
+ if ( ARGUMENT_PRESENT(InitialPc) ) {
+ Context->Iar = (ULONG)InitialPc;
+ }
+ if ( ARGUMENT_PRESENT(InitialSp) ) {
+ Context->Gpr1 = (ULONG)InitialSp - STK_MIN_FRAME;
+ }
+
+ Context->Msr =
+ MASK_SPR(MSR_ILE,1)|
+ MASK_SPR(MSR_FP,1) |
+ MASK_SPR(MSR_FE0,1)|
+ MASK_SPR(MSR_FE1,1)|
+ MASK_SPR(MSR_ME,1) |
+ MASK_SPR(MSR_IR,1) |
+ MASK_SPR(MSR_DR,1) |
+ MASK_SPR(MSR_PR,1) |
+ MASK_SPR(MSR_LE,1);
+ Context->ContextFlags = CONTEXT_FULL;
+
+ //
+ // Set the initial context of the thread in a machine specific way.
+ //
+
+ Context->Gpr3 = (ULONG)Parameter;
+}
+
+NTSTATUS
+RtlRemoteCall(
+ HANDLE Process,
+ HANDLE Thread,
+ PVOID CallSite,
+ ULONG ArgumentCount,
+ PULONG Arguments,
+ BOOLEAN PassContext,
+ BOOLEAN AlreadySuspended
+ )
+
+/*++
+
+Routine Description:
+
+ This function calls a procedure in another thread/process, by using
+ NtGetContext and NtSetContext. Parameters are passed to the target
+ procedure via the nonvolatile registers (s0 - 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 current state of the target thread.
+ //
+
+ Context.ContextFlags = CONTEXT_FULL;
+ Status = NtGetContextThread(Thread, &Context);
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+ }
+
+ if (AlreadySuspended ) {
+
+ Context.Gpr3 = 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 = Context.Gpr1 - sizeof(CONTEXT) - STK_MIN_FRAME;
+ Status = NtWriteVirtualMemory(Process, (PVOID)(NewSp + STK_MIN_FRAME), &Context,
+ sizeof(CONTEXT), NULL);
+ Status = NtWriteVirtualMemory(Process, (PVOID)NewSp, &Context.Gpr1,
+ sizeof(ULONG), NULL);
+ if (NT_SUCCESS(Status) == FALSE) {
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+ }
+
+ Context.Gpr1 = NewSp;
+
+ if (PassContext) {
+ Context.Gpr14 = NewSp + STK_MIN_FRAME;
+ RtlMoveMemory(&Context.Gpr15, Arguments, ArgumentCount * sizeof(ULONG));
+
+ } else {
+
+ RtlMoveMemory(&Context.Gpr14, 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.Iar = (ULONG)CallSite; // Set context will dereference the
+ Context.Gpr2 = 0; // FNDESC in the remote threads context
+ Status = NtSetContextThread(Thread, &Context);
+ if (AlreadySuspended == FALSE) {
+ NtResumeThread(Thread, NULL);
+ }
+ return(Status);
+}
diff --git a/private/ntos/rtl/ppc/debugstb.s b/private/ntos/rtl/ppc/debugstb.s
new file mode 100644
index 000000000..eb5ff5bac
--- /dev/null
+++ b/private/ntos/rtl/ppc/debugstb.s
@@ -0,0 +1,266 @@
+// TITLE("Debug Support Functions")
+//++
+//
+// Copyright (c) 1993 IBM 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:
+//
+// Chuck Bauman 12-Aug-1993
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Initial PowerPC port of NT product1 source 12-Aug-1993
+//
+//--
+
+#include "ksppc.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)
+
+ twi 31,0,DEBUG_STOP_BREAKPOINT
+
+ LEAF_EXIT(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)
+ twi 31,0,DEBUG_STOP_BREAKPOINT
+
+ LEAF_EXIT(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)
+
+ twi 31,0,DEBUG_STOP_BREAKPOINT
+
+ LEAF_EXIT(DbgUserBreakPoint)
+
+//++
+//
+// ULONG
+// DebugPrompt(
+// IN PSTRING Output,
+// IN PSTRING Input
+// )
+//
+// Routine Description:
+//
+// This function executes a debug prompt breakpoint.
+//
+// Arguments:
+//
+// Output (r.3) - Supplies a pointer to the output string descriptor.
+//
+// Input (r.4) - 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)
+
+ lhz r.6,StrMaximumLength(r.4) // set maximum length of input string
+ lwz r.5,StrBuffer(r.4) // set address of input string
+ lhz r.4,StrLength(r.3) // set length of output string
+ lwz r.3,StrBuffer(r.3) // set address of output string
+ twi 31,0,DEBUG_PROMPT_BREAKPOINT // execute a debug prompt breakpoint
+
+ LEAF_EXIT(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)
+
+ twi 31,0,DEBUG_LOAD_SYMBOLS_BREAKPOINT
+
+ LEAF_EXIT(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)
+
+ twi 31,0,DEBUG_UNLOAD_SYMBOLS_BREAKPOINT
+
+ LEAF_EXIT(DebugUnLoadImageSymbols)
+
+#endif
+
+//++
+//
+// NTSTATUS
+// DebugPrint(
+// IN PSTRING Output
+// )
+//
+// Routine Description:
+//
+// This function executes a debug print breakpoint.
+//
+// Arguments:
+//
+// Output (r.3) - 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)
+
+ lhz r.4,StrLength(r.3) // set length of output string
+ lwz r.3,StrBuffer(r.3) // set address of output string
+ twi 31,0,DEBUG_PRINT_BREAKPOINT // execute a debug print breakpoint
+
+ LEAF_EXIT(DebugPrint)
+
+#endif
diff --git a/private/ntos/rtl/ppc/exdsptch.c b/private/ntos/rtl/ppc/exdsptch.c
new file mode 100644
index 000000000..72b64165d
--- /dev/null
+++ b/private/ntos/rtl/ppc/exdsptch.c
@@ -0,0 +1,1196 @@
+/*++
+
+Copyright (c) 1993 IBM Corporation and Microsoft Corporation
+
+Module Name:
+
+ exdsptch.c
+
+Abstract:
+
+ This module implements the dispatching of exception and the unwinding of
+ procedure call frames for PowerPC.
+
+Author:
+
+ Rick Simpson 16-Aug-1993
+
+ based on MIPS version by David N. Cutler (davec) 11-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Tom Wood (twood) 19-Aug-1994
+ Update to use RtlVirtualUnwind even when there isn't a function table
+ entry. Add stack limit parameters to RtlVirtualUnwind.
+
+ Changed RtlLookupFunctionEntry to deal with the indirect entries.
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Define local macros.
+//
+
+#ifndef ROS_DEBUG
+#ifndef READ_ULONG
+#define READ_ULONG(addr,dest) dest = (*((PULONG)(addr)))
+#define READ_DOUBLE(addr,dest) dest = (*((PDOUBLE)(addr)))
+#endif
+
+#include "vunwind.c"
+#endif // ROS_DEBUG
+
+//
+// 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 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,
+ IN ULONG LowStackLimit,
+ IN ULONG HighStackLimit
+ );
+
+BOOLEAN
+RtlDispatchException (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function attempts to dispatch an exception to a frame based
+ handler by searching backwards through the stack based call frames.
+ The search begins with the frame specified in the context record and
+ continues backward until either a handler is found that handles the
+ exception, the stack is found to be invalid (i.e., out of limits or
+ unaligned), or the end of the call hierarchy is reached.
+
+ As each frame is encountered, the PC where control left the corresponding
+ function is determined and used to lookup exception handler information
+ in the runtime function table built by the linker. If the respective
+ routine has an exception handler, then the handler is called. If the
+ handler does not handle the exception, then the prologue of the routine
+ is executed backwards to "unwind" the effect of the prologue and then
+ the next frame is examined.
+
+Arguments:
+
+ ExceptionRecord - Supplies a pointer to an exception record.
+
+ ContextRecord - Supplies a pointer to a context record.
+
+Return Value:
+
+ If the exception is handled by one of the frame based handlers, then
+ a value of TRUE is returned. Otherwise a value of FALSE is returned.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord1;
+ ULONG ControlPc;
+ DISPATCHER_CONTEXT DispatcherContext;
+ EXCEPTION_DISPOSITION Disposition;
+ ULONG EstablisherFrame;
+ ULONG ExceptionFlags;
+ PRUNTIME_FUNCTION FunctionEntry;
+ 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.Iar;
+ 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);
+
+ //
+ // Virtually unwind to the caller of the current routine to obtain
+ // the virtual frame pointer of the establisher and the next PC.
+ //
+
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord1,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ LowLimit,
+ HighLimit);
+
+ //
+ // If there is a function table entry for the routine, check if
+ // there is an exception handler for the frame.
+ //
+
+ if (FunctionEntry != 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) {
+ ULONG Index;
+
+ //
+ // 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 (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 {
+
+ //
+ // 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 (ContextRecord1.Gpr1 < 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;
+
+ //
+ // 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 (within reason) discontiguous code segment(s). If
+ // PrologEndAddress is out of range, it is re-interpreted
+ // as a pointer to the primary function table entry for
+ // that function. The out of range test takes into account
+ // the redundant encoding of millicode and glue code.
+ //
+
+ if (((FunctionEntry->PrologEndAddress < FunctionEntry->BeginAddress) ||
+ (FunctionEntry->PrologEndAddress > FunctionEntry->EndAddress)) &&
+ (FunctionEntry->PrologEndAddress & 3) == 0) {
+ 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 = ContextRecord.Lr - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ ContextRecord.Iar = NextPc + 4;
+ ExceptionRecord->ExceptionAddress = (PVOID)ContextRecord.Iar;
+ 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 = ContextRecord.Lr - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ ContextRecord.Iar = NextPc + 4;
+ ExceptionRecord.ExceptionAddress = (PVOID)ContextRecord.Iar;
+ 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 = ContextRecord->Lr - 4;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ ControlPc = NextPc;
+ ContextRecord->Iar = (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,
+ LowLimit,
+ HighLimit);
+
+ //
+ // 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->Gpr3 = (ULONG)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,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+ }
+
+ 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->Iar = (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,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+ }
+ }
+
+ } else {
+
+ //
+ // Set point at which control left the previous routine.
+ //
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ //
+ // 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) {
+ //
+ // Virtually unwind the target frame to recover the value of r.2.
+ // We must take care to not unwind a glue sequence that may have
+ // been used to reach the target frame. This is done by giving
+ // stack limit values that will regard any stack pointer as bad.
+ //
+ CONTEXT TocContext;
+ RtlMoveMemory((PVOID)&TocContext, ContextRecord, sizeof(CONTEXT));
+ ControlPc = ContextRecord->Iar;
+ FunctionEntry = RtlLookupFunctionEntry(ControlPc);
+ NextPc = RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &TocContext,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0xffffffff,
+ 0);
+ ContextRecord->Gpr2 = TocContext.Gpr2;
+ ContextRecord->Gpr3 = (ULONG)ReturnValue;
+ RtlpRestoreContext(ContextRecord, ExceptionRecord);
+
+ } else {
+ ZwRaiseException(ExceptionRecord, ContextRecord, FALSE);
+ }
+}
+
+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,
+ IN ULONG LowStackLimit,
+ IN ULONG HighStackLimit
+ )
+
+/*++
+
+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.
+
+ If the function is register save millicode, it is treated as a leaf
+ function. If the function is register restore millicode, the remaining
+ body is executed forwards and the address where control left the
+ previous frame is obtained from the final blr instruction.
+
+ If the function was called via glue code and is not that glue code,
+ the prologe of the glue code is executed backwards in addition to the
+ above actions.
+
+ 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 or NULL if the function is a leaf 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.
+
+ LowStackLimit, HighStackLimit - Range of valid values for the stack
+ pointer. This indicates whether it is valid to examine NextPc.
+
+Return Value:
+
+ The address where control left the previous frame is returned as the
+ function value.
+
+--*/
+
+{
+
+ CONTEXT LocalContext;
+
+ //
+ // Copy the context record so updates will not be reflected in the
+ // original copy and then virtually unwind to the caller of the
+ // specified control point.
+ //
+
+ RtlMoveMemory((PVOID)&LocalContext, ContextRecord, sizeof(CONTEXT));
+ return RtlVirtualUnwind(ControlPc,
+ FunctionEntry,
+ &LocalContext,
+ InFunction,
+ EstablisherFrame,
+ ContextPointers,
+ LowStackLimit,
+ HighStackLimit);
+}
diff --git a/private/ntos/rtl/ppc/getcalr.c b/private/ntos/rtl/ppc/getcalr.c
new file mode 100644
index 000000000..018660bb0
--- /dev/null
+++ b/private/ntos/rtl/ppc/getcalr.c
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ getcalr.c
+
+Abstract:
+
+ This module implements the routine RtlGetCallerAddress. It will
+ return the address of the caller, and the callers caller to the
+ specified procedure.
+
+Author:
+
+ Larry Osterman (larryo) 18-Mar-1991 (with help from DaveC)
+
+Revision History:
+
+ 18-Mar-1991 larryo
+
+ Created
+
+ Tom Wood 23-Aug-1994
+ Add stack limit parameters to RtlVirtualUnwind.
+
+--*/
+#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 = ContextRecord.Lr;
+
+ //
+ // 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,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ //
+ // Attempt to unwind to the caller of the caller of this routine (B).
+ //
+
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ if ((FunctionEntry != NULL) && (ContextRecord.Gpr1 < HighLimit)) {
+
+ //
+ // A function table entry was found for the caller of the caller
+ // of this routine (B). Virtually unwind to the caller of the
+ // caller of this routine (B).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ *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) && (ContextRecord.Gpr1 < HighLimit)) {
+
+ //
+ // A function table entry was found for the caller of the
+ // caller of the caller of this routine (A). Virtually unwind
+ // to the caller of the caller of the caller of this routine
+ // (A).
+ //
+
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+
+ *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/ppc/jumps.c b/private/ntos/rtl/ppc/jumps.c
new file mode 100644
index 000000000..761354318
--- /dev/null
+++ b/private/ntos/rtl/ppc/jumps.c
@@ -0,0 +1,153 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ jumps.c
+
+Abstract:
+
+ This module implements the C runtime library functions for set jump and
+ long jump that are compatible with structured exception handling.
+
+Author:
+
+ David N. Cutler (davec) 15-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Tom Wood 23-Aug-1994
+ Add stack limit parameters to RtlVirtualUnwind.
+
+--*/
+
+#include "ntrtlp.h"
+#include "setjmp.h"
+
+VOID
+longjmp (
+ IN jmp_buf JumpBuffer,
+ IN int ReturnValue
+ )
+
+/*++
+
+Routine Description:
+
+ This function executes a long jump operation by virtually unwinding to
+ the caller of the corresponding call to set jump and then calling unwind
+ to transfer control to the jump target.
+
+Arguments:
+
+
+ JumpBuffer - Supplies the address of a jump buffer that contains the
+ virtual frame pointer and target address.
+
+ N.B. This is an array of double to force quadword alignment.
+
+ ReturnValue - Supplies the value that is to be returned to the caller
+ of set jump.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PULONG JumpArray;
+
+ //
+ // If the specified return value is zero, then set it to one.
+ //
+
+ if (ReturnValue == 0) {
+ ReturnValue = 1;
+ }
+
+ //
+ // Unwind to the caller of set jump and return the specified value.
+ // There is no return from unwind.
+ //
+
+ JumpArray = (PULONG)&JumpBuffer[0];
+ RtlUnwind((PVOID)JumpArray[0],
+ (PVOID)JumpArray[1],
+ NULL,
+ (PVOID)ReturnValue);
+}
+
+int
+setjmp (
+ IN jmp_buf JumpBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs a set jump operation by capturing the current
+ context, virtualy unwinding to the caller of set jump, and returns zero
+ to the caller.
+
+Arguments:
+
+ JumpBuffer - Supplies the address of a jump buffer to store the virtual
+ frame pointer and target address of the caller.
+
+ N.B. This is an array of double to force quadword alignment.
+
+Return Value:
+
+ A value of zero is returned.
+
+--*/
+
+{
+
+ CONTEXT ContextRecord;
+ ULONG EstablisherFrame;
+ PRUNTIME_FUNCTION FunctionEntry;
+ BOOLEAN InFunction;
+ PULONG JumpArray;
+ ULONG NextPc;
+
+ //
+ // Capture the current context, virtually unwind to the caller of set
+ // jump, and return zero to the caller.
+ //
+
+ JumpArray = (PULONG)&JumpBuffer[0];
+ RtlCaptureContext(&ContextRecord);
+ NextPc = ContextRecord.Lr - 4;
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ JumpArray[1] = NextPc + 4;
+ FunctionEntry = RtlLookupFunctionEntry(NextPc);
+ NextPc = RtlVirtualUnwind(NextPc,
+ FunctionEntry,
+ &ContextRecord,
+ &InFunction,
+ &EstablisherFrame,
+ NULL,
+ 0,
+ 0xffffffff);
+
+ JumpArray[0] = EstablisherFrame;
+ return 0;
+}
diff --git a/private/ntos/rtl/ppc/largeint.s b/private/ntos/rtl/ppc/largeint.s
new file mode 100644
index 000000000..c59584fcf
--- /dev/null
+++ b/private/ntos/rtl/ppc/largeint.s
@@ -0,0 +1,1089 @@
+// TITLE("Large Integer Arithmetic")
+//++
+//
+// Copyright (c) 1993 IBM Corporation
+//
+// Module Name:
+//
+// largeint.s
+//
+// Abstract:
+//
+// This module implements routines for performing extended integer
+// arithmtic.
+//
+// Author:
+//
+// David N. Cutler (davec) 18-Apr-1990
+// Converted to PowerPC by Walt Daniels and Norman Cohen Aug 93
+// (from MIPS based code)
+//
+// References:
+// See PowerPC Architecture book Appendix E.2 for 64-bit shifts
+// See "Hacker's Delight", Hank Warren, Nov. 91 for fancy divides
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+// Fixed RtlExtendedLargeIntegerDivide (Steve Johns) 18-Feb-94
+// - if divisor >= 2^16 && dividend >= 2^32, quotient incorrect
+// - also, removed 6 uncessary occurrences of CMPI
+//
+//--
+
+#include "ksppc.h"
+
+
+
+//++
+//
+// 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 (r.5, r.6) - Supplies the first addend value.
+//
+// Addend2 (r.7, r.8) - Supplies the second addend value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerAdd)
+
+ addc r.5,r.5,r.7 // add low parts of large integer
+ adde r.6,r.6,r.8 // add high parts with carry
+ stw r.5,0(r.3) // store low 32-bits
+ stw r.6,4(r.3) // store high 32-bits
+ LEAF_EXIT(RtlLargeIntegerAdd) // return
+
+
+
+//++
+//
+// 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 (r.4) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertLongToLargeInteger)
+
+ srawi r.5,r.4,31 // compute high part of result
+ stw r.4,0(r.3) // store low 32-bits
+ stw r.5,4(r.3) // store high 32-bits
+ LEAF_EXIT(RtlConvertLongToLargeInteger) // return
+
+
+
+//++
+//
+// 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 (r.4) - Supplies the value to convert.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlConvertUlongToLargeInteger)
+
+ li r.5,0 // clear high part
+ stw r.4,0(r.3) // store low 32-bits
+ stw r.5,4(r.3) // store high 32-bits
+ LEAF_EXIT(RtlConvertUlongToLargeInteger) // return
+
+
+
+//++
+//
+// 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 (r.4) - Supplies the multiplicand value.
+//
+// Multiplier (r.5) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedIntegerMultiply)
+
+ mullw r.6,r.4,r.5 // keep low 32-bits of result
+ mulhw r.7,r.4,r.5 // keep high 32-bits of result
+ stw r.6,0(r.3) // store low 32-bits
+ stw r.7,4(r.3) // store high 32-bits
+ LEAF_EXIT(RtlEnlargedIntegerMultiply) // return
+
+
+
+//++
+//
+// 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 (r.4) - Supplies the multiplicand value.
+//
+// Multiplier (r.5) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedMultiply)
+
+ mullw r.6,r.4,r.5 // keep low-32-bits
+ mulhwu r.7,r.4,r.5 // keep high 32-bits
+ stw r.6,0(r.3) // store low 32-bits
+ stw r.7,4(r.3) // store high 32-bits
+ LEAF_EXIT(RtlEnlargedUnsignedMultiply) // return
+
+
+
+//++
+//
+// 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 (r.3, r.4) - Supplies the dividend value.
+// (High-order bits in r.4, low-order bits in r.3)
+//
+// Divisor (r.5) - Supplies the divisor value.
+//
+// Remainder (r.6) - Supplies an optional pointer to a variable that
+// receives the remainder. Ptr is null if not needed.
+//
+// Return Value:
+//
+// The unsigned long integer quotient is returned as the function value.
+//
+//--
+
+ LEAF_ENTRY(RtlEnlargedUnsignedDivide)
+
+ cmplw r.4,r.5
+ bge overflow // catch overflow or division by 0
+ cmplwi r.4,0 // test high part for 0
+ beq only_32_bits // 32-bit division suffices
+// Normalize: Shift divisor and dividend left to get rid of leading zeroes
+// in the divisor. Since r.4 < r.5, only zeroes are shifted out of the
+// dividend.
+ cntlzw r.7,r.5 // number of bits to shift (N)
+ slw r.5,r.5,r.7 // shift divisor
+ slw r.4,r.4,r.7 // shift upper part of divisor
+ subfic r.9,r.7,32 // 32-N
+ srw r.9,r.3,r.9 // leftmost N bits of r.3, slid right
+ or r.4,r.4,r.9 // and inserted into low end of r.4
+ slw r.3,r.3,r.7 // shift lower part of divisor
+// Estimate high-order halfword of quotient. If the dividend is
+// A0 A1 A2 A3 and the divisor is B0 B1 (where each Ai or Bi is a halfword),
+// then the estimate is A0 A1 0000 divided by B0 0000, or A0 A1 divided by B0.
+// (r.4 holds A0 A1, r.3 holds A2 A3, and r.5 holds B0 B1.)
+// The estimate may be too high because it does not account for B1; in rare
+// cases, the estimate will not even fit in a halfword. High estimates are
+// corrected for later.
+ srwi r.8,r.5,16 // r.8 <- B0
+ divwu r.12,r.4,r.8 // r.12 <- floor([A0 A1]/B0)
+// Subtract partial quotient times divisor from dividend: If Q0 is the quotient
+// computed above, this means that Q0 0000 times B0 B1 is subtracted from
+// A0 A1 A2 A3. We compute Q0 times B0 B1 and then shift the two-word
+// product left 16 bits.
+ mullw r.9,r.12,r.5 // low word of Q0 times B0 B1
+ mulhwu r.10,r.12,r.5 // high word of Q0 times B0 B1
+ slwi r.10,r.10,16 // shift high word left 16 bits
+ inslwi r.10,r.9,16,16 // move 16 bits from left of low word to
+ // right of high word
+ slwi r.9,r.9,16 // shift low word left 16 bits
+ subfc r.3,r.9,r.3 // low word of difference
+ subfe r.4,r.10,r.4 // high word of difference
+// If the estimate for Q0 was too high, the difference will be negative.
+// While A0 A1 A2 A3 is negative, repeatedly add B0 B1 0000 to A0 A1 A2 A3
+// and decrement Q0 by one to correct for the overestimate.
+ cmpwi r.4,0 // A0 A1 A2 A3 is negative iff A0 A1 is
+ bge Q0_okay // no correction needed
+ inslwi r.10,r.5,16,16 // high word of B0 B1 0000 (= 0000 B0)
+ slwi r.9,r.5,16 // low word of B0 B1 0000 (= B1 0000)
+adjust_Q0:
+ addc r.3,r.3,r.9 // add B0 B1 0000 to A0 A1 A2 A3 (low)
+ adde r.4,r.4,r.10 // add B0 B1 0000 to A0 A1 A2 A3 (high)
+ cmpwi r.4,0 // Is A0 A1 A2 A3 now nonnegative?
+ addi r.12,r.12,-1 // decrement Q0
+ blt adjust_Q0 // if A0 A1 A2 A3 still negative, repeat
+Q0_okay:
+// Estimate low-order halfword of quotient. A0 is necessarily 0000 at this
+// point, so if the remaining part of the dividend is A0 A1 A2 A3 then the
+// estimate is A1 A2 0000 divided by B0 0000, or A1 A2 divided by B0.
+// (r.4 holds A0 A1, r.3 holds A2 A3, and r.8 holds B0.)
+ slwi r.9,r.4,16 // r.9 <- A1 0000
+ inslwi r.9,r.3,16,16 // r.9 <- A1 A2
+ divwu r.11,r.9,r.8 // r.11 <- floor([A1 A2]/B0)
+// Subtract partial quotient times divisor from remaining part of dividend:
+// If Q1 is the quotient computed above, this means
+// that Q1 times B0 B1 is subtracted from A0 A1 A2 A3. We compute
+ mullw r.9,r.11,r.5 // low word of Q1 times B0 B1
+ mulhwu r.10,r.11,r.5 // high word of Q1 times B0 B1
+ subfc r.3,r.9,r.3 // low word of difference
+ subfe r.4,r.10,r.4 // high word of difference
+// If the estimate for Q1 was too high, the difference will be negative.
+// While A0 A1 A2 A3 is negative, repeatedly add B0 B1 to A0 A1 A2 A3
+// and decrement Q1 by one to correct for the overestimate.
+ cmpwi r.4,0 // A0 A1 A2 A3 is negative iff A0 A1 is
+ bge Q1_okay // no correction needed
+adjust_Q1:
+ addc r.3,r.3,r.5 // add B0 B1 to A0 A1 A2 A3 (low)
+ addze r.4,r.4 // add B0 B1 to A0 A1 A2 A3 (high)
+ cmpwi r.4,0 // Is A0 A1 A2 A3 now nonnegative?
+ addi r.11,r.11,-1 // decrement Q1
+ blt adjust_Q1 // if A0 A1 A2 A3 still negative, repeat
+Q1_okay:
+// Build the results. The desired quotient is Q0 Q1.
+// The desired remainder is obtained by shifting A2 A3 right by the number
+// of bits by which the dividend and divisor were shifted left in the
+// normalization step. The number of bits shifted is still in r.7.
+ cmplwi r.6,0 // remainder needed?
+ bne rem1 // if so, go compute it
+ slwi r.3,r.12,16 // r.3 <- Q0 0000
+ or r.3,r.3,r.11 // r.3 <- Q0 Q1
+ blr
+rem1:
+ srw r.8,r.3,r.7 // remainder <- [A2 A3] >> (r.7)
+ slwi r.3,r.12,16 // r.3 <- Q0 0000
+ stw r.8,0(r.6) // store remainder
+ or r.3,r.3,r.11 // r.3 <- Q0 Q1
+ blr
+//
+// End of normal case
+//
+// The case of a 32-bit dividend:
+only_32_bits:
+ cmplwi r.6,0 // remainder needed?
+ bne rem2 // if so, go compute quotient+remainder
+ divwu r.3,r.3,r.5 // result <- dividend/divisor
+ blr
+rem2:
+ divwu r.7,r.3,r.5 // quotient <- dividend / divisor
+ mullw r.8,r.7,r.5 // r.8 <- quotient * divisor
+ subf r.8,r.8,r.3 // remainder<-dividend-quotient*divisor
+ mr r.3,r.7 // result <- quotient
+ stw r.8,0(r.6) // store remainder
+ blr
+// The error cases:
+overflow:
+ twi 6,r.5,0 // trap if divide by zero
+ twi 0x1b,r.5,0 // trap on overflow
+
+ LEAF_EXIT(RtlEnlargedUnsignedDivide)
+
+
+//++
+//
+// ULARGE_INTEGER
+// RtlExtendedLargeIntegerDivide (
+// 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.
+//
+// Arguments:
+//
+// Dividend (r.5, r.6) - Supplies the dividend value.
+//
+// Divisor (r.7) - Supplies the divisor value.
+//
+// Remainder (r.8)- Supplies an optional pointer to a variable
+// that receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedLargeIntegerDivide)
+
+ cmplwi r.7,0 // zero divisor?
+ beq div_zero_s // if so, branch to error exit
+ cmpwi r.6,0 // check sign of dividend high word
+ bne big_dividend
+
+// The high-order word of the dividend is zero, so 32-bit unsigned division
+// can be used.
+ li r.12,0 // upper word of quotient is zero
+ stw r.12,4(r.3) // store upper word of quotient
+ divwu r.11,r.5,r.7 // compute lower word of quotient
+ stw r.11,0(r.3) // store lower word of quotient
+ cmplwi r.8,0 // remainder needed?
+ beqlr // if not, return
+ mullw r.10,r.11,r.7 // quotient * divisor
+ subf r.9,r.10,r.5 // dividend - quotient * divisor
+ stw r.9,0(r.8) // store remainder
+ blr // return
+
+big_dividend:
+ srwi. r.0,r.7,16 // upper 16 bits of divisor
+ bne long_division // if not, must use long division
+
+// The divisor is only one 16-bit digit long, so use short division:
+ srwi r.0,r.6,16 // first 16-bit digit of dividend
+ divwu r.4,r.0,r.7 // first 16-bit digit of quotient
+ mullw r.10,r.4,r.7 // amount to subtract for remainder
+ subf r.9,r.10,r.0 // remainder from first digit
+ insrwi r.6,r.9,16,0 // combine rmndr with 2nd digit of dvdnd
+ divwu r.12,r.6,r.7 // second digit of quotient
+ insrwi r.12,r.4,16,0 // high two quotient digits in one word
+ mullw r.10,r.12,r.7 // amount to subtract for remainder
+ subf r.9,r.10,r.6 // remainder from second digit
+ srwi r.0,r.5,16 // third digit of dividend
+ insrwi r.0,r.9,16,0 // combine rmndr with 3rd digit of dvdnd
+ divwu r.4,r.0,r.7 // third digit of quotient
+ mullw r.10,r.4,r.7 // amount to subtract for remainder
+ subf r.9,r.10,r.0 // remainder from third digit
+ insrwi r.5,r.9,16,0 // combine rmndr with 4th digit of dvdnd
+ divwu r.11,r.5,r.7 // fourth digit of quotient
+ mullw r.10,r.11,r.7 // amount to subtract for remainder
+ insrwi r.11,r.4,16,0 // low two quotient digits in one word
+ subf r.9,r.10,r.5 // remainder from fourth digit
+ b store_results
+
+long_division:
+// Since the divisor is more than one 16-bit digit long, the quotient will
+// be of the form 0x0000 Q2 Q3 Q4, where each of Q2, Q3, and Q4 is a 16-bit
+// digit.
+//
+// Normalize the divisor and dividend so that the high-order bit of the
+// divisor is 1. This normalization must be undone after the division to
+// compute the remainder. Let U1 U2 U3 U4 be the 16-bit digits of the
+// unnormalized dividend. Each digit Ui consists of an S-bit high-order part
+// UiH and a (16-S)-bit low-order part UiL, where S is the number of leading
+// zeroes in the divisor. Thus R6 holds U1H U1L U2H U2L and R5 holds
+// U3H U3L U4H U4L. Let N0 N1 N2 N3 N4 be the 16-bit digits of the normalized
+// dividend: N0 = U1H, N1 = U1L U2H, N2 = U2L U3H, N3 = U3L U4H, N4 = U4L 0...0.
+// Let D1 D2 be the normalized divisor.
+ cntlzw r.0,r.7 // number of bits to shift left (S)
+ subfic r.12,r.0,16 // 16-S
+ subfic r.11,r.0,32 // 32-S
+ srw r.9,r.6,r.12 // U1H U1L U2H = N0 N1
+ srw r.4,r.5,r.11 // U3H
+ slw r.10,r.6,r.0 // U1L U2H U2L 0...0
+ or r.6,r.4,r.10 // U1L U2H U2L U3H = N1 N2
+ slw r.4,r.5,r.0 // U3L U4H U4L 0...0 = N3 N4
+ slw r.7,r.7,r.0 // normalized divisor (D1 D2)
+ srwi r.11,r.7,16 // D1
+// Set Q2 = [N0 N1 N2] / [D1 D2]. Start by guessing Q2 = [N0 N1] / D1, then
+// adjust if necessary. This guess will occasionally be one too high and
+// very rarely two too high, but never higher than that and never too low.
+// (See Theorem B in Section 4.3.1 of Knuth, Vol. II, pp. 256-7.)
+// Let C N0' N1' N2' be the partial remainder [N0 N1 N2] - Q2 * [D1 D2].
+ divwu r.12,r.9,r.11 // guess Q2 = [N0 N1] / D1
+ srwi r.10,r.9,16 // 0x0000 N0
+ mullw r.5,r.12,r.7 // low word of Q2 * [D1 D2]
+ subfc r.9,r.5,r.6 // low word of C N0' N1' N2' (N1' N2')
+ mulhwu r.5,r.12,r.7 // high word of Q2 * [D1 D2]
+ subfe. r.10,r.5,r.10 // high word of C N0' N1' N2' (C N0')
+ bge Q2_okay // if difference is >= 0, Q2 was not too high
+adjust_Q2:
+ addc r.9,r.9,r.7 // low word of [C N0' N1' N2']+[D1 D2]
+ addze. r.10,r.10 // high word of [C N0' N1' N2']+[D1 D2]
+ addi r.12,r.12,-1 // Q2 - 1
+ blt adjust_Q2 // try again if still negative
+Q2_okay:
+// At this point 0 <= [C N0' N1' N2'] < [D1 D2], so [C N0'] = 0x00000000.
+// r.12 holds the upper word of the quotient, 0x0000 Q2.
+// Set Q3 = [N1' N2' N3] / [D1 D2] by guessing and adjusting as above.
+// Let [N0" N1" N2" N3"] be the partial remainder [N1' N2' N3] - Q3 * [D1 D2].
+ divwu r.6,r.9,r.11 // guess Q3 = [N1' N2'] / D1
+ srwi r.10,r.9,16 // 0x0000 N1'
+ slwi r.9,r.9,16 // N2' 0x0000
+ inslwi r.9,r.4,16,16 // N2' N3
+ mullw r.5,r.6,r.7 // low word of Q3 * [D1 D2]
+ subfc r.9,r.5,r.9 // N2" N3"
+ mulhwu r.5,r.6,r.7 // high word of Q3 * [D1 D2]
+ subfe. r.10,r.5,r.10 // N0" N1"
+ bge Q3_okay // if difference >= 0, Q3 was not too high
+adjust_Q3:
+ addc r.9,r.9,r.7 // low word of [N0" N1" N2" N3"]+[D1 D2]
+ addze. r.10,r.10 // high word [N0" N1" N2" N3"]+[D1 D2]
+ addi r.6,r.6,-1 // Q3 - 1
+ blt adjust_Q3 // try again if difference still negative
+Q3_okay:
+// At this point 0 <= [N0" N1" N2" N3"] < [D1 D2], so [N0" N1"] = 0x00000000.
+// Set Q4 = [N2" N3" N4] / [D1 D2] by guessing and adjusting as above.
+// Let [R1 R2 R3 R4] be the partial remainder [N2" N3" N4] - Q4 * [D1 D2].
+ divwu r.11,r.9,r.11 // guess Q4 = [N2" N3"] / D1
+ insrwi r.4,r.9,16,0 // N3" N4
+ srwi r.10,r.9,16 // 0x0000 N2"
+ mullw r.5,r.11,r.7 // low word of Q4 * [D1 D2]
+ subfc r.9,r.5,r.4 // R3 R4
+ mulhwu r.5,r.11,r.7 // high word of Q4 * [D1 D2]
+ subfe. r.10,r.5,r.10 // R1 R2
+ bge Q4_okay // if difference < 0, Q4 was not too high
+adjust_Q4:
+ addc r.9,r.9,r.7 // low word of [R1 R2 R3 R4]+[D1 D2]
+ addze. r.10,r.10 // high word of [R1 R2 R3 R4]+[D1 D2]
+ addi r.11,r.11,-1 // Q4 - 1
+ blt adjust_Q4 // try again if partial remainder still negative
+Q4_okay:
+// At this point 0 <= [R1 R2 R3 R4] < [D1 D2], so [R3 R4] is the remainder.
+ insrwi r.11,r.6,16,0 // low word of quotient: Q3 Q4
+ srw r.9,r.9,r.0 // unnormalize remainder
+
+store_results:
+ stw r.11,0(r.3) // store low word of quotient
+ stw r.12,4(r.3) // store high word of quotient
+ cmplwi r.8,0 // remainder needed?
+ beqlr // if not, return
+ stw r.9,0(r.8) // store remainder
+ blr // return
+
+div_zero_s:
+ twi 6,r.7,0 // Trap on divide by zero
+
+ LEAF_EXIT(RtlExtendedLargeIntegerDivide)
+
+
+//++
+//
+// LARGE_INTEGER
+// RtlExtendedMagicDivide (
+// IN LARGE_INTEGER Dividend,
+// IN ULARGE_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.
+// The value returned is the most significant 64 bits of the product
+// Dividend*MagicDivisor, shifted right ShiftCount bits.
+//
+// Arguments:
+//
+// Dividend (r.5, r.6) - Supplies the dividend value.
+//
+// MagicDivisor (r.7, r.8) - Supplies the magic divisor value
+// which is a 64-bit multiplicative reciprocal.
+//
+// Shiftcount (r.9) - Supplies the right shift adjustment value,
+// assumed to be in the range 0 to 63.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+// Let Dividend = A B and MagicDivisor = C D, where each of A, B, C, and D is
+// a 32-bit word. Then Dividend*MagicDivisor is a 128-bit product, computed
+// as follows:
+// A B
+// x C D
+// ==========================================================
+// high_word(B*D) low_word(B*D)
+// high_word(A*D) low_word(A*D)
+// high_word(B*C) low_word(B*C)
+// high_word(A*C) low_word(A*C)
+// ==========================================================
+// P1 P2 P3 P4
+//
+// Since the return value is [P1 P2] >> Shift_Count, P3 and P4 need not be
+// computed, but the carry out of the P3 column must be computed to compute P2.
+
+ LEAF_ENTRY(RtlExtendedMagicDivide)
+
+// If the dividend is negative, negate it and record the fact by setting
+// cr7 to LT.
+ crclr 4*cr.7+0 // clear cr.7 LT bit
+ cmpwi r.6,0 // is high-order word of divisor < 0?
+ bge divisor_nonnegative // if not, we are ready to compute
+ crset 4*cr.7+0 // set cr.7 LT bit to mark negation
+ subfic r.5,r.5,0 // negate lower half of dividend
+ subfze r.6,r.6 // negate upper half of dividend
+divisor_nonnegative:
+// To avoid pipeline delays, produce partial products first, in the order they
+// will be consumed by the addc and addze instructions below.
+ mulhwu r.11,r.6,r.7 // high(A*D)
+ mulhwu r.0,r.5,r.8 // high(B*C)
+ mulhwu r.12,r.6,r.8 // high(A*C)
+ mullw r.4,r.6,r.8 // low(A*C)
+ mulhwu r.10,r.5,r.7 // high(B*D)
+ mullw r.6,r.6,r.7 // low(A*D)
+ mullw r.7,r.5,r.8 // low(B*C)
+// Now combine the partial products, forming P1 in r.12, P2 in r.11, P3 in r.10:
+ addc r.11,r.11,r.0 // high(A*D)+high(B*C)
+ addze r.12,r.12 // high(A*C)+partial carry
+ addc r.11,r.11,r.4 // high(A*D)+high(B*C)+low(A*C)
+ addze r.12,r.12 // high(A*C)+partial carry
+ addc r.10,r.10,r.6 // high(B*D)+low(A*D)
+ addze r.11,r.11 // hi(A*D)+hi(B*C)+low(A*C)+part. carry
+ addze r.12,r.12 // high(A*C)+partial carry
+ addc r.10,r.10,r.7 // high(B*D)+low(A*D)+low(B*C) = P3
+ addze r.11,r.11 // hi(A*D)+hi(B*C)+low(A*C)+carry = P2
+ addze r.12,r.12 // high(A*C)+carry = P1
+// Shift the 64-bit value whose high half is in r.12 and whose low half is in
+// r.11 right by (r.7) bits. The sequence below depends on the fact that
+// shift amounts are interpreted mod 64. In particular, a shift amount of
+// -N bits, where 0 < N <= 32, specifies a shift of 64-N bits, where
+// 32 <= 64-N < 64, and a shift of 32 or more bits always yields zero.
+// Let s = (r.9) be the amount to shift right. The sequence below behaves
+// differently when s<32, when s=32, and when s>32. When s<32, we view the
+// 64-bit value to be shifted as follows:
+//
+// | r.12 | r.11 |
+// +-----------------+------------+-----------------+------------+
+// | T | U | V | W |
+// +-----------------+------------+-----------------+------------+
+// |<- (32-s) bits ->|<- s bits ->|<- (32-s) bits ->|<- s bits ->|
+//
+// When s >= 32, we view the 64-bit value to be shifted as follows:
+//
+// | r.12 | r.11 |
+// +-----------------+------------+------------------------------+
+// | X | Y | Z |
+// +-----------------+------------+------------------------------+
+// | |<- (s-32) ->|<------------ 32 ------------>|
+// |<- (64-s) bits ->|<--------------- s bits ------------------>|
+//
+ // When s < 32: | When s = 32: | When s > 32:
+ // -------------+--------------+-------------
+ subfic r.7,r.9,32 // 32-s (> 0) | 0 | 32-s (< 0)
+ addi r.8,r.9,-32 // s-32 (< 0) | 0 | s-32 (> 0)
+ srw r.11,r.11,r.9 // 0...0 V | 0 | 0
+ slw r.4,r.12,r.7 // U 0...0 | X (= X Y) | 0
+ or r.11,r.11,r.4 // U V | X | 0
+ srw r.4,r.12,r.8 // 0 | X | 0...0 X
+ or r.11,r.11,r.4 // U V | X | 0...0 X
+ srw r.12,r.12,r.9 // 0...0 T | 0 | 0
+
+// If the original dividend was negated, we now negate the result:
+ bnl cr.7,sign_is_right
+ subfic r.11,r.11,0 // negate low word
+ subfze r.12,r.12 // negate high word
+sign_is_right:
+// Store the result:
+ stw r.11,0(r.3) // low word of result
+ stw r.12,4(r.3) // high word of result
+ blr // return
+
+ LEAF_EXIT(RtlExtendedMagicDivide)
+
+
+//++
+//
+// ULARGE_INTEGER
+// RtlLargeIntegerDivide (
+// IN ULARGE_INTEGER Dividend,
+// IN ULARGE_INTEGER Divisor,
+// IN PLARGE_INTEGER Remainder.
+// )
+//
+// Routine Description:
+//
+// This function divides an unsigned large integer by an unsigned large
+// integer and returns the resultant quotient and optionally the remainder.
+//
+// Arguments:
+//
+// Dividend (r.5, r.6) - Supplies the dividend value.
+//
+// Divisor (r.7, r.8) - Supplies the divisor value.
+//
+// Remainder (r.9)- Supplies an optional pointer to a variable
+// that receives the remainder.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerDivide)
+
+ or. r.0,r.7,r.8 // combine low and high parts of divisor
+ beq div0 // if 0, then attempted division by zero
+ li r.0,64 // set loop count
+ mtctr r.0 // in the count register
+ li r.10,0 // clear partial remainder
+ li r.11,0 //
+
+// Invariants for the following loop:
+// 1. (q<<(CTR)*Divisor) + [r.11 r.10 r.6 r.5]>>(64-(CTR)) = Dividend
+// 2. [r.11 r.10] < Divisor
+// where q is the rightmost (64-(CTR)) bits of [r.6 r.5]
+// Initially, (CTR)=64 and [r.11 r.10] = 0,, so q=0 and
+// [r.11 r.10 r.6 r.5]>>(64-(CTR)) = [0 0 r.6 r.5]>>0 = [r.6 r.5], reducing
+// the loop invariants to:
+// 1. [r.6 r.5] = Dividend
+// 2. 0 < Divisor
+// At the end of the loop, (CTR)=0, so q=[r.6 r.5] and the loop invariants
+// reduce to:
+// 1. [r.6 r.5]*Divisor + [r.11 r.10] = Dividend
+// 2. [r.11 r.10] < Divisor
+// That is, [r.6 r.5] holds the quotient and [r.11 r.10] holds the remainder.
+// During execution of the loop, [r.11 r.10] holds the partial remainder, the
+// leftmost (CTR) bits of [r.6 r.5] hold the bits of the dividend not yet
+// appended to the partial remainder, and the remaining bits of [r.6 r.5]
+// hold the leftmost (64-(CTR)) bits of the quotient.
+
+divl:
+// Shift the 128-bit quantity [r.11 r.10 r.6 r.5] left one bit. This has the
+// effect of dropping the leftmost bit of the partial remainder (necessarily
+// zero), "bringing down" the next bit of the dividend to the right end of the
+// partial remainder, and shifting the partial quotient left one bit.
+ slwi r.11,r.11,1 // shift r.11 left one bit
+ inslwi r.11,r.10,1,31 // left bit of r.10 to right bit of r.11
+ slwi r.10,r.10,1 // shift r.10 left one bit
+ inslwi r.10,r.6,1,31 // left bit of r.6 to right bit of r.10
+ slwi r.6,r.6,1 // shift r.6 left one bit
+ inslwi r.6,r.5,1,31 // left bit of r.5 to right bit of r.6
+ slwi r.5,r.5,1 // shift r.5 left one bit
+// If [r.11 r.10] >= Divisor, increment the quotient and subtract the divisor
+// from the partial remainder:
+ cmplw cr.0,r.11,r.8 // high(partial_rem) vs. high(divisor)
+ cmplw cr.1,r.10,r.7 // low(partial_rem) vs. low(divisor)
+ bgt cr.0,PR_greater // high(part_rem) > high(divisor)
+ blt cr.0,endl // high(part_rem) < high(divisor)
+ blt cr.1,endl // highs =, low(part_rem) < low(divisor)
+
+PR_greater:
+ ori r.5,r.5,1 // increment shifted quotient
+ subfc r.10,r.7,r.10 // low(part_rem-divisor)
+ subfe r.11,r.8,r.11 // high(part_rem-divisor)
+
+endl: bdnz divl // decrement CTR and loop
+
+ cmplwi r.9,0 // remainder requested?
+ beq norem // no remainder
+ stw r.10,0(r.9) // store low part of remainder
+ stw r.11,4(r.9) // store high part of remainder
+norem: stw r.5,0(r.3) // store low part of quotient
+ stw r.6,4(r.3) // store high part of quotient
+ blr
+
+div0:
+ twi 6,r.0,0 // Trap on divide by zero
+
+ LEAF_EXIT(RtlLargeIntegerDivide) //
+
+
+
+//++
+//
+// LARGE_INTEGER
+// Rtl ExtendedIntegerMultiply (
+// 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 (r.5, r.6) - Supplies the multiplicand value.
+//
+// Multiplier (r.7) - Supplies the multiplier value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlExtendedIntegerMultiply)
+
+// If A B is the multiplicand and C is the multiplier (where A, B, and C are
+// each 32 bits long), the product is computed as follows:
+//
+// A B
+// * C
+// =============================
+// high(B*C) low(B*C)
+// + high(A*C) low(A*C)
+// =============================
+// P1 P2 P3
+//
+// Since the high-order bit of B is not a sign bit but the high-order bit of
+// C is, we have no multiplication instruction appropriate for computing
+// high(B*C) directly. Instead, we negate any negative operand before
+// doing the multiplication, multiply using unsigned arithmetic, and then
+// negate the product if we had negated exactly one operand. If P1 is
+// nonzero before negating the product, the multiplication has overflowed.
+// We use the LT bit of cr.7 to track whether exactly one operand has
+// been negated.
+
+ crclr 4*cr.7+0 // clear LT bit of cr.7
+ cmpwi r.6,0 // test sign of multiplicand
+ bge multiplicand_adjusted // if nonnegative, proceed
+ subfic r.5,r.5,0 // negate low part of multiplicand
+ subfze r.6,r.6 // negate high part of multiplicand
+ crset 4*cr.7+0 // set LT bit of cr.7
+multiplicand_adjusted:
+ cmpwi r.7,0 // test sign of multiplier
+ bge multiplier_adjusted // if nonnegative, proceed
+ neg r.7,r.7 // negate multiplier
+ crnot 4*cr.7+0,4*cr.7+0 // invert LT bit of cr.7
+multiplier_adjusted:
+ mulhwu r.9,r.5,r.7 // high(B*C)
+ mullw r.10,r.6,r.7 // low(A*C)
+ mulhwu r.11,r.6,r.7 // high(A*C)
+ mullw r.8,r.5,r.7 // P3 = low(B*C)
+ addc r.9,r.9,r.10 // P2 = high(B*C)+low(A*C)
+ addze r.11,r.11 // P1 = high(A*C)+[carry out of P2]
+ cmpwi r.11,0 // check for overflow
+ bne mull_over
+ bnl cr.7,product_adjusted // was exactly one operand negated?
+ subfic r.8,r.8,0 // negate low part of product
+ subfze r.9,r.9 // negate high part of product
+product_adjusted:
+ stw r.8,0(r.3) // store low word of product
+ stw r.9,4(r.3) // store high word of product
+ blr
+mull_over:
+ twi 0x1b,r.11,0 // Trap on overflow
+
+ LEAF_EXIT(RtlExtendedIntegerMultiply)
+
+
+
+//++
+//
+// 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 (r.5, r.6) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerNegate)
+
+ subfic r.5,r.5,0 // double precision subtract from 0
+ subfze r.6,r.6
+ stw r.5,0(r.3) // store low part of result
+ stw r.6,4(r.3) // store high part of result
+ LEAF_EXIT(RtlLargeIntegerNegate) // return
+
+
+
+//++
+//
+// 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 (r.5, r.6) - Supplies the minuend value.
+//
+// Subtrahend (r.7, r.8) - Supplies the subtrahend value.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerSubtract)
+
+ subfc r.5,r.7,r.5 // double precision subtract
+ subfe r.6,r.8,r.6
+ stw r.5,0(r.3) // store low part of result
+ stw r.6,4(r.3) // store high part of result
+ LEAF_EXIT(RtlLargeIntegerSubtract) // return
+
+
+
+//++
+//
+// 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 (r.5, r.6) - Supplies the large integer to be shifted.
+//
+// ShiftCount (r.7) - Supplies the left shift count.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftLeft)
+
+ andi. r.7,r.7,0x3f // mod 64 of shift count
+ subfic r.8,r.7,32
+ slw r.6,r.6,r.7
+ srw r.0,r.5,r.8
+ or r.6,r.6,r.0
+ addic r.8,r.7,-32
+ slw r.0,r.5,r.8
+ or r.6,r.6,r.0
+ slw r.5,r.5,r.7
+ stw r.5,0(r.3) // store low result
+ stw r.6,4(r.3) // store high result
+ LEAF_EXIT(RtlLargeIntegerShiftLeft)
+
+
+
+//++
+//
+// 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 (r.5, r.6) - Supplies the large integer to be shifted.
+//
+// ShiftCount (r.7) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerShiftRight)
+
+ andi. r.7,r.7,0x3f // mod 64 of shift count
+
+// The sequence below depends on the fact that shift amounts are interpreted
+// mod 64. In particular, a shift amount of -N bits, where 0 < N <= 32,
+// specifies a shift of 64-N bits, where 32 <= 64-N < 64, and a shift of 32 or
+// more bits always yields zero. Let s = (r.7) be the amount to shift right.
+// The sequence below behaves differently when s<32, when s=32, and when s>32.
+// When s<32, we view the 64-bit value to be shifted as follows:
+//
+//
+// | r.6 | r.5 |
+// +-----------------+------------+-----------------+------------+
+// | T | U | V | W |
+// +-----------------+------------+-----------------+------------+
+// |<- (32-s) bits ->|<- s bits ->|<- (32-s) bits ->|<- s bits ->|
+//
+// When s >= 32, we view the 64-bit value to be shifted as follows:
+//
+// | r.6 | r.5 |
+// +-----------------+------------+------------------------------+
+// | X | Y | Z |
+// +-----------------+------------+------------------------------+
+// | |<- (s-32) ->|<------------ 32 ------------>|
+// |<- (64-s) bits ->|<--------------- s bits ------------------>|
+//
+ // When s < 32: | When s = 32: | When s > 32:
+ // -------------+--------------+-------------
+ subfic r.8,r.7,32 // 32-s (> 0) | 0 | 32-s (< 0)
+ addi r.9,r.7,-32 // s-32 (< 0) | 0 | s-32 (> 0)
+ srw r.5,r.5,r.7 // 0...0 V | 0 | 0
+ slw r.0,r.6,r.8 // U 0...0 | X (= X Y) | 0
+ or r.5,r.5,r.0 // U V | X | 0
+ srw r.0,r.6,r.9 // 0 | X | 0...0 X
+ or r.5,r.5,r.0 // U V | X | 0...0 X
+ srw r.6,r.6,r.7 // 0...0 T | 0 | 0
+
+ stw r.5,0(r.3) // store low result
+ stw r.6,4(r.3) // store high result
+ LEAF_EXIT(RtlLargeIntegerShiftRight)
+
+
+
+//++
+//
+// 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 (r.5, r.6) - Supplies the large integer to be shifted.
+//
+// ShiftCount (r.7) - Supplies the right shift count.
+//
+// Return Value:
+//
+// The large integer result is stored at the address supplied by r.3.
+//
+//--
+
+ LEAF_ENTRY(RtlLargeIntegerArithmeticShift)
+
+ andi. r.7,r.7,0x3f // mod 64 of shift count
+
+// The sequence below depends on the fact that shift amounts are interpreted
+// mod 64. In particular, a shift amount of -N bits, where 0 < N <= 32,
+// specifies a shift of 64-N bits, where 32 <= 64-N < 64, and an arithmetic
+// shift of 32 or more bits always yields 32 copies of the original sign bit.
+// Let s = (r.7) be the amount to shift right. The sequence below behaves
+// differently when s<=32 and when s>32. When s<=32, we view the 64-bit value
+// to be shifted as follows:
+//
+//
+// | r.6 | r.5 |
+// +-----------------+------------+-----------------+------------+
+// | T | U | V | W |
+// +-----------------+------------+-----------------+------------+
+// |<- (32-s) bits ->|<- s bits ->|<- (32-s) bits ->|<- s bits ->|
+//
+// When s > 32, we view the 64-bit value to be shifted as follows:
+//
+// | r.6 | r.5 |
+// +-----------------+------------+------------------------------+
+// | X | Y | Z |
+// +-----------------+------------+------------------------------+
+// | |<- (s-32) ->|<------------ 32 ------------>|
+// |<- (64-s) bits ->|<--------------- s bits ------------------>|
+//
+
+ // When s <= 32: | When s > 32:
+ // ----------------------+----------------------
+ subfic r.8,r.7,32 // 32-s (>= 0) | 32-s (< 0)
+ srw r.5,r.5,r.7 // 0...0 V | 0
+ slw r.0,r.6,r.8 // U 0...0 | 0
+ or r.5,r.5,r.0 // U V | 0
+ addic. r.9,r.7,-32 // s-32 (<= 0) | s-32 (> 0)
+ sraw r.10,r.6,r.9 // sign(TU)...sign(TU) | sign(XY)...sign(XY) X
+ ble more_than_32 // (CR0 set by addic. two instructions earlier.)
+ mr r.5,r.10 // [instruction skipped] | sign(XY)...sign(XY) X
+more_than_32: // |
+ sraw r.6,r.6,r.7 // sign(T)...sign(T) T | sign(XY)...sign(XY)
+
+ stw r.5,0(r.3) // store low result
+ stw r.6,4(r.3) // store high result
+ LEAF_EXIT(RtlLargeIntegerArithmeticShift)
diff --git a/private/ntos/rtl/ppc/lzntppc.s b/private/ntos/rtl/ppc/lzntppc.s
new file mode 100644
index 000000000..c23ce1990
--- /dev/null
+++ b/private/ntos/rtl/ppc/lzntppc.s
@@ -0,0 +1,1347 @@
+// TITLE("LZ Decompression")
+//++
+//
+// Copyright (c) 1994 Microsoft Corporation
+//
+// Module Name:
+//
+// lzntppc.s
+//
+// Abstract:
+//
+// This module implements the decompression engine needed
+// to support file system compression.
+//
+// Author:
+//
+// Chuck Lenzmeier (chuckl) 29-Nov-1994
+// adapted from Mark Enstrom's lzntmips.s
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+//--
+
+#include "ksppc.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
+//
+
+//
+// define stack based storage
+//
+
+ .struct 0
+LzHeader: .space StackFrameHeaderLength
+LzLr: .space 4
+LzR26: .space 4
+LzR27: .space 4
+LzR28: .space 4
+LzR29: .space 4
+LzR30: .space 4
+LzR31: .space 4
+ .align 3
+LzFrameLength:
+
+
+// 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:
+//
+// r3 - UncompressedBuffer - Pointer to start of destination buffer
+// r4 - EndOfUncompressedBufferPlus1 - One byte beyond uncompressed buffer
+// r5 - CompressedBuffer - Pointer to buffer of compressed data (this pointer
+// has been adjusted to point past the chunk header)
+// r6 - EndOfCompressedBufferPlus1 - One byte beyond compressed buffer
+// r7 - FinalUncompressedChunkSize - return bytes written to UncompressedBuffer
+//
+// Return Value:
+//
+// NTSTATUS -- STATUS_SUCCESS or STATUS_BAD_COMPRESSION_BUFFER
+//
+//--
+
+ SPECIAL_ENTRY(LZNT1DecompressChunk)
+
+ mflr r0
+ stw r31, LzR31-LzFrameLength(sp)
+ stw r30, LzR30-LzFrameLength(sp)
+ stw r29, LzR29-LzFrameLength(sp)
+ stw r28, LzR28-LzFrameLength(sp)
+ stw r27, LzR27-LzFrameLength(sp)
+ stw r26, LzR26-LzFrameLength(sp)
+ stw r0, LzLr -LzFrameLength(sp)
+ stwu sp, -LzFrameLength(sp)
+
+ PROLOGUE_END(LZNT1DecompressChunk)
+
+//
+// make copy of UncompressedBuffer for current output pointer
+//
+
+ mr r8,r3
+
+//
+// Initialize variables used in keeping track of the
+// LZ Copy Token format. r9 is used to store the maximum
+// displacement for each phase of LZ decoding
+// (see explanation of format in lzkm.c). This displacement
+// is added to the start of the CompressedBuffer address
+// so that a boundary crossing can be detected.
+//
+
+ li r9,0x10 // r9 = Max Displacement for LZ
+ add r10,r9,r3 // r10 = Format boundary
+ li r11,0xffff >> 4 // r11 = length mask
+ li r12,12 // r12 = 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
+//
+
+ subi r31,r4,8 // safe end of UncompressedBuffer
+ subi r30,r6,1+2*8 // safe end of CompressedBuffer
+
+Top:
+
+//
+// make sure safe copy can be performed for at least 8 literal bytes
+//
+
+ lbz r29,0(r5) // load flag byte
+
+ cmplw cr7,r5,r30 // safe check needed on UncompressedBuffer?
+ cmplw cr6,r8,r31 // safe check needed on CompressedBuffer?
+ bgt cr7,SafeCheckStart // branch if safe checks needed
+ bgt cr6,SafeCheckStart // branch if safe checks needed
+
+//
+// fall-through for copying 8 bytes.
+//
+
+ andi. r0,r29,0x01 // check bit 0 of flags
+ lbz r28,1(r5) // load literal or CopyToken[0]
+ bne LzCopy0 // if set, go to copy routine
+ stb r28,0(r8) // store literal byte to dst
+
+ andi. r0,r29,0x02 // check bit 1 of flags
+ lbz r28,2(r5) // load literal or CopyToken[0]
+ bne LzCopy1 // if set, go to copy routine
+ stb r28,1(r8) // store literal byte to dst
+
+ andi. r0,r29,0x04 // check bit 2 of flags
+ lbz r28,3(r5) // load literal or CopyToken[0]
+ bne LzCopy2 // if set, go to copy routine
+ stb r28,2(r8) // store literal byte to dst
+
+ andi. r0,r29,0x08 // check bit 3 of flags
+ lbz r28,4(r5) // load literal or CopyToken[0]
+ bne LzCopy3 // if set, go to copy routine
+ stb r28,3(r8) // store literal byte to dst
+
+ andi. r0,r29,0x10 // check bit 4 of flags
+ lbz r28,5(r5) // load literal or CopyToken[0]
+ bne LzCopy4 // if set, go to copy routine
+ stb r28,4(r8) // store literal byte to dst
+
+ andi. r0,r29,0x20 // check bit 5 of flags
+ lbz r28,6(r5) // load literal or CopyToken[0]
+ bne LzCopy5 // if set, go to copy routine
+ stb r28,5(r8) // store literal byte to dst
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+
+
+LzCopy0:
+
+//
+// LzCopy0
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,2(r5) // load second byte of copy token
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy0CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy0CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy0CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy0CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy0NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 1
+//
+
+ subi r8,r8,1 // unbias output pointer
+
+ andi. r0,r29,0x02 // check bit 1 of flags
+ lbz r28,2(r5) // load literal or CopyToken[0]
+ bne LzCopy1 // if set, go to copy routine
+ stb r28,1(r8) // store literal byte to dst
+
+ andi. r0,r29,0x04 // check bit 2 of flags
+ lbz r28,3(r5) // load literal or CopyToken[0]
+ bne LzCopy2 // if set, go to copy routine
+ stb r28,2(r8) // store literal byte to dst
+
+ andi. r0,r29,0x08 // check bit 3 of flags
+ lbz r28,4(r5) // load literal or CopyToken[0]
+ bne LzCopy3 // if set, go to copy routine
+ stb r28,3(r8) // store literal byte to dst
+
+ andi. r0,r29,0x10 // check bit 4 of flags
+ lbz r28,5(r5) // load literal or CopyToken[0]
+ bne LzCopy4 // if set, go to copy routine
+ stb r28,4(r8) // store literal byte to dst
+
+ andi. r0,r29,0x20 // check bit 5 of flags
+ lbz r28,6(r5) // load literal or CopyToken[0]
+ bne LzCopy5 // if set, go to copy routine
+ stb r28,5(r8) // store literal byte to dst
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy0NotSafe:
+
+ li r31,7 // seven bits left in current flag byte
+ addi r5,r5,2 // make r5 point to next src byte
+ srwi r29,r29,1 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+
+LzCopy1:
+
+//
+// LzCopy1
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,3(r5) // load second byte of copy token
+ addi r8,r8,1 // move r8 to point to byte 1
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy1CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy1CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy1CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy1CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy1NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 2
+//
+
+ subi r8,r8,2 // unbias output pointer
+
+ andi. r0,r29,0x04 // check bit 2 of flags
+ lbz r28,3(r5) // load literal or CopyToken[0]
+ bne LzCopy2 // if set, go to copy routine
+ stb r28,2(r8) // store literal byte to dst
+
+ andi. r0,r29,0x08 // check bit 3 of flags
+ lbz r28,4(r5) // load literal or CopyToken[0]
+ bne LzCopy3 // if set, go to copy routine
+ stb r28,3(r8) // store literal byte to dst
+
+ andi. r0,r29,0x10 // check bit 4 of flags
+ lbz r28,5(r5) // load literal or CopyToken[0]
+ bne LzCopy4 // if set, go to copy routine
+ stb r28,4(r8) // store literal byte to dst
+
+ andi. r0,r29,0x20 // check bit 5 of flags
+ lbz r28,6(r5) // load literal or CopyToken[0]
+ bne LzCopy5 // if set, go to copy routine
+ stb r28,5(r8) // store literal byte to dst
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy1NotSafe:
+
+ li r31,6 // six bits left in current flag byte
+ addi r5,r5,3 // make r5 point to next src byte
+ srwi r29,r29,2 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+
+LzCopy2:
+
+//
+// LzCopy2
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,4(r5) // load second byte of copy token
+ addi r8,r8,2 // move r8 to point to byte 2
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy2CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy2CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy2CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy2CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy2NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 3
+//
+
+ subi r8,r8,3 // unbias output pointer
+
+ andi. r0,r29,0x08 // check bit 3 of flags
+ lbz r28,4(r5) // load literal or CopyToken[0]
+ bne LzCopy3 // if set, go to copy routine
+ stb r28,3(r8) // store literal byte to dst
+
+ andi. r0,r29,0x10 // check bit 4 of flags
+ lbz r28,5(r5) // load literal or CopyToken[0]
+ bne LzCopy4 // if set, go to copy routine
+ stb r28,4(r8) // store literal byte to dst
+
+ andi. r0,r29,0x20 // check bit 5 of flags
+ lbz r28,6(r5) // load literal or CopyToken[0]
+ bne LzCopy5 // if set, go to copy routine
+ stb r28,5(r8) // store literal byte to dst
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy2NotSafe:
+
+ li r31,5 // five bits left in current flag byte
+ addi r5,r5,4 // make r5 point to next src byte
+ srwi r29,r29,3 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+
+LzCopy3:
+
+//
+// LzCopy3
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,5(r5) // load second byte of copy token
+ addi r8,r8,3 // move r8 to point to byte 3
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy3CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy3CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy3CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy3CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy3NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 4
+//
+
+ subi r8,r8,4 // unbias output pointer
+
+ andi. r0,r29,0x10 // check bit 4 of flags
+ lbz r28,5(r5) // load literal or CopyToken[0]
+ bne LzCopy4 // if set, go to copy routine
+ stb r28,4(r8) // store literal byte to dst
+
+ andi. r0,r29,0x20 // check bit 5 of flags
+ lbz r28,6(r5) // load literal or CopyToken[0]
+ bne LzCopy5 // if set, go to copy routine
+ stb r28,5(r8) // store literal byte to dst
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy3NotSafe:
+
+ li r31,4 // four bits left in current flag byte
+ addi r5,r5,5 // make r5 point to next src byte
+ srwi r29,r29,4 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+
+LzCopy4:
+
+//
+// LzCopy4
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,6(r5) // load second byte of copy token
+ addi r8,r8,4 // move r8 to point to byte 4
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy4CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy4CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy4CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy4CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy4NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 5
+//
+
+ subi r8,r8,5 // unbias output pointer
+
+ andi. r0,r29,0x20 // check bit 5 of flags
+ lbz r28,6(r5) // load literal or CopyToken[0]
+ bne LzCopy5 // if set, go to copy routine
+ stb r28,5(r8) // store literal byte to dst
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy4NotSafe:
+
+ li r31,3 // three bits left in current flag byte
+ addi r5,r5,6 // make r5 point to next src byte
+ srwi r29,r29,5 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+LzCopy5:
+
+//
+// LzCopy5
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,7(r5) // load second byte of copy token
+ addi r8,r8,5 // move r8 to point to byte 5
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy5CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy5CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy5CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy5CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy5NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 6
+//
+
+ subi r8,r8,6 // unbias output pointer
+
+ andi. r0,r29,0x40 // check bit 6 of flags
+ lbz r28,7(r5) // load literal or CopyToken[0]
+ bne LzCopy6 // if set, go to copy routine
+ stb r28,6(r8) // store literal byte to dst
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy5NotSafe:
+
+ li r31,2 // two bits left in current flag byte
+ addi r5,r5,7 // make r5 point to next src byte
+ srwi r29,r29,6 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+
+LzCopy6:
+
+//
+// LzCopy6
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,8(r5) // load second byte of copy token
+ addi r8,r8,6 // move r8 to point to byte 6
+ addi r5,r5,1 // fix-up src addr for return to switch
+ cmplw r10,r8 // is output pointer above format boundary?
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if necessary, call boundary adjust routine
+
+//
+// Check for a breach of the format boundary.
+//
+
+ cmplw r10,r8 // if r8 above format boundary,
+ bltl LzAdjustBoundary // call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy6CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy6CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy6CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy6CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then jump to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ bgt cr6,LzCopy6NotSafe // if beyond safe end, jump to safe code
+
+//
+// adjust r8 back to position it would be if this was a literal byte
+// copy. Continue flag check at position 7
+//
+
+ subi r8,r8,7 // unbias output pointer
+
+ andi. r0,r29,0x80 // check bit 7 of flags
+ lbz r28,8(r5) // load literal or CopyToken[0]
+ bne LzCopy7 // if set, go to copy routine
+ stb r28,7(r8) // store literal byte to dst
+
+ addi r5,r5,9 // inc src addr
+ addi r8,r8,8 // inc dst addr
+
+ b Top
+
+LzCopy6NotSafe:
+
+ li r31,1 // one bit left in current flag byte
+ addi r5,r5,8 // make r5 point to next src byte
+ srwi r29,r29,7 // shift flag byte into next position
+ b SafeCheckLoop
+
+
+LzCopy7:
+
+//
+// LzCopy7
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer address of current flag byte
+// r8 - UncomressedBuffer address at start of flag byte check
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+//
+// This routine is special since it is for the last bit in the flag
+// byte. The InputPointer(r5) and OutputPointer(r8) are biased at
+// the top of this segment and don't need to be biased again
+//
+//
+// Check for a breach of the format boundary.
+//
+
+ lbz r0,9(r5) // load second byte of copy token
+ addi r8,r8,7 // move r8 to point to byte 7
+ addi r5,r5,10 // r5 points to next actual src byte
+ cmplw r10,r8 // if r8 above format boundary,
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // call boundary adjust routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzCopy7CopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzCopy7CopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzCopy7CopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzCopy7CopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+// if r8 > Safe end of uncomressed buffer, then fall through to the
+// safe (slow) routine to do safety check before every load/store
+//
+
+ cmplw cr7,r8,r4 // at real end of uncompressed buffer?
+ cmplw cr6,r8,r31 // at safe end of uncompressed buffer?
+ beq cr7,LzSuccess // if at real end, success
+ ble cr6,Top // if not beyond safe end, goto top of loop
+
+//
+// r8 and r5 are already corrected
+// fall through to SafeCheckStart
+//
+
+
+
+//
+// Near the end of either compressed or uncompressed buffers,
+// check buffer limits before any load or store
+//
+
+SafeCheckStart:
+
+ cmplw r5,r6 // check for end of CompressedBuffer
+ beq LzSuccess // jump if done
+
+ lbz r29,0(r5) // load next flag byte
+ addi r5,r5,1 // inc src addr to literal/CopyFlag[0]
+ li r31,8 // loop count
+
+SafeCheckLoop:
+
+ cmplw cr7,r5,r6 // end of CompressedBuffer?
+ cmplw cr6,r8,r4 // end of UncompressedBuffer?
+ beq cr7,LzSuccess // branch if done
+ beq cr6,LzSuccess // branch if done
+
+ andi. r0,r29,1 // check current flag bit
+ lbz r28,0(r5) // load literal or CopyToken[0]
+ bne LzSafeCopy // if set, go to safe copy routine
+
+ addi r5,r5,1 // inc CompressedBuffer adr
+ stb r28,0(r8) // store literal byte
+ addi r8,r8,1 // inc UncompressedBuffer
+
+SafeCheckReentry:
+
+ subic. r31,r31,1 // decrement loop count
+ srwi r29,r29,1 // move next bit into position
+ bne SafeCheckLoop // loop until done with this flag byte
+
+ b SafeCheckStart // get next flag byte
+
+
+LzSafeCopy:
+
+//
+// LzSafeCopy
+//
+// r28 - CopyToken[0]
+// r5 - CompressedBuffer current address
+// r8 - UncomressedBuffer current address
+// r29 - Flag byte
+//
+// Load copy token (first byte already loaded), then combine into a 16 bit field.
+// Note that there may not actually be room for a copy token in the compressed buffer.
+//
+// Check for a breach of the format boundary.
+//
+
+ mr r27,r5 // save address of copy token
+ addi r5,r5,2 // fix-up src addr for return to switch
+ cmplw cr7,r5,r6 // does token fit in compressed buffer?
+ cmplw r10,r8 // is r8 above format boundary?
+ bgt cr7,LzCompressError // if gt, token straddles end of compressed buffer
+ lbz r0,1(r27) // load second byte of copy token
+ insrwi r28,r0,8,16 // insert second byte next to first
+ bltl LzAdjustBoundary // if lt, call boundary adjustment routine
+
+//
+// Extract offset and length from copy token
+//
+
+ srw r27,r28,r12 // r27 = offset
+ and r28,r28,r11 // r28 = length from field
+ addi r27,r27,1 // r27 = real offset
+ addi r28,r28,3 // r28 = real length
+
+//
+// Make sure offset doesn't go below start of uncompressed buffer
+//
+// check if length will not go up to or beyond actual uncompressed buffer length
+//
+
+ sub r26,r8,r27 // r26 = src pointer
+ sub r0,r4,r8 // r0 = length remaining in UncompressedBuffer
+ cmplw cr7,r26,r3 // is src below start of UncompressedBuffer?
+ cmplw cr6,r0,r28 // attempt to copy too much?
+ blt cr7,LzCompressError // error in compressed data
+ bltl cr6,LzAdjustLength // adjust if necessary
+
+//
+// copy r28 bytes bytes from (r26) to (r8)
+//
+
+// cmpwi r28,0 // if length 0?
+// beq LzSafeCopyCopyDone // skip if nothing to copy (IS THIS POSSIBLE?!?)
+ mtctr r28 // move length to count register
+
+ subi r26,r26,1 // bias r26 for lbzu
+ subi r8,r8,1 // bias r8 for stbu
+
+LzSafeCopyCopyLoop:
+
+ lbzu r0,1(r26) // load from src
+ stbu r0,1(r8) // store to dst
+ bdnz LzSafeCopyCopyLoop // loop until done
+
+ addi r8,r8,1 // unbias r8 for stbu
+
+//LzSafeCopyCopyDone:
+
+//
+// if r8 = r4, then we are up to the end of the uncompressed buffer.
+// return success
+//
+
+ cmplw r8,r4
+ bne 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
+//
+
+ sub r28,r8,r3 // bytes stored
+ li r3,STATUS_SUCCESS // indicate success
+ stw r28,0(r7) // store length
+
+LzComplete:
+
+ lwz r0, LzLr(sp)
+ lwz r31, LzR31(sp)
+ lwz r30, LzR30(sp)
+ lwz r29, LzR29(sp)
+ lwz r28, LzR28(sp)
+ lwz r27, LzR27(sp)
+ lwz r26, LzR26(sp)
+ mtlr r0
+ addi sp, sp, LzFrameLength
+
+ SPECIAL_EXIT(LZNT1DecompressChunk)
+
+//
+// fatal error in compressed data format
+//
+
+LzCompressError:
+
+ LWI (r3,STATUS_BAD_COMPRESSION_BUFFER)
+ b LzComplete
+
+
+//
+// at least one format boundary has been crossed, set up new bouandry
+// then jump back to the check routine to make sure new boundary is
+// correct
+//
+
+LzAdjustBoundary:
+
+ slwi r9,r9,1 // next length boundary
+ srwi r11,r11,1 // reduce width of length mask
+ add r10,r9,r3 // r10 = next offset boundary
+ subi r12,r12,1 // reduce shift count to isolate offset
+
+ cmplw r10,r8 // still above format boundary?
+ blt LzAdjustBoundary // if yes, keep shifting
+
+ blr // return to caller
+
+//
+// The length specified in the copy token (r28) is greater than the
+// length remaining in the uncompressed buffer (r0).
+//
+
+LzAdjustLength:
+
+ mr r28,r0 // length = MIN(specified length, length in buffer)
+ blr // return to caller
+
diff --git a/private/ntos/rtl/ppc/movemem.s b/private/ntos/rtl/ppc/movemem.s
new file mode 100644
index 000000000..4a0398322
--- /dev/null
+++ b/private/ntos/rtl/ppc/movemem.s
@@ -0,0 +1,2194 @@
+// TITLE("Compare, Move, Zero, and Fill Memory Support")
+//++
+//
+// Copyright (c) 1993 IBM Corporation
+//
+// Module Name:
+//
+// mvmem.s
+//
+// Abstract:
+//
+// This module implements functions to compare, move, zero, and fill
+// blocks of memory. If the memory is aligned, then these functions
+// are very efficient.
+//
+// N.B. These routines MUST preserve all floating state since they are
+// frequently called from interrupt service routines that normally
+// do not save or restore floating state.
+//
+// Author:
+//
+// Curt Fawcett (crf) 10-Aug-1993
+//
+// Environment:
+//
+// User or Kernel mode.
+//
+// Revision History:
+//
+// Curt Fawcett 11-Jan-1994 Removed register definitions
+// and fixed for new assembler
+//
+//--
+
+#include <ksppc.h>
+
+//
+// Define local constants
+//
+ .set BLKLN,32
+//
+//--
+//++
+//
+// 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:
+//
+// SRC1 (r.3) - Supplies a pointer to the first block of memory to
+// compare.
+//
+// SRC2 (r.4) - Supplies a pointer to the second block of memory to
+// compare.
+//
+// LNGTH (r.5) - 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.
+//
+//--
+//
+// Define the routine entry point
+
+ LEAF_ENTRY(RtlCompareMemory)
+//
+// Compare Memory
+//
+// Check alignment
+//
+ or. r.6,r.5,r.5 // Check for zero length
+ mr r.12,r.5 // Save original length
+ beq GetResults2 // Jump if zero length
+ cmpwi r.5,4 // Check for less than 4 bytes
+ add r.11,r.3,r.5 // Get ending SRC1 address
+ xor r.9,r.3,r.4 // Check for same alignment
+ blt- CompareByByte // Jump if single byte compares
+ andi. r.9,r.9,3 // Isolate alignment bits
+ bne- CompUnaligned // Jump if different alignments
+//
+// Compare Memory - Same SRC1 and SRC2 alignment
+//
+// Compare extra bytes until a word boundary is reached
+//
+
+CompAligned:
+ andi. r.6,r.4,3 // Check alignment type
+ beq+ CompBlkDiv // Jump to process 32-Byte blocks
+ cmpwi r.6,3 // Check for 1 byte unaligned
+ lbz r.7,0(r.3) // Get unaligned byte
+ lbz r.8,0(r.4) // Get unaligned byte
+ bne+ Comp2 // If not, check next case
+ li r.6,1 // Set byte move count
+ b UpdateCompAddrs // Jump to update addresses
+Comp2:
+ cmpwi r.6,2 // Check for halfword aligned
+ li r.6,2 // Set byte move count
+ bne+ Comp3 // If not, check next case
+ lhz r.7,0(r.3) // Get unaligned halfword
+ lhz r.8,0(r.4) // Get unaligned halfword
+ b UpdateCompAddrs // Jump to update addresses
+Comp3:
+ cmpw r.7,r.8 // Check for 1st word equal
+ lhz r.7,1(r.3) // Get unaligned halfword
+ lhz r.8,1(r.4) // Get unaligned halfword
+ li r.6,3 // Set byte move count
+ bne Wrd1ne // Jump if 1st word not equal
+UpdateCompAddrs:
+ cmpw r.7,r.8 // Check for 1st word equal
+ sub r.5,r.5,r.6 // Decrement LNGTH by unaligned
+ bne Wrd1ne // Jump if 1st word not equal
+ add r.3,r.3,r.6 // Update the SRC1 address
+ add r.4,r.4,r.6 // Update the SRC2 address
+//
+// Divide the block to process into 32-byte blocks
+//
+
+CompBlkDiv:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompareBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Compare 32-byte blocks
+//
+CompFullBlks:
+ lwz r.6,0(r.3) // Get 1st SRC1 word
+ lwz r.7,0(r.4) // Get 1st SRC2 word
+ lwz r.8,4(r.3) // Get 2nd SRC1 word
+ cmpw r.6,r.7 // Check for 1st word equal
+ lwz r.9,4(r.4) // Get 2nd SRC2 word
+ bne- Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lwz r.6,8(r.3) // Get 3rd SRC1 word
+ lwz r.7,8(r.4) // Get 3rd SRC2 word
+ bne- Wrd2ne // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lwz r.8,12(r.3) // Get 4th SRC1 word
+ lwz r.9,12(r.4) // Get 4th SRC2 word
+ bne- Wrd3ne // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ lwz r.6,16(r.3) // Get 5th SRC1 word
+ lwz r.7,16(r.4) // Get 5th SRC2 word
+ bne- Wrd4ne // Jump if 4th word not equal
+ cmpw r.6,r.7 // Check for 5th word equal
+ lwz r.8,20(r.3) // Get 6th SRC1 word
+ lwz r.9,20(r.4) // Get 6th SRC2 word
+ bne- Wrd5ne // Jump if 5th word not equal
+ cmpw r.8,r.9 // Check for 6th word equal
+ lwz r.6,24(r.3) // Get 7th SRC1 word
+ lwz r.7,24(r.4) // Get 7th SRC2 word
+ bne- Wrd6ne // Jump if 6th word not equal
+ cmpw r.6,r.7 // Check for 7th word equal
+ lwz r.8,28(r.3) // Get 8th SRC1 word
+ lwz r.9,28(r.4) // Get 8th SRC2 word
+ bne- Wrd7ne // Jump if 7th word not equal
+ cmpw r.8,r.9 // Check for 8th word equal
+ bne- Wrd8ne // Jump if 8th word not equal
+ addi r.3,r.3,32 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for all blocks done
+ addi r.4,r.4,32 // Update SRC2 pointer
+ bne+ CompFullBlks // Jump if more blocks
+//
+// Compare 4-byte blocks
+//
+
+CompareBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompareByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+CompLpOn4Bytes:
+ lwz r.6,0(r.3) // Get 1st SRC1 word
+ lwz r.7,0(r.4) // Get 1st SRC2 word
+ cmpw r.6,r.7 // Check for 1st word equal
+ bne- Wrd1ne // Jump if 1st word not equal
+ addi r.3,r.3,4 // Get pointer to next SRC1 block
+ cmpw r.3,r.10 // Check for last block
+ addi r.4,r.4,4 // Get pointer to next SRC2 block
+ bne+ CompLpOn4Bytes // Jump if more blocks
+//
+// Compare 1-byte blocks
+//
+
+CompareByByte:
+ cmpwi r.5,0 // Check for no bytes left
+ beq+ GetResults // Jump to return if done
+ lbz r.6,0(r.3) // Get 1st SRC1 byte
+ lbz r.7,0(r.4) // Load 1st SRC2 byte
+ cmpw r.6,r.7 // Check for 1st word equal
+ bne- Wrd1ne // Jump if 1st word not equal
+ addi r.3,r.3,1 // Update SRC1 address
+ cmpwi r.5,1 // Check for no bytes left
+ addi r.4,r.4,1 // Update SRC2 address
+ beq+ GetResults // Jump to return if done
+ lbz r.6,0(r.3) // Get 2nd SRC1 byte
+ lbz r.7,0(r.4) // Load 2nd SRC2 byte
+ cmpw r.6,r.7 // Check for 1st word equal
+ bne- Wrd1ne // Jump if 1st word not equal
+ cmpwi r.5,2 // Check for no bytes left
+ addi r.4,r.4,1 // Update SRC2 address
+ addi r.3,r.3,1 // Update SRC1 address
+ beq+ GetResults // Jump to return if done
+ lbz r.6,0(r.3) // Get 3rd SRC1 byte
+ lbz r.7,0(r.4) // Load 3rd SRC2 byte
+ cmpw r.6,r.7 // Check for 1st word equal
+ bne- Wrd1ne // Jump if 1st word not equal
+
+ addi r.4,r.4,1 // Update SRC2 address
+ addi r.3,r.3,1 // Update SRC1 address
+ b GetResults // Jump to return
+//
+// Compare - SRC1 and SRC2 have different alignments
+//
+
+CompUnaligned:
+ or r.9,r.3,r.4 // Check if either byte unaligned
+ andi. r.9,r.9,3 // Isolate alignment
+ cmpwi r.9,2 // Check for even result
+ bne+ CompByteUnaligned // Jump for byte unaligned
+//
+// Divide the blocks to process into 32-byte blocks
+//
+
+CompBlkDivUnaligned:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompHWrdBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Compare - SRC1 or SRC2 is halfword aligned, the other is by word
+//
+
+CompByHWord:
+ lhz r.6,0(r.3) // Get 1st hword of 1st SRC1 wrd
+ lhz r.7,0(r.4) // Get 1st hword of 1st SRC2 wrd
+ lhz r.8,2(r.3) // Get 2nd hword of 1st SRC1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lhz r.9,2(r.4) // Get 2nd hword of 1st SRC2 wrd
+ bne- Wrd1ne // Check for 1st word equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lhz r.6,4(r.3) // Get 1st hword of 2nd SRC1 wrd
+ lhz r.7,4(r.4) // Get 1st hword of 2nd SRC2 wrd
+ bne- Wrd1ne // Check for 1st word equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lhz r.8,6(r.3) // Get 2nd hword of 2nd SRC1 wrd
+ lhz r.9,6(r.4) // Get 2nd hword of 2nd SRC2 wrd
+ bne- Wrd2ne // Check for 2nd word equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lhz r.6,8(r.3) // Get 1st hword of 3rd SRC1 wrd
+ lhz r.7,8(r.4) // Get 1st hword of 3rd SRC2 wrd
+ bne- Wrd2ne // Check for 2nd word equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lhz r.8,10(r.3) // Get 2nd hword of 3rd SRC1 wrd
+ lhz r.9,10(r.4) // Get 2nd hword of 3rd SRC2 wrd
+ bne- Wrd3ne // Check for 3rd word equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lhz r.6,12(r.3) // Get 1st hword of 4th SRC1 wrd
+ lhz r.7,12(r.4) // Get 1st hword of 4th SRC2 wrd
+ bne- Wrd3ne // Check for 3rd word equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lhz r.8,14(r.3) // Get 2nd hword of 4th SRC1 wrd
+ lhz r.9,14(r.4) // Get 2nd hword of 4th SRC2 wrd
+ bne- Wrd4ne // Check for 4th word equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ lhz r.6,16(r.3) // Get 1st hword of 5th SRC1 wrd
+ lhz r.7,16(r.4) // Get 1st hword of 5th SRC2 wrd
+ bne- Wrd4ne // Check for 4th word equal
+ cmpw r.6,r.7 // Check for 5th word equal
+ lhz r.8,18(r.3) // Get 2nd hword of 5th SRC1 wrd
+ lhz r.9,18(r.4) // Get 2nd hword of 5th SRC2 wrd
+ bne- Wrd5ne // Check for 5th word equal
+ cmpw r.8,r.9 // Check for 5th word equal
+ lhz r.6,20(r.3) // Get 1st hword of 6th SRC1 wrd
+ lhz r.7,20(r.4) // Get 1st hword of 6th SRC2 wrd
+ bne- Wrd5ne // Check for 5th word equal
+ cmpw r.6,r.7 // Check for 6th word equal
+ lhz r.8,22(r.3) // Get 2nd hword of 6th SRC1 wrd
+ lhz r.9,22(r.4) // Get 2nd hword of 6th SRC2 wrd
+ bne- Wrd6ne // Check for 6th word equal
+ cmpw r.8,r.9 // Check for 6th word equal
+ lhz r.6,24(r.3) // Get 1st hword of 7th SRC1 wrd
+ lhz r.7,24(r.4) // Get 1st hword of 7th SRC2 wrd
+ bne- Wrd6ne // Check for 6th word equal
+ cmpw r.6,r.7 // Check for 7th word equal
+ lhz r.8,26(r.3) // Get 2nd hword of 7th SRC1 wrd
+ lhz r.9,26(r.4) // Get 2nd hword of 7th SRC2 wrd
+ bne- Wrd7ne // Check for 7th word equal
+ cmpw r.8,r.9 // Check for 7th word equal
+ lhz r.6,28(r.3) // Get 1st hword of 8th SRC1 wrd
+ lhz r.7,28(r.4) // Get 1st hword of 8th SRC2 wrd
+ bne- Wrd7ne // Check for 7th word equal
+ cmpw r.6,r.7 // Check for 8th word equal
+ lhz r.8,30(r.3) // Get 2nd hword of 8th SRC1 wrd
+ lhz r.9,30(r.4) // Get 2nd hword of 8th SRC2 wrd
+ bne- Wrd8ne // Check for 8th word equal
+ cmpw r.8,r.9 // Check for 8th word equal
+ bne- Wrd8ne // Check for 8th word equal
+ addi r.3,r.3,32 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for all blocks done
+ addi r.4,r.4,32 // Update SRC2 pointer
+ bne+ CompByHWord // Jump if more blocks
+//
+// Compare 4-byte blocks with SRC2 Halfword unaligned
+//
+
+CompHWrdBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompareByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+CompHWrdLpOn4Bytes:
+ lhz r.6,0(r.3) // Get 1st hword of 1st SRC1 wrd
+ lhz r.7,0(r.4) // Get 1st hword of 1st SRC2 wrd
+ lhz r.8,2(r.3) // Get 2nd hword of 1st SRC1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lhz r.9,2(r.4) // Get 2nd hword of 1st SRC2 wrd
+ bne- Wrd1ne // Check for 1st word equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ bne- Wrd1ne // Check for 1st word equal
+ addi r.3,r.3,4 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for last block
+ addi r.4,r.4,4 // Update SRC2 pointer
+ bne+ CompHWrdLpOn4Bytes // Jump if more blocks
+
+ b CompareByByte // Jump to complete last bytes
+//
+// Compare - Byte unaligned
+//
+
+CompByteUnaligned:
+ and r.9,r.3,r.4 // Check for both byte aligned
+ andi. r.9,r.9,1 // Isolate alignment bits
+ beq- CmpBlksByByte // Jump if both not byte aligned
+//
+// Divide the blocks to process into 32-byte blocks
+//
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompByteBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Compare - SRC1 and SRC2 are byte unaligned differently
+//
+
+CompByByte:
+ lbz r.6,0(r.3) // Get first byte of 1st SRC1 wrd
+ lbz r.7,0(r.4) // Get first byte of 1st SRC2 wrd
+ lhz r.8,1(r.3) // Get mid-h-word of 1st SRC1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lhz r.9,1(r.4) // Get mid-h-word of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lhz r.6,3(r.3) // Get h-word crossing 1st/2nd SRC1 wrd
+ lhz r.7,3(r.4) // Get h-word crossing 1st/2nd SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 1st word equal
+ lhz r.8,5(r.3) // Get mid-h-word of 2nd SRC1 wrd
+ lhz r.9,5(r.4) // Get mid-h-word of 2nd SRC2 wrd
+ bne Wrd1ne // Jump if 2nd word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lhz r.6,7(r.3) // Get h-word crossing 2nd/3rd SRC1 wrd
+ lhz r.7,7(r.4) // Get h-word crossing 2nd/3rd SRC2 wrd
+ bne Wrd2ne // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lhz r.8,9(r.3) // Get mid-h-word of 3rd SRC1 wrd
+ lhz r.9,9(r.4) // Get mid-h-word of 3rd SRC2 wrd
+ bne Wrd2ne // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lhz r.6,11(r.3) // Get h-word crossing 3rd/4th SRC1 wrd
+ lhz r.7,11(r.4) // Get h-word crossing 3rd/4th SRC2 wrd
+ bne Wrd3ne // Jump if 3rd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lhz r.8,13(r.3) // Get mid-h-word of 4th SRC1 wrd
+ lhz r.9,13(r.4) // Get mid-h-word of 4th SRC2 wrd
+ bne Wrd3ne // Jump if 4th word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ lhz r.6,15(r.3) // Get h-word crossing 4th/5th SRC1 wrd
+ lhz r.7,15(r.4) // Get h-word crossing 4th/5th SRC2 wrd
+ bne Wrd4ne // Jump if 4th word not equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lhz r.8,17(r.3) // Get mid-h-word of 5th SRC1 wrd
+ lhz r.9,17(r.4) // Get mid-h-word of 5th SRC2 wrd
+ bne Wrd4ne // Jump if 5th word not equal
+ cmpw r.8,r.9 // Check for 5th word equal
+ lhz r.6,19(r.3) // Get h-word crossing 5th/6th SRC1 wrd
+ lhz r.7,19(r.4) // Get h-word crossing 5th/6th SRC2 wrd
+ bne Wrd5ne // Jump if 5th word not equal
+ cmpw r.6,r.7 // Check for 5th word equal
+ lhz r.8,21(r.3) // Get mid-h-word of 6th SRC1 wrd
+ lhz r.9,21(r.4) // Get mid-h-word of 6th SRC2 wrd
+ bne Wrd5ne // Jump if 6th word not equal
+ cmpw r.8,r.9 // Check for 6th word equal
+ lhz r.6,23(r.3) // Get h-word crossing 6th/7th SRC1 wrd
+ lhz r.7,23(r.4) // Get h-word crossing 6th/7th SRC2 wrd
+ bne Wrd6ne // Jump if 6th word not equal
+ cmpw r.6,r.7 // Check for 6th word equal
+ lhz r.8,25(r.3) // Get mid-h-word of 7th SRC1 wrd
+ lhz r.9,25(r.4) // Get mid-h-word of 7th SRC2 wrd
+ bne Wrd6ne // Jump if 7th word not equal
+ cmpw r.8,r.9 // Check for 7th word equal
+ lhz r.6,27(r.3) // Get h-word crossing 7th/8th SRC1 wrd
+ lhz r.7,27(r.4) // Get h-word crossing 7th/8th SRC2 wrd
+ bne Wrd7ne // Jump if 7th word not equal
+ cmpw r.6,r.7 // Check for 7th word equal
+ lhz r.8,29(r.3) // Get mid-h-word of 8th SRC1 wrd
+ lhz r.9,29(r.4) // Get mid-h-word of 8th SRC2 wrd
+ bne Wrd7ne // Jump if 8th word not equal
+ cmpw r.8,r.9 // Check for 8th word equal
+ lbz r.6,31(r.3) // Get last byte of 8th SRC1 wrd
+ lbz r.7,31(r.4) // Get last byte of 8th SRC2 wrd
+ bne Wrd8ne // Jump if 8th word not equal
+ cmpw r.6,r.7 // Check for 8th word equal
+ bne Wrd8ne // Jump if 8th word not equal
+ addi r.3,r.3,32 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for all blocks done
+ addi r.4,r.4,32 // Update SRC2 pointer
+ bne+ CompByByte // Jump if more blocks
+//
+// Compare 4-byte blocks with SRC2 or SRC1 Byte aligned
+//
+
+CompByteBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompareByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+CompByteLpOn4Bytes:
+ lbz r.6,0(r.3) // Get first byte of 1st SRC1 wrd
+ lbz r.7,0(r.4) // Get first byte of 1st SRC2 wrd
+ lhz r.8,1(r.3) // Get mid-h-word of 1st SRC1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lhz r.9,1(r.4) // Get mid-h-word of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lbz r.6,3(r.3) // Get last byte of 1st SRC1 wrd
+ lbz r.7,3(r.4) // Get last byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 1st word equal
+ bne Wrd1ne // Jump if 1st word not equal
+ addi r.3,r.3,4 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for last block
+ addi r.4,r.4,4 // Update SRC2 pointer
+ bne+ CompByteLpOn4Bytes // Jump if more blocks
+
+ b CompareByByte // Jump to complete last bytes
+//
+// Compare - Either SRC1 or SRC2 is byte unaligned but not both
+//
+// Divide the blocks to process into 32-byte blocks
+//
+
+CmpBlksByByte:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompBlksOf4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+
+CompBlksByByte:
+ lbz r.6,0(r.3) // Get first byte of 1st SRC1 wrd
+ lbz r.7,0(r.4) // Get first byte of 1st SRC2 wrd
+ lbz r.8,1(r.3) // Get 2nd byte of 1st SRC1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lbz r.9,1(r.4) // Get 2nd byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lbz r.6,2(r.3) // Get first byte of 1st SRC1 wrd
+ lbz r.7,2(r.4) // Get first byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 1st word equal
+ lbz r.8,3(r.3) // Get 2nd byte of 1st SRC1 wrd
+ lbz r.9,3(r.4) // Get 2nd byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lbz r.6,4(r.3) // Get first byte of 2nd SRC1 wrd
+ lbz r.7,4(r.4) // Get first byte of 2nd SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lbz r.8,5(r.3) // Get 2nd byte of 2nd SRC1 wrd
+ lbz r.9,5(r.4) // Get 2nd byte of 2nd SRC2 wrd
+ bne Wrd2ne // Jump if 2nd word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lbz r.6,6(r.3) // Get first byte of 2nd SRC1 wrd
+ lbz r.7,6(r.4) // Get first byte of 2nd SRC2 wrd
+ bne Wrd2ne // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lbz r.8,7(r.3) // Get 2nd byte of 2nd SRC1 wrd
+ lbz r.9,7(r.4) // Get 2nd byte of 2nd SRC2 wrd
+ bne Wrd2ne // Jump if 2nd word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lbz r.6,8(r.3) // Get first byte of 3rd SRC1 wrd
+ lbz r.7,8(r.4) // Get first byte of 3rd SRC2 wrd
+ bne Wrd2ne // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lbz r.8,9(r.3) // Get 2nd byte of 3rd SRC1 wrd
+ lbz r.9,9(r.4) // Get 2nd byte of 3rd SRC2 wrd
+ bne Wrd3ne // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lbz r.6,10(r.3) // Get first byte of 3rd SRC1 wrd
+ lbz r.7,10(r.4) // Get first byte of 3rd SRC2 wrd
+ bne Wrd3ne // Jump if 3rd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lbz r.8,11(r.3) // Get 2nd byte of 3rd SRC1 wrd
+ lbz r.9,11(r.4) // Get 2nd byte of 3rd SRC2 wrd
+ bne Wrd3ne // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lbz r.6,12(r.3) // Get first byte of 4th SRC1 wrd
+ lbz r.7,12(r.4) // Get first byte of 4th SRC2 wrd
+ bne Wrd3ne // Jump if 3rd word not equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lbz r.8,13(r.3) // Get 2nd byte of 4th SRC1 wrd
+ lbz r.9,13(r.4) // Get 2nd byte of 4th SRC2 wrd
+ bne Wrd4ne // Jump if 4th word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ lbz r.6,14(r.3) // Get first byte of 4th SRC1 wrd
+ lbz r.7,14(r.4) // Get first byte of 4th SRC2 wrd
+ bne Wrd4ne // Jump if 4th word not equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lbz r.8,15(r.3) // Get 2nd byte of 4th SRC1 wrd
+ lbz r.9,15(r.4) // Get 2nd byte of 4th SRC2 wrd
+ bne Wrd4ne // Jump if 4th word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ lbz r.6,16(r.3) // Get first byte of 5th SRC1 wrd
+ lbz r.7,16(r.4) // Get first byte of 5th SRC2 wrd
+ bne Wrd4ne // Jump if 4th word not equal
+ cmpw r.6,r.7 // Check for 5th word equal
+ lbz r.8,17(r.3) // Get 2nd byte of 5th SRC1 wrd
+ lbz r.9,17(r.4) // Get 2nd byte of 5th SRC2 wrd
+ bne Wrd5ne // Jump if 5th word not equal
+ cmpw r.8,r.9 // Check for 5th word equal
+ lbz r.6,18(r.3) // Get first byte of 5th SRC1 wrd
+ lbz r.7,18(r.4) // Get first byte of 5th SRC2 wrd
+ bne Wrd5ne // Jump if 5th word not equal
+ cmpw r.6,r.7 // Check for 5th word equal
+ lbz r.8,19(r.3) // Get 2nd byte of 5th SRC1 wrd
+ lbz r.9,19(r.4) // Get 2nd byte of 5th SRC2 wrd
+ bne Wrd5ne // Jump if 5th word not equal
+ cmpw r.8,r.9 // Check for 5th word equal
+ lbz r.6,20(r.3) // Get first byte of 6th SRC1 wrd
+ lbz r.7,20(r.4) // Get first byte of 6th SRC2 wrd
+ bne Wrd5ne // Jump if 5th word not equal
+ cmpw r.6,r.7 // Check for 6th word equal
+ lbz r.8,21(r.3) // Get 2nd byte of 6th SRC1 wrd
+ lbz r.9,21(r.4) // Get 2nd byte of 6th SRC2 wrd
+ bne Wrd6ne // Jump if 6th word not equal
+ cmpw r.8,r.9 // Check for 6th word equal
+ lbz r.6,22(r.3) // Get first byte of 6th SRC1 wrd
+ lbz r.7,22(r.4) // Get first byte of 6th SRC2 wrd
+ bne Wrd6ne // Jump if 6th word not equal
+ cmpw r.6,r.7 // Check for 6th word equal
+ lbz r.8,23(r.3) // Get 2nd byte of 6th SRC1 wrd
+ lbz r.9,23(r.4) // Get 2nd byte of 6th SRC2 wrd
+ bne Wrd6ne // Jump if 6th word not equal
+ cmpw r.8,r.9 // Check for 6th word equal
+ lbz r.6,24(r.3) // Get first byte of 7th SRC1 wrd
+ lbz r.7,24(r.4) // Get first byte of 7th SRC2 wrd
+ bne Wrd6ne // Jump if 6th word not equal
+ cmpw r.6,r.7 // Check for 7th word equal
+ lbz r.8,25(r.3) // Get 2nd byte of 7th SRC1 wrd
+ lbz r.9,25(r.4) // Get 2nd byte of 7th SRC2 wrd
+ bne Wrd7ne // Jump if 7th word not equal
+ cmpw r.8,r.9 // Check for 7th word equal
+ lbz r.6,26(r.3) // Get first byte of 7th SRC1 wrd
+ lbz r.7,26(r.4) // Get first byte of 7th SRC2 wrd
+ bne Wrd7ne // Jump if 7th word not equal
+ cmpw r.6,r.7 // Check for 7th word equal
+ lbz r.8,27(r.3) // Get 2nd byte of 7th SRC1 wrd
+ lbz r.9,27(r.4) // Get 2nd byte of 7th SRC2 wrd
+ bne Wrd7ne // Jump if 7th word not equal
+ cmpw r.8,r.9 // Check for 7th word equal
+ lbz r.6,28(r.3) // Get first byte of 8th SRC1 wrd
+ lbz r.7,28(r.4) // Get first byte of 8th SRC2 wrd
+ bne Wrd7ne // Jump if 7th word not equal
+ cmpw r.6,r.7 // Check for 8th word equal
+ lbz r.8,29(r.3) // Get 2nd byte of 8th SRC1 wrd
+ lbz r.9,29(r.4) // Get ond byte of 8th SRC2 wrd
+ bne Wrd8ne // Jump if 8th word not equal
+ cmpw r.8,r.9 // Check for 8th word equal
+ lbz r.6,30(r.3) // Get first byte of 8th SRC1 wrd
+ lbz r.7,30(r.4) // Get first byte of 8th SRC2 wrd
+ bne Wrd8ne // Jump if 8th word not equal
+ cmpw r.6,r.7 // Check for 8th word equal
+ lbz r.8,31(r.3) // Get 2nd byte of 8th SRC1 wrd
+ lbz r.9,31(r.4) // Get 2nd byte of 8th SRC2 wrd
+ bne Wrd8ne // Jump if 8th word not equal
+ cmpw r.8,r.9 // Check for 8th word equal
+ bne Wrd8ne // Jump if 8th word not equal
+ addi r.3,r.3,32 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for all blocks done
+ addi r.4,r.4,32 // Update SRC2 pointer
+ bne+ CompBlksByByte // Jump if more blocks
+//
+// Divide the blocks to process into 32-byte blocks
+//
+CompBlksOf4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- CompareByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+CompBlksLpOn4Bytes:
+ lbz r.6,0(r.3) // Get first byte of 1st SRC1 wrd
+ lbz r.7,0(r.4) // Get first byte of 1st SRC2 wrd
+ lbz r.8,1(r.3) // Get 2nd byte of 1st SRC1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lbz r.9,1(r.4) // Get 2nd byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lbz r.6,2(r.3) // Get first byte of 1st SRC1 wrd
+ lbz r.7,2(r.4) // Get first byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 1st word equal
+ lbz r.8,3(r.3) // Get 2nd byte of 1st SRC1 wrd
+ lbz r.9,3(r.4) // Get 2nd byte of 1st SRC2 wrd
+ bne Wrd1ne // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ bne Wrd1ne // Jump if 1st word not equal
+ addi r.3,r.3,4 // Update SRC1 pointer
+ cmpw r.3,r.10 // Check for last block
+ addi r.4,r.4,4 // Update SRC2 pointer
+ bne+ CompBlksLpOn4Bytes // Jump if more blocks
+
+ b CompareByByte // Jump to complete last bytes
+//
+// Adjust pointers to SRC1 and SRC2 to isolate offending byte compare
+//
+Wrd2ne:
+ addi r.3,r.3,4 // Adjust to point to 2nd word
+ addi r.4,r.4,4 // Adjust to point to 2nd word
+ b Compare1byte // Jump to isolate the bad byte
+Wrd3ne:
+ addi r.3,r.3,8 // Adjust to point to 3rd word
+ addi r.4,r.4,8 // Adjust to point to 3rd word
+ b Compare1byte // Jump to isolate the bad byte
+Wrd4ne:
+ addi r.3,r.3,12 // Adjust to point to 4th word
+ addi r.4,r.4,12 // Adjust to point to 4th word
+ b Compare1byte // Jump to isolate the bad byte
+Wrd5ne:
+ addi r.3,r.3,16 // Adjust to point to 5th word
+ addi r.4,r.4,16 // Adjust to point to 5th word
+ b Compare1byte // Jump to isolate the bad byte
+Wrd6ne:
+ addi r.3,r.3,20 // Adjust to point to 6th word
+ addi r.4,r.4,20 // Adjust to point to 6th word
+ b Compare1byte // Jump to isolate the bad byte
+Wrd7ne:
+ addi r.3,r.3,24 // Adjust to point to 7th word
+ addi r.4,r.4,24 // Adjust to point to 7th word
+ b Compare1byte // Jump to isolate the bad byte
+Wrd8ne:
+ addi r.3,r.3,28 // Adjust to point to 8th word
+ addi r.4,r.4,28 // Adjust to point to 8th word
+Wrd1ne:
+Compare1byte:
+ sub r.5,r.11,r.3 // Calculate remaining byte count
+ add r.8,r.3,r.5 // Get new ending address
+ cmpwi r.5,0 // Check for no block to compare
+ beq- GetResults // Jump if processing completed
+SingleByte:
+ lbz r.6,0(r.3) // Get next SRC1 byte
+ lbz r.7,0(r.4) // Get next SRC2 byte
+ addi r.4,r.4,1 // Update SRC2 to next byte
+ cmpw r.6,r.7 // Check for unequal bytes
+ bne- GetResults // Jump if bytes aren't equal
+ addi r.3,r.3,1 // Update SRC1 to next byte
+ cmpw r.3,r.8 // Check for being done
+ bne+ SingleByte // Jump if more bytes
+//
+// Compute the results
+//
+GetResults:
+ sub r.6,r.11,r.3 // Get no. of bytes not compared
+GetResults2:
+ sub r.3,r.12,r.6 // Get no. of bytes that match
+//
+// Exit the routine
+//
+ LEAF_EXIT(RtlCompareMemory)
+
+//++
+//
+// ULONG
+// RtlEqualMemory (
+// IN PVOID Source1,
+// IN PVOID Source2,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function compares two blocks of memory for equality.
+//
+// Arguments:
+//
+// Source1 (r.3) - Supplies a pointer to the first block of memory to
+// compare.
+//
+// Source2 (r.4) - Supplies a pointer to the second block of memory to
+// compare.
+//
+// Length (r.5) - 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)
+
+//
+// Check alignment
+//
+
+ clrlwi r.12,r.5,28 // isolate residual bytes (Length & 15)
+ or r.9,r.3,r.4 // merge alignment bits
+ sub. r.11,r.5,r.12 // subtract out residual bytes
+ add r.10,r.3,r.5 // get ending Source1 address
+ beq+ EqualByByte // if eq, no 16-byte block to compare
+ andi. r.9,r.9,3 // isolate alignment bits
+ add r.5,r.3,r.11 // compute ending block address
+ bne- EqualUnaligned // if ne, different alignments
+
+EqualAligned:
+
+//
+// Both blocks are word-aligned, and there are at least 16 bytes to compare.
+//
+
+ lwz r.6,0(r.3) // Get 1st Source1 word
+ lwz r.7,0(r.4) // Get 1st Source2 word
+ lwz r.8,4(r.3) // Get 2nd Source1 word
+ cmpw r.6,r.7 // Check for 1st word equal
+ lwz r.9,4(r.4) // Get 2nd Source2 word
+ bne- EqualNotEqual // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lwz r.6,8(r.3) // Get 3rd Source1 word
+ lwz r.7,8(r.4) // Get 3rd Source2 word
+ bne- EqualNotEqual // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lwz r.8,12(r.3) // Get 4th Source1 word
+ lwz r.9,12(r.4) // Get 4th Source2 word
+ bne- EqualNotEqual // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ addi r.3,r.3,16 // Update Source1 pointer
+ bne- EqualNotEqual // Jump if 4th word not equal
+ cmpw r.3,r.5 // Check for all blocks done
+ addi r.4,r.4,16 // Update Source2 pointer
+ bne- EqualAligned // Jump if more blocks
+
+ sub r.5,r.10,r.3 // compute remaining bytes
+
+EqualByByte:
+
+//
+// Compare 1-byte blocks until done.
+//
+
+ cmpwi r.5,0 // Check for no bytes left
+ beq+ EqualEqual // Jump to return if done
+
+EqualByByteLoop:
+
+ lbz r.6,0(r.3) // Get Source1 byte
+ lbz r.7,0(r.4) // Get Source2 byte
+ addi r.3,r.3,1 // Update Source1 address
+ cmpw r.6,r.7 // Check for equality
+ addi r.4,r.4,1 // Update Source2 address
+ bne- EqualNotEqual // Jump if not equal
+ cmpw r.10,r.3 // Check for end of block
+ bne- EqualByByteLoop // Loop if not done
+
+EqualEqual:
+
+//
+// The blocks are not equal.
+//
+
+ li r.3,TRUE // indicate blocks are equal
+
+ blr // return to caller
+
+EqualUnaligned:
+
+//
+// There are at least 16 bytes to compare, but at least one of the blocks
+// is not word-aligned.
+//
+
+ andi. r.9,r.9,1 // isolate alignment bits
+ bne- EqualByteUnaligned // jump if at least one not halfword aligned
+
+EqualUnalignedLoop:
+
+//
+// Both blocks are halfword-aligned, and there are at least 16 bytes to compare.
+//
+
+ lhz r.6,0(r.3) // Get 1st hword of 1st Source1 wrd
+ lhz r.7,0(r.4) // Get 1st hword of 1st Source2 wrd
+ lhz r.8,2(r.3) // Get 2nd hword of 1st Source1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lhz r.9,2(r.4) // Get 2nd hword of 1st Source2 wrd
+ bne- EqualNotEqual // Check for 1st word equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lhz r.6,4(r.3) // Get 1st hword of 2nd Source1 wrd
+ lhz r.7,4(r.4) // Get 1st hword of 2nd Source2 wrd
+ bne- EqualNotEqual // Check for 1st word equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lhz r.8,6(r.3) // Get 2nd hword of 2nd Source1 wrd
+ lhz r.9,6(r.4) // Get 2nd hword of 2nd Source2 wrd
+ bne- EqualNotEqual // Check for 2nd word equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lhz r.6,8(r.3) // Get 1st hword of 3rd Source1 wrd
+ lhz r.7,8(r.4) // Get 1st hword of 3rd Source2 wrd
+ bne- EqualNotEqual // Check for 2nd word equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lhz r.8,10(r.3) // Get 2nd hword of 3rd Source1 wrd
+ lhz r.9,10(r.4) // Get 2nd hword of 3rd Source2 wrd
+ bne- EqualNotEqual // Check for 3rd word equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lhz r.6,12(r.3) // Get 1st hword of 4th Source1 wrd
+ lhz r.7,12(r.4) // Get 1st hword of 4th Source2 wrd
+ bne- EqualNotEqual // Check for 3rd word equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lhz r.8,14(r.3) // Get 2nd hword of 4th Source1 wrd
+ lhz r.9,14(r.4) // Get 2nd hword of 4th Source2 wrd
+ bne- EqualNotEqual // Check for 4th word equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ addi r.3,r.3,16 // Update Source1 pointer
+ bne- EqualNotEqual // Check for 4th word equal
+ cmpw r.3,r.5 // Check for all blocks done
+ addi r.4,r.4,16 // Update Source2 pointer
+ bne- EqualUnalignedLoop // Jump if more blocks
+
+ sub r.5,r.10,r.3 // compute remaining bytes
+ b EqualByByte // compare rest byte-by-byte
+
+EqualByteUnaligned:
+
+//
+// There are at least 16 bytes to compare, but at least one of the blocks
+// is not halfword-aligned.
+//
+// Because we don't expect very high byte counts in RtlEqualMemory, and
+// we also don't expect unaligned buffers very often, we don't bother
+// with the byte/halfword fetches that RtlCompareMemory does.
+//
+
+ lbz r.6,0(r.3) // Get first byte of 1st Source1 wrd
+ lbz r.7,0(r.4) // Get first byte of 1st Source2 wrd
+ lbz r.8,1(r.3) // Get 2nd byte of 1st Source1 wrd
+ cmpw r.6,r.7 // Check for 1st word equal
+ lbz r.9,1(r.4) // Get 2nd byte of 1st Source2 wrd
+ bne EqualNotEqual // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lbz r.6,2(r.3) // Get first byte of 1st Source1 wrd
+ lbz r.7,2(r.4) // Get first byte of 1st Source2 wrd
+ bne EqualNotEqual // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 1st word equal
+ lbz r.8,3(r.3) // Get 2nd byte of 1st Source1 wrd
+ lbz r.9,3(r.4) // Get 2nd byte of 1st Source2 wrd
+ bne EqualNotEqual // Jump if 1st word not equal
+ cmpw r.8,r.9 // Check for 1st word equal
+ lbz r.6,4(r.3) // Get first byte of 2nd Source1 wrd
+ lbz r.7,4(r.4) // Get first byte of 2nd Source2 wrd
+ bne EqualNotEqual // Jump if 1st word not equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lbz r.8,5(r.3) // Get 2nd byte of 2nd Source1 wrd
+ lbz r.9,5(r.4) // Get 2nd byte of 2nd Source2 wrd
+ bne EqualNotEqual // Jump if 2nd word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lbz r.6,6(r.3) // Get first byte of 2nd Source1 wrd
+ lbz r.7,6(r.4) // Get first byte of 2nd Source2 wrd
+ bne EqualNotEqual // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 2nd word equal
+ lbz r.8,7(r.3) // Get 2nd byte of 2nd Source1 wrd
+ lbz r.9,7(r.4) // Get 2nd byte of 2nd Source2 wrd
+ bne EqualNotEqual // Jump if 2nd word not equal
+ cmpw r.8,r.9 // Check for 2nd word equal
+ lbz r.6,8(r.3) // Get first byte of 3rd Source1 wrd
+ lbz r.7,8(r.4) // Get first byte of 3rd Source2 wrd
+ bne EqualNotEqual // Jump if 2nd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lbz r.8,9(r.3) // Get 2nd byte of 3rd Source1 wrd
+ lbz r.9,9(r.4) // Get 2nd byte of 3rd Source2 wrd
+ bne EqualNotEqual // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lbz r.6,10(r.3) // Get first byte of 3rd Source1 wrd
+ lbz r.7,10(r.4) // Get first byte of 3rd Source2 wrd
+ bne EqualNotEqual // Jump if 3rd word not equal
+ cmpw r.6,r.7 // Check for 3rd word equal
+ lbz r.8,11(r.3) // Get 2nd byte of 3rd Source1 wrd
+ lbz r.9,11(r.4) // Get 2nd byte of 3rd Source2 wrd
+ bne EqualNotEqual // Jump if 3rd word not equal
+ cmpw r.8,r.9 // Check for 3rd word equal
+ lbz r.6,12(r.3) // Get first byte of 4th Source1 wrd
+ lbz r.7,12(r.4) // Get first byte of 4th Source2 wrd
+ bne EqualNotEqual // Jump if 3rd word not equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lbz r.8,13(r.3) // Get 2nd byte of 4th Source1 wrd
+ lbz r.9,13(r.4) // Get 2nd byte of 4th Source2 wrd
+ bne EqualNotEqual // Jump if 4th word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ lbz r.6,14(r.3) // Get first byte of 4th Source1 wrd
+ lbz r.7,14(r.4) // Get first byte of 4th Source2 wrd
+ bne EqualNotEqual // Jump if 4th word not equal
+ cmpw r.6,r.7 // Check for 4th word equal
+ lbz r.8,15(r.3) // Get 2nd byte of 4th Source1 wrd
+ lbz r.9,15(r.4) // Get 2nd byte of 4th Source2 wrd
+ bne EqualNotEqual // Jump if 4th word not equal
+ cmpw r.8,r.9 // Check for 4th word equal
+ addi r.3,r.3,16 // Update Source1 pointer
+ bne EqualNotEqual // Jump if 4th word not equal
+ cmpw r.3,r.5 // Check for all blocks done
+ addi r.4,r.4,16 // Update Source2 pointer
+ bne- EqualByteUnaligned // Jump if more blocks
+
+ sub r.5,r.10,r.3 // compute remaining bytes
+ b EqualByByte // compare rest byte-by-byte
+
+EqualNotEqual:
+
+//
+// The blocks are not equal.
+//
+
+ li r.3, FALSE // indicate blocks are not equal
+
+ LEAF_EXIT(RtlEqualMemory) // return to caller
+
+//++
+//
+// VOID
+// RtlMoveMemory (
+// IN PVOID Destination,
+// IN PVOID Source,
+// IN ULONG Length
+// )
+//
+// Routine Description:
+//
+// This function moves memory either forward or backward, aligned or
+// unaligned, in 32-byte blocks, followed by 4-byte blocks, followed
+// by any remaining bytes.
+//
+// The alternate entry point, RtlCopyMemory, moves non-overlapping
+// blocks only, in the forward direction.
+//
+// RtlCopyMemory32 is the same as RtlCopyMemory but is guaranteed
+// never to copy more than 32 bits at a time. RtlCopyMemory may
+// (probably will) be modified in the future to copy 64 bits at
+// a time.
+//
+// Arguments:
+//
+// DEST (r.3) - Supplies a pointer to the destination address of
+// the move operation.
+//
+// SRC (r.4) - Supplies a pointer to the source address of the move
+// operation.
+//
+// LNGTH (r.5) - Supplies the length, in bytes, of the memory to be
+// moved.
+//
+// Return Value:
+//
+// None.
+//
+//--
+//
+// Define the routine entry point
+//
+ LEAF_ENTRY(RtlMoveMemory)
+//
+// Check to see if destination block overlaps the source block
+// If so, jump to a backward move to preserve source block from
+// being corrupted.
+//
+ cmpw r.4,r.3 // Check to see if DEST > SRC
+ bge+ MoveForward // Jump if no overlap possible
+ add r.10,r.4,r.5 // Get ending SRC address
+ cmpw r.10,r.3 // Check for overlap
+ bgt- MoveBackward // Jump for overlap
+//
+// Move Memory Forward
+//
+// Check alignment
+//
+
+ ALTERNATE_ENTRY(RtlCopyMemory)
+ ALTERNATE_ENTRY(RtlCopyMemory32)
+
+MoveForward:
+ cmpwi r.5,4 // Check for less than 4 bytes
+ blt- FwdMoveByByte // Jump if single byte moves
+ xor r.9,r.4,r.3 // Check for same alignment
+ andi. r.9,r.9,3 // Isolate alignment bits
+ bne- MvFwdUnaligned // Jump if different alignments
+//
+// Move Memory Forward - Same SRC and DEST alignment
+//
+// Load and store extra bytes until a word boundary is reached
+//
+
+MvFwdAligned:
+ andi. r.6,r.3,3 // Check alignment type
+ beq+ FwdBlkDiv // Jump to process 32-Byte blocks
+ cmpwi r.6,3 // Check for 1 byte unaligned
+ bne+ FwdChkFor2 // If not, check next case
+ lbz r.7,0(r.4) // Get unaligned byte
+ li r.6,1 // Set byte move count
+ stb r.7,0(r.3) // Store unaligned byte
+ b UpdateAddrs // Jump to update addresses
+FwdChkFor2:
+ cmpwi r.6,2 // Check for halfword aligned
+ bne+ FwdChkFor1 // If not, check next case
+ lhz r.7,0(r.4) // Get unaligned halfword
+ li r.6,2 // Set byte move count
+ sth r.7,0(r.3) // Store unaligned halfword
+ b UpdateAddrs // Jump to update addresses
+FwdChkFor1:
+ lbz r.8,0(r.4) // Get unaligned byte
+ lhz r.7,1(r.4) // Get unaligned halfword
+ stb r.8,0(r.3) // Store unaligned byte
+ sth r.7,1(r.3) // Store unaligned halfword
+ li r.6,3 // Set byte move count
+UpdateAddrs:
+ sub r.5,r.5,r.6 // Decrement LNGTH by unaligned
+ add r.4,r.4,r.6 // Update the SRC address
+ add r.3,r.3,r.6 // Update the DEST address
+//
+// Divide the block to process into 32-byte blocks
+//
+
+FwdBlkDiv:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMoveBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Move 32-byte blocks
+//
+FwdMvFullBlks:
+ lwz r.6,0(r.4) // Get 1st SRC word
+ lwz r.7,4(r.4) // Get 2nd SRC word
+ stw r.6,0(r.3) // Store 1st DEST word
+ stw r.7,4(r.3) // Store 2nd DEST word
+ lwz r.6,8(r.4) // Get 3rd SRC word
+ lwz r.7,12(r.4) // Get 4th SRC word
+ stw r.6,8(r.3) // Store 3rd DEST word
+ stw r.7,12(r.3) // Store 4th DEST word
+ lwz r.6,16(r.4) // Get 5th SRC word
+ lwz r.7,20(r.4) // Get 6th SRC word
+ stw r.6,16(r.3) // Store 5th DEST word
+ stw r.7,20(r.3) // Store 6th DEST word
+ lwz r.6,24(r.4) // Get 7th SRC word
+ lwz r.7,28(r.4) // Get 8th SRC word
+ addi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ stw r.6,24(r.3) // Store 7th DEST word
+ stw r.7,28(r.3) // Store 8th DEST word
+ addi r.3,r.3,32 // Update DEST pointer
+ bne+ FwdMvFullBlks // Jump if more blocks
+//
+// Move 4-byte blocks
+//
+
+FwdMoveBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+FwdLpOn4Bytes:
+ lwz r.6,0(r.4) // Load next set of 4 bytes
+ addi r.4,r.4,4 // Get pointer to next SRC block
+ cmpw r.4,r.10 // Check for last block
+ stw r.6,0(r.3) // Store next DEST block
+ addi r.3,r.3,4 // Get pointer to next DEST block
+ bne+ FwdLpOn4Bytes // Jump if more blocks
+//
+// Move 1-byte blocks
+//
+
+FwdMoveByByte:
+ cmpwi r.5,0 // Check for no bytes left
+ beqlr+ // Return if done
+ cmpwi r.5,1 // Check for no bytes left
+ lbz r.6,0(r.4) // Get 1st SRC byte
+ stb r.6,0(r.3) // Store 1st DEST byte
+ beqlr+ // Return if done
+ cmpwi r.5,2 // Check for no bytes left
+ lbz r.6,1(r.4) // Get 2nd SRC byte
+ stb r.6,1(r.3) // Store 2nd DEST byte
+ beqlr+ // Return if done
+ lbz r.6,2(r.4) // Get 3rd SRC byte
+ stb r.6,2(r.3) // Store 3rd byte word
+ blr // Return
+//
+// Forward Move - SRC and DEST have different alignments
+//
+
+MvFwdUnaligned:
+ or r.9,r.4,r.3 // Check if either byte unaligned
+ andi. r.9,r.9,3 // Isolate alignment
+ cmpwi r.9,2 // Check for even result
+ bne+ FwdMvByteUnaligned // Jump for byte unaligned
+//
+// Divide the blocks to process into 32-byte blocks
+//
+
+FwdBlkDivUnaligned:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMvHWrdBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Forward Move - SRC or DEST is halfword aligned, the other is by word
+//
+FwdMvByHWord:
+ lhz r.6,0(r.4) // Get 1st 2 bytes of 1st SRC wrd
+ lhz r.7,2(r.4) // Get 2nd 2 bytes of 1st SRC wrd
+ sth r.6,0(r.3) // Put 1st 2 bytes of 1st DST wrd
+ sth r.7,2(r.3) // Put 2nd 2 bytes of 1st DST wrd
+ lhz r.6,4(r.4) // Get 1st 2 bytes of 2nd SRC wrd
+ lhz r.7,6(r.4) // Get 2nd 2 bytes of 2nd SRC wrd
+ sth r.6,4(r.3) // Put 1st 2 bytes of 2nd DST wrd
+ sth r.7,6(r.3) // Put 2nd 2 bytes of 2nd DST wrd
+ lhz r.6,8(r.4) // Get 1st 2 bytes of 3rd SRC wrd
+ lhz r.7,10(r.4) // Get 2nd 2 bytes of 3rd SRC wrd
+ sth r.6,8(r.3) // Put 1st 2 bytes of 3rd DST wrd
+ sth r.7,10(r.3) // Put 2nd 2 bytes of 3rd DST wrd
+ lhz r.6,12(r.4) // Get 1st 2 bytes of 4th SRC wrd
+ lhz r.7,14(r.4) // Get 2nd 2 bytes of 4th SRC wrd
+ sth r.6,12(r.3) // Put 1st 2 bytes of 4th DST wrd
+ sth r.7,14(r.3) // Put 2nd 2 bytes of 4th DST wrd
+ lhz r.6,16(r.4) // Get 1st 2 bytes of 5th SRC wrd
+ lhz r.7,18(r.4) // Get 2nd 2 bytes of 5th SRC wrd
+ sth r.6,16(r.3) // Put 1st 2 bytes of 5th DST wrd
+ sth r.7,18(r.3) // Put 2nd 2 bytes of 5th DST wrd
+ lhz r.6,20(r.4) // Get 1st 2 bytes of 6th SRC wrd
+ lhz r.7,22(r.4) // Get 2nd 2 bytes of 6th SRC wrd
+ sth r.6,20(r.3) // Put 1st 2 bytes of 6th DST wrd
+ sth r.7,22(r.3) // Put 2nd 2 bytes of 6th DST wrd
+ lhz r.6,24(r.4) // Get 1st 2 bytes of 7th SRC wrd
+ lhz r.7,26(r.4) // Get 2nd 2 bytes of 7th SRC wrd
+ sth r.6,24(r.3) // Put 1st 2 bytes of 7th DST wrd
+ sth r.7,26(r.3) // Put 2nd 2 bytes of 7th DST wrd
+ lhz r.6,28(r.4) // Get 1st 2 bytes of 8th SRC wrd
+ lhz r.7,30(r.4) // Get 2nd 2 bytes of 8th SRC wrd
+ addi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ sth r.6,28(r.3) // Put 1st 2 bytes of 8th DST wrd
+ sth r.7,30(r.3) // Put 2nd 2 bytes of 8th DST wrd
+ addi r.3,r.3,32 // Update DEST pointer
+ bne+ FwdMvByHWord // Jump if more blocks
+//
+// Move 4-byte blocks with DEST Halfword unaligned
+//
+
+FwdMvHWrdBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+FwdHWrdLpOn4Bytes:
+ lhz r.6,0(r.4) // Get 1st 2 bytes of 1st SRC wrd
+ lhz r.7,2(r.4) // Get 2nd 2 bytes of 1st SRC wrd
+ addi r.4,r.4,4 // Update SRC pointer
+ cmpw r.4,r.10 // Check for last block
+ sth r.6,0(r.3) // Put 1st 2 bytes of 1st DST wrd
+ sth r.7,2(r.3) // Put 2nd 2 bytes of 1st DST wrd
+ addi r.3,r.3,4 // Update DEST pointer
+ bne+ FwdHWrdLpOn4Bytes // Jump if more blocks
+
+ b FwdMoveByByte // Jump to complete last bytes
+//
+// Forward Move - DEST is byte unaligned - Check SRC
+//
+
+FwdMvByteUnaligned:
+ and r.9,r.3,r.4 // Check for both byte aligned
+ andi. r.9,r.9,1 // Isolate alignment bits
+ beq- FwdBlksByByte // Jump if both not byte aligned
+//
+// Divide the blocks to process into 32-byte blocks
+//
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMvByteBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Forward Move - Both DEST and SRC are byte unaligned, but differently
+//
+
+FwdMvByByte:
+ lbz r.6,0(r.4) // Get first byte of 1st SRC word
+ lhz r.7,1(r.4) // Get mid-h-word of 1st SRC word
+ lhz r.8,3(r.4) // Get h-word crossing 1st/2nd SRC word
+ stb r.6,0(r.3) // Put first byte of 1st DEST word
+ sth r.7,1(r.3) // Put mid-h-word of 1st DEST word
+ sth r.8,3(r.3) // Put h-word crossing 1st/2nd DEST word
+ lhz r.6,5(r.4) // Get mid-h-word of 2nd SRC word
+ lhz r.7,7(r.4) // Get h-word crossing 2nd/3rd SRC word
+ lhz r.8,9(r.4) // Get mid-h-word of 3rd SRC word
+ sth r.6,5(r.3) // Put mid-h-word of 2nd DEST word
+ sth r.7,7(r.3) // Put h-word crossing 2nd/3rd DEST word
+ sth r.8,9(r.3) // Put mid-h-word of 3rd DEST word
+ lhz r.6,11(r.4) // Get h-word crossing 3rd/4th SRC word
+ lhz r.7,13(r.4) // Get mid-h-word of 4th SRC word
+ lhz r.8,15(r.4) // Get h-word crossing 4th/5th SRC word
+ sth r.6,11(r.3) // Put h-word crossing 3rd/4th DEST word
+ sth r.7,13(r.3) // Put mid-h-word of 4th DEST word
+ sth r.8,15(r.3) // Put h-word crossing 4th/5th DEST word
+ lhz r.6,17(r.4) // Get mid-h-word of 5th SRC word
+ lhz r.7,19(r.4) // Get h-word crossing 5th/6th SRC word
+ lhz r.8,21(r.4) // Get mid-h-word of 6th SRC word
+ sth r.6,17(r.3) // Put mid-h-word of 5th DEST word
+ sth r.7,19(r.3) // Put h-word crossing 5th/6th DEST word
+ sth r.8,21(r.3) // Put mid-h-word of 6th DEST word
+ lhz r.6,23(r.4) // Get h-word crossing 6th/7th SRC word
+ lhz r.7,25(r.4) // Get mid-h-word of 7th SRC word
+ lhz r.8,27(r.4) // Get h-word crossing 7th/8th SRC word
+ sth r.6,23(r.3) // Put h-word crossing 6th/7th DEST word
+ sth r.7,25(r.3) // Put mid-h-word of 7th DEST word
+ sth r.8,27(r.3) // Put h-word crossing 7th/8th DEST word
+ lhz r.6,29(r.4) // Get mid-h-word of 8th SRC word
+ lbz r.7,31(r.4) // Get last byte of 8th SRC word
+ addi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ sth r.6,29(r.3) // Put mid-h-word of 8th DEST word
+ stb r.7,31(r.3) // Put last byte of 8th DEST word
+ addi r.3,r.3,32 // Update DEST pointer
+ bne+ FwdMvByByte // Jump if more blocks
+//
+// Move 4-byte blocks with DEST or SRC byte aligned
+//
+
+FwdMvByteBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+FwdByteLpOn4Bytes:
+ lbz r.6,0(r.4) // Get first byte of 1st SRC word
+ lhz r.7,1(r.4) // Get mid-h-word of 1st SRC word
+ lbz r.8,3(r.4) // Get last byte of 1st SRC word
+ stb r.6,0(r.3) // Put first byte of 1st DEST wd
+ addi r.4,r.4,4 // Update SRC pointer
+ cmpw r.4,r.10 // Check for last block
+ sth r.7,1(r.3) // Put mid-h-word of 1st DEST wd
+ stb r.8,3(r.3) // Put last byte of 1st DEST wrd
+ addi r.3,r.3,4 // Update DEST pointer
+ bne+ FwdByteLpOn4Bytes // Jump if more blocks
+
+ b FwdMoveByByte // Jump to complete last bytes
+//
+// Forward Move - Either SRC or DEST are byte unaligned but not both
+//
+// Divide the blocks to process into 32-byte blocks
+//
+
+FwdBlksByByte:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMvBlksOf4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+
+FwdMvBlksByByte:
+ lbz r.6,0(r.4) // Get first byte of 1st SRC wrd
+ lbz r.7,1(r.4) // Get second byte of 1st SRC wrd
+ stb r.6,0(r.3) // Put first byte of 1st DEST wrd
+ stb r.7,1(r.3) // Put second byte of 1st DST wrd
+ lbz r.6,2(r.4) // Get third byte of 1st SRC wrd
+ lbz r.7,3(r.4) // Get fourth byte of 1st SRC wrd
+ stb r.6,2(r.3) // Put third byte of 1st DEST wrd
+ stb r.7,3(r.3) // Put fourth byte of 1st DST wrd
+ lbz r.6,4(r.4) // Get first byte of 2nd SRC wrd
+ lbz r.7,5(r.4) // Get 2nd byte of 2nd SRC wrd
+ stb r.6,4(r.3) // Put first byte of 2nd DEST wrd
+ stb r.7,5(r.3) // Put second byte of 2nd DST wrd
+ lbz r.6,6(r.4) // Get third byte of 2nd SRC wrd
+ lbz r.7,7(r.4) // Get fourth byte of 2nd SRC wrd
+ stb r.6,6(r.3) // Put third byte of 2nd DEST wrd
+ stb r.7,7(r.3) // Put fourth byte of 2nd DST wrd
+ lbz r.6,8(r.4) // Get first byte of 3rd SRC wrd
+ lbz r.7,9(r.4) // Get second byte of 3rd SRC wrd
+ stb r.6,8(r.3) // Put first byte of 3rd DEST wrd
+ stb r.7,9(r.3) // Put second byte of 3rd DST wrd
+ lbz r.6,10(r.4) // Get third byte of 3rd SRC wrd
+ lbz r.7,11(r.4) // Get fourth byte of 3rd SRC wrd
+ stb r.6,10(r.3) // Put third byte of 3rd DEST wrd
+ stb r.7,11(r.3) // Put fourth byte of 3rd DST wrd
+ lbz r.6,12(r.4) // Get first byte of 4th SRC wrd
+ lbz r.7,13(r.4) // Get second byte of 4th SRC wrd
+ stb r.6,12(r.3) // Put first byte of 4th DEST wrd
+ stb r.7,13(r.3) // Put second byte of 4th DST wrd
+ lbz r.6,14(r.4) // Get third byte of 4th SRC wrd
+ lbz r.7,15(r.4) // Get fourth byte of 4th SRC wrd
+ stb r.6,14(r.3) // Put third byte of 4th DEST wrd
+ stb r.7,15(r.3) // Put fourth byte of 4th DST wrd
+ lbz r.6,16(r.4) // Get first byte of 5th SRC wrd
+ lbz r.7,17(r.4) // Get second byte of 5th SRC wrd
+ stb r.6,16(r.3) // Put first byte of 5th DEST wrd
+ stb r.7,17(r.3) // Put second byte of 5th DST wrd
+ lbz r.6,18(r.4) // Get third byte of 5th SRC wrd
+ lbz r.7,19(r.4) // Get fourth byte of 5th SRC wrd
+ stb r.6,18(r.3) // Put third byte of 5th DEST wrd
+ stb r.7,19(r.3) // Put fourth byte of 5th DST wrd
+ lbz r.6,20(r.4) // Get first byte of 6th SRC wrd
+ lbz r.7,21(r.4) // Get second byte of 6th SRC wrd
+ stb r.6,20(r.3) // Put first byte of 6th DEST wrd
+ stb r.7,21(r.3) // Put second byte of 6th DST wrd
+ lbz r.6,22(r.4) // Get third byte of 6th SRC wrd
+ lbz r.7,23(r.4) // Get fourth byte of 6th SRC wrd
+ stb r.6,22(r.3) // Put third byte of 6th DEST wrd
+ stb r.7,23(r.3) // Put fourth byte of 6th DST wrd
+ lbz r.6,24(r.4) // Get first byte of 7th SRC wrd
+ lbz r.7,25(r.4) // Get second byte of 7th SRC wrd
+ stb r.6,24(r.3) // Put first byte of 7th DEST wrd
+ stb r.7,25(r.3) // Put second byte of 7th DST wrd
+ lbz r.6,26(r.4) // Get third byte of 7th SRC wrd
+ lbz r.7,27(r.4) // Get fourth byte of 7th SRC wrd
+ stb r.6,26(r.3) // Put third byte of 7th DEST wrd
+ stb r.7,27(r.3) // Put fourth byte of 7th DST wrd
+ lbz r.6,28(r.4) // Get first byte of 8th SRC wrd
+ lbz r.7,29(r.4) // Get second byte of 8th SRC wrd
+ stb r.6,28(r.3) // Put first byte of 8th DEST wrd
+ stb r.7,29(r.3) // Put second byte of 8th DST wrd
+ lbz r.6,30(r.4) // Get third byte of 8th SRC wrd
+ lbz r.7,31(r.4) // Get fourth byte of 8th SRC wrd
+ addi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ stb r.6,30(r.3) // Put third byte of 8th DEST wrd
+ stb r.7,31(r.3) // Put fourth byte of 8th DST wrd
+ addi r.3,r.3,32 // Update DEST pointer
+ bne+ FwdMvBlksByByte // Jump if more blocks
+//
+// Move 4-byte blocks with DEST or SRC Byte aligned
+//
+
+FwdMvBlksOf4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ add r.10,r.4,r.7 // Get address of last full block
+ beq- FwdMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+FwdBlksLpOn4Bytes:
+ lbz r.6,0(r.4) // Get first byte of 1st SRC wrd
+ lbz r.7,1(r.4) // Get second byte of 1st SRC wrd
+ stb r.6,0(r.3) // Put first byte of 1st DEST wrd
+ stb r.7,1(r.3) // Put second byte of 1st DST wrd
+ lbz r.6,2(r.4) // Get third byte of 1st SRC wrd
+ lbz r.7,3(r.4) // Get fourth byte of 1st SRC wrd
+ addi r.4,r.4,4 // Update SRC pointer
+ cmpw r.4,r.10 // Check for last block
+ stb r.6,2(r.3) // Put third byte of 1st DEST wrd
+ stb r.7,3(r.3) // Put fourth byte of 1st DST wrd
+ addi r.3,r.3,4 // Update DEST pointer
+ bne+ FwdBlksLpOn4Bytes // Jump if more blocks
+
+ b FwdMoveByByte // Jump to complete last bytes
+//
+// Move Memory Backward
+//
+// Check alignment
+//
+
+MoveBackward:
+ add r.4,r.4,r.5 // Compute ending SRC address
+ add r.3,r.3,r.5 // Compute ending DEST address
+ cmpwi r.5,4 // Check for less than 4 bytes
+ blt- BckMoveByByte // Jump if single byte moves
+ xor r.9,r.4,r.3 // Check for same alignment
+ andi. r.9,r.9,3 // Isolate alignment bits
+ bne- MvBckUnaligned // Jump if different alignments
+//
+// Move Memory Backword - Same SRC and DEST alignment
+//
+// Load and store extra bytes until a word boundary is reached
+//
+
+MvBckAligned:
+ andi. r.6,r.3,3 // Check alignment type
+ beq+ BckBlkDiv // Jump to process 32-Byte blocks
+ cmpwi r.6,1 // Check for 1 byte unaligned
+ bne+ BckChkFor2 // If not, check next case
+ lbz r.7,-1(r.4) // Get unaligned byte
+ sub r.5,r.5,r.6 // Decrement LNGTH by unaligned
+ stb r.7,-1(r.3) // Store unaligned byte
+ b BckUpdateAddrs // Jump to update addresses
+BckChkFor2:
+ cmpwi r.6,2 // Check for halfword aligned
+ bne+ BckChkFor3 // If not, check next case
+ lhz r.7,-2(r.4) // Get unaligned halfword
+ sub r.5,r.5,r.6 // Decrement LNGTH by unaligned
+ sth r.7,-2(r.3) // Store unaligned halfword
+ b BckUpdateAddrs // Jump to update addresses
+BckChkFor3:
+ lbz r.8,-1(r.4) // Get unaligned byte
+ lhz r.7,-3(r.4) // Get unaligned halfword
+ stb r.8,-1(r.3) // Store unaligned byte
+ sth r.7,-3(r.3) // Store unaligned halfword
+ sub r.5,r.5,r.6 // Decrement LNGTH by unaligned
+BckUpdateAddrs:
+ sub r.4,r.4,r.6 // Update the SRC address
+ sub r.3,r.3,r.6 // Update the DEST address
+//
+// Divide the block to process into 32-byte blocks
+//
+
+BckBlkDiv:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMoveBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Move 32-byte blocks
+//
+BckMvFullBlks:
+ lwz r.6,-4(r.4) // Get 1st SRC word
+ lwz r.7,-8(r.4) // Get 2nd SRC word
+ stw r.6,-4(r.3) // Store 1st DEST word
+ stw r.7,-8(r.3) // Store 2nd DEST word
+ lwz r.6,-12(r.4) // Get 3rd SRC word
+ lwz r.7,-16(r.4) // Get 4th SRC word
+ stw r.6,-12(r.3) // Store 3rd DEST word
+ stw r.7,-16(r.3) // Store 4th DEST word
+ lwz r.6,-20(r.4) // Get 5th SRC word
+ lwz r.7,-24(r.4) // Get 6th SRC word
+ stw r.6,-20(r.3) // Store 5th DEST word
+ stw r.7,-24(r.3) // Store 6th DEST word
+ lwz r.6,-28(r.4) // Get 7th SRC word
+ lwz r.7,-32(r.4) // Get 8th SRC word
+ subi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ stw r.6,-28(r.3) // Store 7th DEST word
+ stw r.7,-32(r.3) // Store 8th DEST word
+ subi r.3,r.3,32 // Update DEST pointer
+ bne+ BckMvFullBlks // Jump if more blocks
+//
+// Move 4-byte blocks
+//
+
+BckMoveBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+BckLpOn4Bytes:
+ lwz r.6,-4(r.4) // Load next set of 4 bytes
+ subi r.4,r.4,4 // Get pointer to next SRC block
+ cmpw r.4,r.10 // Check for last block
+ stw r.6,-4(r.3) // Store next DEST block
+ subi r.3,r.3,4 // Get pointer to next DEST block
+ bne+ BckLpOn4Bytes // Jump if more blocks
+//
+// Move 1-byte blocks
+//
+
+BckMoveByByte:
+ cmpwi r.5,0 // Check for no bytes left
+ beqlr+ // Return if done
+ lbz r.6,-1(r.4) // Get 1st SRC byte
+ cmpwi r.5,1 // Check for no bytes left
+ stb r.6,-1(r.3) // Store 1st DEST byte
+ beqlr+ // Return if done
+ lbz r.6,-2(r.4) // Get 2nd SRC byte
+ cmpwi r.5,2 // Check for no bytes left
+ stb r.6,-2(r.3) // Store 2nd DEST byte
+ beqlr+ // Return if done
+ lbz r.6,-3(r.4) // Get 3rd SRC byte
+ stb r.6,-3(r.3) // Store 3rd byte word
+ blr // Return
+//
+// Backward Move - SRC and DEST have different alignments
+//
+
+MvBckUnaligned:
+ or r.9,r.4,r.3 // Check for either byte unaligned
+ andi. r.9,r.9,3 // Isolate alignment
+ cmpwi r.9,2 // Check for even result
+ bne+ BckMvByteUnaligned // Jump for byte unaligned
+//
+// Divide the blocks to process into 32-byte blocks
+//
+
+BckBlkDivUnaligned:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMvHWrdBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Backward Move - SRC or DEST is halfword aligned, the other is by word
+//
+BckMvByHWord:
+ lhz r.6,-2(r.4) // Get 1st 2 bytes of 1st SRC wrd
+ lhz r.7,-4(r.4) // Get 2nd 2 bytes of 1st SRC wrd
+ sth r.6,-2(r.3) // Put 1st 2 bytes of 1st DST wrd
+ sth r.7,-4(r.3) // Put 2nd 2 bytes of 1st DST wrd
+ lhz r.6,-6(r.4) // Get 1st 2 bytes of 2nd SRC wrd
+ lhz r.7,-8(r.4) // Get 2nd 2 bytes of 2nd SRC wrd
+ sth r.6,-6(r.3) // Put 1st 2 bytes of 2nd DST wrd
+ sth r.7,-8(r.3) // Put 2nd 2 bytes of 2nd DST wrd
+ lhz r.6,-10(r.4) // Get 1st 2 bytes of 3rd SRC wrd
+ lhz r.7,-12(r.4) // Get 2nd 2 bytes of 3rd SRC wrd
+ sth r.6,-10(r.3) // Put 1st 2 bytes of 3rd DST wrd
+ sth r.7,-12(r.3) // Put 2nd 2 bytes of 3rd DST wrd
+ lhz r.6,-14(r.4) // Get 1st 2 bytes of 4th SRC wrd
+ lhz r.7,-16(r.4) // Get 2nd 2 bytes of 4th SRC wrd
+ sth r.6,-14(r.3) // Put 1st 2 bytes of 4th DST wrd
+ sth r.7,-16(r.3) // Put 2nd 2 bytes of 4th DST wrd
+ lhz r.6,-18(r.4) // Get 1st 2 bytes of 5th SRC wrd
+ lhz r.7,-20(r.4) // Get 2nd 2 bytes of 5th SRC wrd
+ sth r.6,-18(r.3) // Put 1st 2 bytes of 5th DST wrd
+ sth r.7,-20(r.3) // Put 2nd 2 bytes of 5th DST wrd
+ lhz r.6,-22(r.4) // Get 1st 2 bytes of 6th SRC wrd
+ lhz r.7,-24(r.4) // Get 2nd 2 bytes of 6th SRC wrd
+ sth r.6,-22(r.3) // Put 1st 2 bytes of 6th DST wrd
+ sth r.7,-24(r.3) // Put 2nd 2 bytes of 6th DST wrd
+ lhz r.6,-26(r.4) // Get 1st 2 bytes of 7th SRC wrd
+ lhz r.7,-28(r.4) // Get 2nd 2 bytes of 7th SRC wrd
+ sth r.6,-26(r.3) // Put 1st 2 bytes of 7th DST wrd
+ sth r.7,-28(r.3) // Put 2nd 2 bytes of 7th DST wrd
+ lhz r.6,-30(r.4) // Get 1st 2 bytes of 8th SRC wrd
+ lhz r.7,-32(r.4) // Get 2nd 2 bytes of 8th SRC wrd
+ subi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ sth r.6,-30(r.3) // Put 1st 2 bytes of 8th DST wrd
+ sth r.7,-32(r.3) // Put 2nd 2 bytes of 8th DST wrd
+ subi r.3,r.3,32 // Update DEST pointer
+ bne+ BckMvByHWord // Jump if more blocks
+//
+// Move 4-byte blocks with DEST Halfword unaligned
+//
+
+BckMvHWrdBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+BckHWrdLpOn4Bytes:
+ lhz r.6,-2(r.4) // Get 1st 2 bytes of 1st SRC wrd
+ lhz r.7,-4(r.4) // Get 2nd 2 bytes of 1st SRC wrd
+ subi r.4,r.4,4 // Update SRC pointer
+ cmpw r.4,r.10 // Check for last block
+ sth r.6,-2(r.3) // Put 1st 2 bytes of 1st DST wrd
+ sth r.7,-4(r.3) // Put 2nd 2 bytes of 1st DST wrd
+ subi r.3,r.3,4 // Update DEST pointer
+ bne+ BckHWrdLpOn4Bytes // Jump if more blocks
+
+ b BckMoveByByte // Jump to complete last bytes
+//
+// Check for both byte unaligned
+//
+
+BckMvByteUnaligned:
+ and r.9,r.3,r.4 // Check for both byte aligned
+ and r.9,r.9,1 // Isolate alignment bits
+ bne- BckBlksByByte // Jump if both not byte aligned
+//
+// Divide the blocks to process into 32-byte blocks
+//
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMvByteBy4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+//
+// Backward Move - Both SRC and DEST are byte unaligned, but differently
+//
+
+BckMvByByte:
+ lbz r.6,-1(r.4) // Get first byte of 1st SRC word
+ lhz r.7,-3(r.4) // Get mid-h-word of 1st SRC word
+ lhz r.8,-5(r.4) // Get h-word crossing 1st/2nd SRC word
+ stb r.6,-1(r.3) // Put first byte of 1st DEST word
+ sth r.7,-3(r.3) // Put mid-h-word of 1st DEST word
+ sth r.8,-5(r.3) // Put h-word crossing 1st/2nd DEST word
+ lhz r.6,-7(r.4) // Get mid-h-word of 2nd SRC word
+ lhz r.7,-9(r.4) // Get h-word crossing 2nd/3rd SRC word
+ lhz r.8,-11(r.4) // Get mid-h-word of 3rd SRC word
+ sth r.6,-7(r.3) // Put mid-h-word of 2nd DEST word
+ sth r.7,-9(r.3) // Put h-word crossing 2nd/3rd DEST word
+ sth r.8,-11(r.3) // Put mid-h-word of 3rd DEST word
+ lhz r.6,-13(r.4) // Get h-word crossing 3rd/4th SRC word
+ lhz r.7,-15(r.4) // Get mid-h-word of 4th SRC word
+ lhz r.8,-17(r.4) // Get h-word crossing 4th/5th SRC word
+ sth r.6,-13(r.3) // Put h-word crossing 3rd/4th DEST word
+ sth r.7,-15(r.3) // Put mid-h-word of 4th DEST word
+ sth r.8,-17(r.3) // Put h-word crossing 4th/5th DEST word
+ lhz r.6,-19(r.4) // Get mid-h-word of 5th SRC word
+ lhz r.7,-21(r.4) // Get h-word crossing 5th/6th SRC word
+ lhz r.8,-23(r.4) // Get mid-h-word of 6th SRC word
+ sth r.6,-19(r.3) // Put mid-h-word of 5th DEST word
+ sth r.7,-21(r.3) // Put h-word crossing 5th/6th DEST word
+ sth r.8,-23(r.3) // Put mid-h-word of 6th DEST word
+ lhz r.6,-25(r.4) // Get h-word crossing 6th/7th SRC word
+ lhz r.7,-27(r.4) // Get mid-h-word of 7th SRC word
+ lhz r.8,-29(r.4) // Get h-word crossing 7th/8th SRC word
+ sth r.6,-25(r.3) // Put h-word crossing 6th/7th DEST word
+ sth r.7,-27(r.3) // Put mid-h-word of 7th DEST word
+ sth r.8,-29(r.3) // Put h-word crossing 7th/8th DEST word
+ lhz r.6,-31(r.4) // Get mid-h-word of 8th SRC word
+ lbz r.7,-32(r.4) // Get last byte of 8th SRC word
+ subi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ sth r.7,-31(r.3) // Put mid-h-word of 8th DEST wd
+ stb r.8,-32(r.3) // Put last byte of 8th DEST wrd
+ subi r.3,r.3,32 // Update DEST pointer
+ bne+ BckMvByByte // Jump if more blocks
+//
+// Move 4-byte blocks with DEST and SRC Byte aligned
+//
+
+BckMvByteBy4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+BckByteLpOn4Bytes:
+ lbz r.6,-1(r.4) // Get first byte of 1st SRC word
+ lhz r.7,-3(r.4) // Get mid-h-word of 1st SRC word
+ lbz r.8,-4(r.4) // Get last byte of 1st SRC word
+ stb r.6,-1(r.3) // Put first byte of 1st DEST wd
+ subi r.4,r.4,4 // Update SRC pointer
+ cmpw r.4,r.10 // Check for last block
+ sth r.7,-3(r.3) // Put mid-h-word of 1st DEST wd
+ stb r.8,-4(r.3) // Put last byte of 1st DEST wrd
+ subi r.3,r.3,4 // Update DEST pointer
+ bne+ BckByteLpOn4Bytes // Jump if more blocks
+
+ b BckMoveByByte // Jump to complete last bytes
+//
+// Backward Move - Either DEST or SRC byte unaligned but not both
+//
+// Divide the blocks to process into 32-byte blocks
+//
+
+BckBlksByByte:
+ andi. r.6,r.5,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.5,r.6 // Get full block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMvBlksOf4Bytes // Jump if no full blocks
+ mr r.5,r.6 // Set Length = remainder
+
+BckMvBlksByByte:
+ lbz r.6,-1(r.4) // Get first byte of 1st SRC wrd
+ lbz r.7,-2(r.4) // Get second byte of 1st SRC wrd
+ stb r.6,-1(r.3) // Put first byte of 1st DEST wrd
+ stb r.7,-2(r.3) // Put second byte of 1st DST wrd
+ lbz r.6,-3(r.4) // Get third byte of 1st SRC wrd
+ lbz r.7,-4(r.4) // Get fourth byte of 1st SRC wrd
+ stb r.6,-3(r.3) // Put third byte of 1st DEST wrd
+ stb r.7,-4(r.3) // Put fourth byte of 1st DST wrd
+ lbz r.6,-5(r.4) // Get first byte of 2nd SRC wrd
+ lbz r.7,-6(r.4) // Get second byte of 2nd SRC wrd
+ stb r.6,-5(r.3) // Put first byte of 2nd DEST wrd
+ stb r.7,-6(r.3) // Put second byte of 2nd DST wrd
+ lbz r.6,-7(r.4) // Get third byte of 2nd SRC wrd
+ lbz r.7,-8(r.4) // Get fourth byte of 2nd SRC wrd
+ stb r.6,-7(r.3) // Put third byte of 2nd DEST wrd
+ stb r.7,-8(r.3) // Put fourth byte of 2nd DST wrd
+ lbz r.6,-9(r.4) // Get first byte of 3rd SRC wrd
+ lbz r.7,-10(r.4) // Get second byte of 3rd SRC wrd
+ stb r.6,-9(r.3) // Put first byte of 3rd DST wrd
+ stb r.7,-10(r.3) // Put second byte of 3rd DST wrd
+ lbz r.6,-11(r.4) // Get third byte of 3rd SRC wrd
+ lbz r.7,-12(r.4) // Get fourth byte of 3rd SRC wrd
+ stb r.6,-11(r.3) // Put third byte of 3rd DEST wrd
+ stb r.7,-12(r.3) // Put fourth byte of 3rd DST wrd
+ lbz r.6,-13(r.4) // Get first byte of 4th SRC wrd
+ lbz r.7,-14(r.4) // Get second byte of 4th SRC wrd
+ stb r.6,-13(r.3) // Put first byte of 4th DEST wrd
+ stb r.7,-14(r.3) // Put second byte of 4th DST wrd
+ lbz r.6,-15(r.4) // Get third byte of 4th SRC wrd
+ lbz r.7,-16(r.4) // Get fourth byte of 4th SRC wrd
+ stb r.6,-15(r.3) // Put third byte of 4th DEST wrd
+ stb r.7,-16(r.3) // Put fourth byte of 4th DST wrd
+ lbz r.6,-17(r.4) // Get first byte of 5th SRC wrd
+ lbz r.7,-18(r.4) // Get second byte of 5th SRC wrd
+ stb r.6,-17(r.3) // Put first byte of 5th DEST wrd
+ stb r.7,-18(r.3) // Put second byte of 5th DST wrd
+ lbz r.6,-19(r.4) // Get third byte of 5th SRC wrd
+ lbz r.7,-20(r.4) // Get fourth byte of 5th SRC wrd
+ stb r.6,-19(r.3) // Put third byte of 5th DEST wrd
+ stb r.7,-20(r.3) // Put fourth byte of 5th DST wrd
+ lbz r.6,-21(r.4) // Get first byte of 6th SRC wrd
+ lbz r.7,-22(r.4) // Get second byte of 6th SRC wrd
+ stb r.6,-21(r.3) // Put first byte of 6th DEST wrd
+ stb r.7,-22(r.3) // Put second byte of 6th DST wrd
+ lbz r.6,-23(r.4) // Get third byte of 6th SRC wrd
+ lbz r.7,-24(r.4) // Get fourth byte of 6th SRC wrd
+ stb r.6,-23(r.3) // Put third byte of 6th DEST wrd
+ stb r.7,-24(r.3) // Put fourth byte of 6th DST wrd
+ lbz r.6,-25(r.4) // Get first byte of 7th SRC wrd
+ lbz r.7,-26(r.4) // Get second byte of 7th SRC wrd
+ stb r.6,-25(r.3) // Put first byte of 7th DEST wrd
+ stb r.7,-26(r.3) // Put second byte of 7th DST wrd
+ lbz r.6,-27(r.4) // Get third byte of 7th SRC wrd
+ lbz r.7,-28(r.4) // Get fourth byte of 7th SRC wrd
+ stb r.6,-27(r.3) // Put third byte of 7th DEST wrd
+ stb r.7,-28(r.3) // Put fourth byte of 7th DST wrd
+ lbz r.6,-29(r.4) // Get first byte of 8th SRC wrd
+ lbz r.7,-30(r.4) // Get second byte of 8th SRC wrd
+ stb r.6,-29(r.3) // Put first byte of 8th DEST wrd
+ stb r.7,-30(r.3) // Put second byte of 8th DST wrd
+ lbz r.6,-31(r.4) // Get third byte of 8th SRC wrd
+ lbz r.7,-32(r.4) // Get fourth byte of 8th SRC wrd
+ subi r.4,r.4,32 // Update SRC pointer
+ cmpw r.4,r.10 // Check for all blocks done
+ stb r.6,-31(r.3) // Put third byte of 8th DEST wrd
+ stb r.7,-32(r.3) // Put fourth byte of 8th DST wrd
+ subi r.3,r.3,32 // Update DEST pointer
+ bne+ BckMvBlksByByte // Jump if more blocks
+//
+// Move 4-byte blocks with DEST or SRC Byte aligned, but not the other
+//
+
+BckMvBlksOf4Bytes:
+ andi. r.6,r.5,4-1 // Isolate remainder of LNGTH/4
+ sub. r.7,r.5,r.6 // Get 4-byte block count
+ sub r.10,r.4,r.7 // Get address of last full block
+ beq- BckMoveByByte // Jump if no 4-byte blocks
+ mr r.5,r.6 // Set Length = remainder
+
+BckBlksLpOn4Bytes:
+ lbz r.6,-1(r.4) // Get first byte of 1st SRC wrd
+ lbz r.7,-2(r.4) // Get second byte of 1st SRC wrd
+ stb r.6,-1(r.3) // Put first byte of 1st DEST wrd
+ stb r.7,-2(r.3) // Put second byte of 1st DST wrd
+ lbz r.6,-3(r.4) // Get third byte of 1st SRC wrd
+ lbz r.7,-4(r.4) // Get fourth byte of 1st SRC wrd
+ subi r.4,r.4,4 // Update SRC pointer
+ cmpw r.4,r.10 // Check for last block
+ stb r.6,-3(r.3) // Put third byte of 1st DEST wrd
+ stb r.7,-4(r.3) // Put fourth byte of 1st DST wrd
+ subi r.3,r.3,4 // Update DEST pointer
+ bne+ BckBlksLpOn4Bytes // Jump if more blocks
+
+ b BckMoveByByte // Jump to complete last bytes
+//
+// Exit the routine
+//
+MvExit:
+ LEAF_EXIT(RtlMoveMemory)
+
+//++
+//
+// VOID
+// 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:
+//
+// DEST (r.3) - Supplies a pointer to the memory to zero.
+//
+// LENGTH (r.4) - Supplies the length, in bytes, of the memory to be
+// zeroed.
+//
+// Return Value:
+//
+// None.
+//
+//--
+//
+// Define the entry point
+//
+ LEAF_ENTRY(RtlZeroMemory)
+//
+// Fill Memory with the zeros
+//
+// Zero extra bytes until a word boundary is reached
+//
+ cmpwi cr.1,r.4,4 // Check for less than 3 bytes
+ mtcrf 0x01,r.3 // Check alignment type
+ li r.5,0 // Set pattern as 0
+ blt- cr.1,ZeroByByte // Jump to handle small cases
+ li r.6,1
+ZeroMem:
+ bt 31,ZeroOdd // Branch if 1 or 3
+ bf 30,ZBlkDiv // Branch if not 2
+ sth r.5,0(r.3) // Store unaligned halfword
+ li r.6,2
+ b ZUpdteAddr // Jump to update addresses
+ZeroOdd:
+ bt 30,Zero1 // Branch if align 3
+ sth r.5,1(r.3) // Store unaligned halfword
+ li r.6,3
+Zero1:
+ stb r.5,0(r.3) // Store unaligned byte
+ZUpdteAddr:
+ sub r.4,r.4,r.6 // Decrement LENGTH by unaligned
+ add r.3,r.3,r.6 // Update the DEST address
+//
+// Divide the block to process into 32-byte blocks
+//
+ZBlkDiv:
+ andi. r.6,r.4,BLKLN-1 // Isolate remainder of LENGTH/32
+ sub. r.7,r.4,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- ZeroBy4Bytes // Jump if no full blocks
+ mr r.4,r.6 // Set Length = Remainder
+//
+// Zero 32-Byte Blocks
+//
+// Check for 32-Byte Boundary, if so use the cache zero
+//
+ andi. r.9,r.3,31 // Check for cache boundary
+ li r.6,0 // Set offset=0
+ beq+ BlkZeroC // Jump if on cache boundary
+//
+// If not 32-byte boundary, fill to 32-bit boundary then use cache zero
+//
+ srwi r.8,r.7,5 // Get block count
+ cmpwi r.8,1 // Check for single block
+ mr r.12,r.9 // Save offset value
+ li r11,32 // Get full block count
+ sub r9,r.11,r.9 // Get distance to cache boundary
+ beq- BlkZero // Jump if single block
+//
+// Adjust pointers and loop counts
+//
+ sub. r.8,r.4,r.9 // TMP=Remainder-Unaligned Count
+ add r.10,r.10,r.9 // Get new end pointer
+ mr r.4,r.8 // Set new remainder count (TMP)
+ bge+ AlignToCache // Jump if TMP >= 0
+ sub r.10,r.10,r.9 // Subtract previous increment
+ add r4,r.11,r8 // Get new rem cnt (32-abs(TMP))
+ sub r.10,r.10,r.12 // Get new end pointer
+//
+// Fill to 32-byte boundary - Using 4-byte blocks
+//
+AlignToCache:
+ andi. r.8,r.9,3 // Isolate remainder of LENGTH/4
+ sub. r.9,r.9,r.8 // Get full word byte count
+ li r.7,4 // Initialize loop decrement
+ beq- ByteAlignToCache // Jump if no full blocks
+//
+Align4Bytes:
+ stw r.5,0(r.3)
+ sub. r.9,r.9,r.7 // Increment the loop counter
+ addi r.3,r.3,4 // Increment the DEST address
+ bne+ Align4Bytes // Jump if more 4-Byte Blk fills
+//
+// Align to cache boundary using 1-Byte Blocks
+//
+ByteAlignToCache:
+ cmpwi r.8,0 // Check for completion
+ add r.3,r.3,r.8 // Update DEST address
+ beq+ BlkZeroC // Jump if cache aligned
+//
+ cmpwi r.8,1 // Check for done
+ stb r.5,0(r.3) // Zero 1 byte
+ beq+ BlkZeroC // Jump if done
+ cmpwi r.8,2 // Check cache aligned
+ stb r.5,1(r.3) // Zero 1 byte
+ beq+ BlkZeroC // Jump cache aligned
+ stb r.5,2(r.3) // Zero 1 Byte
+//
+// Zero using the cache
+//
+BlkZeroC:
+#if 0 // BLDR_KERNEL_RUNTIME != 1
+//
+// In order to allow us to boot in write-through or cache-inhibited
+// mode, the boot loader does not use dcbz.
+//
+
+ dcbz r.6,r.3 // Zero 32-byte cache block
+ addi r.3,r.3,32 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ BlkZeroC // Jump if more 32-Byte Blk fills
+ b ZeroBy4Bytes // Jump to finish
+#endif
+//
+// Zero using normal stores
+//
+BlkZero:
+ stw r.5,0(r.3) // Store the 1st DEST word
+ stw r.5,4(r.3) // Store the 2nd DEST word
+ stw r.5,8(r.3) // Store the 3rd DEST word
+ stw r.5,12(r.3) // Store the 4th DEST word
+ stw r.5,16(r.3) // Store the 5th DEST word
+ stw r.5,20(r.3) // Store the 6th DEST word
+ stw r.5,24(r.3) // Store the 7th DEST word
+ stw r.5,28(r.3) // Store the 8th DEST word
+ addi r.3,r.3,32 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ BlkZero // Jump if more 32-Byte Blk fills
+//
+// Zero 4-Byte Blocks
+//
+ZeroBy4Bytes:
+ andi. r.6,r.4,3 // Isolate remainder of LENGTH/4
+ sub. r.7,r.4,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- ZeroByByte // Jump if no full blocks
+ mr r.4,r.6 // Set Length = Remainder
+//
+Zero4Bytes:
+ stw r.5,0(r.3)
+ addi r.3,r.3,4 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ Zero4Bytes // Jump if more 4-Byte Blk fills
+//
+// Zero 1-Byte Blocks
+//
+ZeroByByte:
+ cmpwi r.4,0 // Check for completion
+ beqlr+ // Return if done
+//
+Zero1Byte:
+ cmpwi r.4,1 // Check for done
+ stb r.5,0(r.3) // Zero 1 byte
+ beqlr+ // Return if done
+ cmpwi r.4,2 // Check for done
+ stb r.5,1(r.3) // Zero 1 byte
+ beqlr+ // Return if done
+ stb r.5,2(r.3) // Zero 1 Byte
+//
+// Exit
+//
+ZeroExit:
+ LEAF_EXIT(RtlZeroMemory)
+//
+//++
+//
+// VOID
+// RtlFillMemory (
+// IN PVOID Destination,
+// IN ULONG Length,
+// IN UCHAR Fill
+// )
+//
+// Routine Description:
+//
+// This function fills memory by first aligning the destination
+// address to a longword boundary, and then filling 32-byte blocks,
+// followed by 4-byte blocks, followed by any remaining bytes.
+//
+// Arguments:
+//
+// DEST (r.3) - Supplies a pointer to the memory to fill.
+//
+// LENGTH (r.4) - Supplies the length, in bytes, of the memory to be
+// filled.
+//
+// PTTRN (r.5) - Supplies the fill byte.
+//
+// Return Value:
+//
+// None.
+//
+//--
+//
+// Define the entry point
+//
+ LEAF_ENTRY(RtlFillMemory)
+
+ cmpwi cr.1,r.4,4 // Check for less than 4 bytes
+
+//
+// Initialize a register with the fill byte duplicated
+//
+
+ rlwimi r.5,r.5,8,0x0000ff00 // propogate rightmost byte
+ rlwimi. r.5,r.5,16,0xffff0000 // thru upper 3 bytes
+
+//
+// Fill Memory with the pattern
+//
+//
+// Fill extra bytes until a word boundary is reached
+//
+
+ mtcrf 0x01,r.3 // Check alignment type
+ blt- cr.1,FillByByte // Jump to handle small cases
+ li r.6,1 // Default unaligned count to 1 byte
+ beq- ZeroMem // Use RtlZeroMemory if fill 0
+ bt 31,FillOdd // Branch if align 1 or 3
+ bf 30,BlkDiv // Branch if not 2
+ sth r.5,0(r.3) // Store unaligned halfword
+ li r.6,2 // Set count to 2 bytes
+ b UpdteAddr // Jump to update addresses
+FillOdd:
+ bt 30,Fill1 // Branch if align 3
+ sth r.5,1(r.3) // Store unaligned halfword
+ li r.6,3 // Set count to 3 bytes
+Fill1:
+ stb r.5,0(r.3) // Store unaligned byte
+UpdteAddr:
+ sub r.4,r.4,r.6 // Decrement LENGTH by unaligned
+ add r.3,r.3,r.6 // Update the DEST address
+//
+// Divide the block to process into 32-byte blocks
+//
+
+BlkDiv:
+ andi. r.6,r.4,BLKLN-1 // Isolate remainder of LENGTH/32
+ sub. r.7,r.4,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- FillBy4Bytes // Jump if no full blocks
+ mr r.4,r.6 // Set Length = Remainder
+//
+// Fill 32-Byte Blocks
+//
+BlkFill:
+ stw r.5,0(r.3) // Store the 1st DEST word
+ stw r.5,4(r.3) // Store the 2nd DEST word
+ stw r.5,8(r.3) // Store the 3rd DEST word
+ stw r.5,12(r.3) // Store the 4th DEST word
+ stw r.5,16(r.3) // Store the 5th DEST word
+ stw r.5,20(r.3) // Store the 6th DEST word
+ stw r.5,24(r.3) // Store the 7th DEST word
+ stw r.5,28(r.3) // Store the 8th DEST word
+ addi r.3,r.3,32 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ BlkFill // Jump if more 32-Byte Blk fills
+//
+// Fill 4-Byte Blocks
+//
+FillBy4Bytes:
+ andi. r.6,r.4,3 // Isolate remainder of LENGTH/4
+ sub. r.7,r.4,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- FillByByte // Jump if no full blocks
+ mr r.4,r.6 // Set Length = Remainder
+//
+Fill4Bytes:
+ stw r.5,0(r.3)
+ addi r.3,r.3,4 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ Fill4Bytes // Jump if more 4-Byte Blk fills
+//
+// Fill 1-Byte Blocks
+//
+FillByByte:
+ cmpwi r.4,0 // Check for completion
+ beqlr+ // Return if done
+//
+Fill1Byte:
+ cmpwi r.4,1 // Check for done
+ stb r.5,0(r.3) // Fill 1 byte
+ beqlr+ // Return if done
+ cmpwi r.4,2 // Check for done
+ stb r.5,1(r.3) // Fill 1 byte
+ beqlr+ // Return if done
+ stb r.5,2(r.3) // Fill 1 Byte
+//
+// Exit
+//
+FillExit:
+ LEAF_EXIT(RtlFillMemory)
+
+//++
+//
+// VOID
+// 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:
+//
+// DEST (r.3) - Supplies a pointer to the memory to fill.
+//
+// LENGTH (r.4) - Supplies the length, in bytes, of the memory to be
+// filled.
+//
+// PTTRN (r.5) - Supplies the fill pattern.
+//
+// Return Value:
+//
+// None.
+//
+//--
+//
+// Define the entry point
+//
+ LEAF_ENTRY(RtlFillMemoryUlong)
+//
+// Make sure length is even number of longwords
+//
+ srwi r.4,r.4,2 // Shift length to divide by 4
+ slwi r.4,r.4,2 // Make sure LENGTH is even
+//
+// Divide the block to process into 32-byte blocks
+//
+ andi. r.6,r.4,BLKLN-1 // Isolate remainder of LNGTH/32
+ sub. r.7,r.4,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beq- FillUlBy4Bytes // Jump if no full blocks
+ mr r.4,r.6 // Set Length = Remainder
+//
+// Fill 32-Byte Blocks
+//
+BlkFillUl:
+ stw r.5,0(r.3) // Store the 1st DEST word
+ stw r.5,4(r.3) // Store the 2nd DEST word
+ stw r.5,8(r.3) // Store the 3rd DEST word
+ stw r.5,12(r.3) // Store the 4th DEST word
+ stw r.5,16(r.3) // Store the 5th DEST word
+ stw r.5,20(r.3) // Store the 6th DEST word
+ stw r.5,24(r.3) // Store the 7th DEST word
+ stw r.5,28(r.3) // Store the 8th DEST word
+ addi r.3,r.3,32 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ BlkFillUl // Jump if more 32-Byte Blk fills
+//
+// Fill 4-Byte Blocks
+//
+FillUlBy4Bytes:
+ andi. r.6,r.4,3 // Isolate remainder of LENGTH/4
+ sub. r.7,r.4,r.6 // Get full block count
+ add r.10,r.3,r.7 // Get address of last full block
+ beqlr- // Return if done
+ mr r.4,r.6 // Set Length = Remainder
+//
+FillUl4Bytes:
+ stw r.5,0(r.3)
+ addi r.3,r.3,4 // Increment the DEST address
+ cmpw r.3,r.10 // Check for completion
+ bne+ FillUl4Bytes // Jump if more 4-Byte Blk fills
+//
+// Exit
+//
+FillUlExit:
+ LEAF_EXIT(RtlFillMemoryUlong)
+
diff --git a/private/ntos/rtl/ppc/ntrtlppc.h b/private/ntos/rtl/ppc/ntrtlppc.h
new file mode 100644
index 000000000..3c79a0d04
--- /dev/null
+++ b/private/ntos/rtl/ppc/ntrtlppc.h
@@ -0,0 +1,43 @@
+/*++
+
+Copyright (c) 1993 IBM Corporation and Microsoft Corporation
+
+Module Name:
+
+ ntrtlmip.h
+
+Abstract:
+
+ PowerPC specific parts of ntrtlp.h
+
+Author:
+
+ Rick Simposn 16-Aug-93
+
+ based on MIPS version by 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/ppc/trampoln.s b/private/ntos/rtl/ppc/trampoln.s
new file mode 100644
index 000000000..5cd50561a
--- /dev/null
+++ b/private/ntos/rtl/ppc/trampoln.s
@@ -0,0 +1,687 @@
+//++
+//
+// Copyright (c) 1993 IBM Corporation and Microsoft Corporation
+//
+// Module Name:
+//
+// trampoln.s
+//
+// Abstract:
+//
+// This module implements the trampoline code necessary to dispatch user
+// mode APCs.
+//
+// Author:
+//
+// Rick Simpson 25-Oct-1993
+//
+// based on MIPS version by David N. Cutler (davec) 3-Apr-1990
+//
+// Environment:
+//
+// User mode only.
+//
+// Revision History:
+//
+//--
+
+//list(off)
+#include "ksppc.h"
+//list(on)
+ .extern __C_specific_handler
+ .extern ..RtlDispatchException
+ .extern ..RtlRaiseException
+ .extern ..RtlRaiseStatus
+ .extern ZwCallbackReturn
+ .extern ZwContinue
+ .extern ZwRaiseException
+ .extern ZwTestAlert
+
+//
+// Define layout and length of APC Dispatcher stack frame.
+// N.B. This must exactly match the computations in KiInitializeUserApc()
+//
+
+ .struct 0
+ADFrame: .space StackFrameHeaderLength
+ADContext: .space ContextFrameLength
+ADTrap: .space TrapFrameLength
+ADToc: .long 0
+ .space STK_SLACK_SPACE
+ .align 3
+ADFrameLength:
+
+ .text
+
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the APC dispatcher.
+//
+//--
+
+ .ydata // scope table -- exception handler
+ .align 2
+UserApcDispatcherScopeTable:
+ .long 1 // number of scope table entries
+ .long ..KiUserApcDispatcher // start of scope
+ .long KiUserApcDispatcher.end // end of scope
+ .long KiUserApcHandler // filter to decide what to do
+ .long 0 // it always decides to "continue search"
+ .text
+
+ FN_TABLE (KiUserApcDispatch,__C_specific_handler,UserApcDispatcherScopeTable)
+
+ DUMMY_ENTRY (KiUserApcDispatch)
+
+ stwu r.sp, -ADFrameLength (r.sp)
+ mflr r.0
+ stw r.0, ADContext + CxLr (r.sp)
+ stw r.0, ADContext + CxIar (r.sp)
+ stw r.toc, ADToc (r.sp)
+
+ PROLOGUE_END (KiUserApcDispatch)
+
+//++
+//
+// 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 stack 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.
+//
+// On entry here, a stack frame as shown above is already addressed
+// via r.1
+//
+// Arguments:
+//
+// r.1 - Stack frame pointer, already set up
+//
+// r.3 - Supplies the normal context parameter that was specified when the
+// APC was initialized.
+//
+// r.4 - Supplies the first argument that was provied by the executive when
+// the APC was queued.
+//
+// r.5 - Supplies the second argument that was provided by the executive
+// when the APC was queued.
+//
+// r.6 - Supplies that address of the descriptor for the function that is
+// to be called.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY (KiUserApcDispatcher)
+
+ lwz r.0, 0 (r.6) // fetch address of APC entry point
+ mtlr r.0 // move into Link Reg
+ lwz r.2, 4 (r.6) // fetch TOC address for APC
+ blrl // call specified APC routine
+
+ lwz r.2, ADToc (r.sp) // reload our own TOC pointer
+ la r.3, ADContext (r.sp) // 1st parm = addr of Context Frame
+ lwz r.5, [toc] ZwContinue (r.2) // fetch ptr to function descriptor
+ li r.4, 1 // 2nd parm = TRUE (test alert)
+ lwz r.0, 0 (r.5) // fetch addr of ZwContinue entry point
+ mtlr r.0 // move into Link Reg
+ lwz r.2, 4 (r.5) // fetch TOC addr for ZwContinue
+
+ // this next is done under protest --
+ // we are copying MIPS slavishly:
+ la r.12, ADTrap (r.sp) // "secret" parm = addr of Trap Frame
+ blrl // execute system service to continue
+ lwz r.2, ADToc (r.sp) // reload our own TOC pointer
+
+//
+// Unsuccessful completion after attempting to continue execution. Use the
+// return status as the exception code, set noncontinuable exception and
+// attempt to raise another exception. Note there is no return from raise
+// status.
+//
+
+ ori r.31, r.3, 0 // save status value
+ADloop:
+ ori r.3, r.31, 0 // set status value
+ bl ..RtlRaiseStatus // raise exception
+ b ADloop // loop on return
+
+ DUMMY_EXIT (KiUserApcDispatcher)
+ DUMMY_EXIT (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 through the
+// APC dispatcher is in progress. If an unwind is in progress, then test
+// alert is called to ensure that all currently queued APCs are executed.
+//
+// Arguments:
+//
+// ExceptionRecord (r.3) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (r.5) - Supplies a pointer to a context record.
+//
+// DispatcherContext (r.6) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// ExceptionContinueSearch is returned as the function value.
+//--
+
+ .struct 0
+ .space StackFrameHeaderLength // canonical stack frame header
+ .space 4 // reserve space for return address
+ .space 4 // reserve space for r.31
+ .align 3
+HFrameLength: // length of handler frame
+
+ .text
+
+ NESTED_ENTRY (KiUserApcHandler, HFrameLength, 1, 0)
+
+ ori r.31, r.toc, 0 // save our TOC value in r.31
+
+ PROLOGUE_END (KiUserApcHandler)
+
+ lwz r.0, ErExceptionFlags (r.3) // get exception flags
+ andi. r.0, r.0, EXCEPTION_UNWIND // check if unwind in progress
+ beq H10 // if eq, no unwind in progress
+ lwz r.7, [toc] ZwTestAlert (r.toc) // get addr of function descriptor
+ lwz r.0, 0 (r.7) // get entry point address
+ mtlr r.0 // into Link Reg
+ lwz r.toc, 4 (r.7) // get callee's TOC pointer
+ blrl // test for alert pending
+ ori r.toc, r.31, 0 // reload our own TOC pointer
+
+H10: li r.3, ExceptionContinueSearch // set disposition value
+
+ NESTED_EXIT (KiUserApcHandler, HFrameLength, 1, 0)
+
+// SBTTL("User Callback Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the callback dispatcher.
+//
+//--
+
+#if 0
+ .ydata // scope table -- exception handler
+ .align 2
+UserCallbackDispatcherScopeTable:
+ .long 1 // number of scope table entries
+ .long ..KiUserCallbackDispatcher // start of scope
+ .long KiUserCallbackDispatcher.end // end of scope
+ .long KiUserCallbackHandler // filter to decide what to do
+ .long 0 // it always decides to "continue search"
+ .text
+
+ FN_TABLE (KiUserCallbackDispatch,__C_specific_handler,UserCallbackDispatcherScopeTable)
+#endif
+
+ DUMMY_ENTRY (KiUserCallbackDispatch)
+
+ stwu r.sp, -CkFrameLength(r.sp)
+ mflr r.0
+ stw r.0, CkLr(r.sp)
+ stw r.toc, CkToc(r.sp)
+
+ PROLOGUE_END (KiUserCallbackDispatch)
+
+//++
+//
+// 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)
+
+ lwz r.6, TePeb(r.13) // get address of PEB
+ ori r.31, r.toc, 0 // save our TOC value in r.31
+
+ lwz r.5, CkApiNumber(r.1) // get API number
+ lwz r.6, PeKernelCallbackTable(r.6) // get address of callback table
+ lwz r.3, CkBuffer(r.1) // get input buffer address
+ slwi r.5, r.5, 2 // compute offset to table entry
+ lwz r.4, CkLength(r.1) // get input buffer length
+ lwzx r.5, r.5, r.6 // get descriptor for callback routine
+ lwz r.0, 0 (r.5) // get entry point address
+ mtlr r.0 // into link register
+ lwz r.toc, 4 (r.5) // get callee's TOC pointer
+ blrl // call specified function
+
+//
+// If a return from the callback function occurs, then the output buffer
+// address and length are returned as NULL.
+//
+
+ ori r.toc, r.31, 0 // reload our own TOC pointer
+
+ lwz r.7, [toc] ZwCallbackReturn (r.toc) // get addr of function descriptor
+ ori r.5, r.3, 0 // set completion status
+ li r.3, 0 // set zero buffer address
+ li r.4, 0 // set zero buffer lenfth
+ lwz r.0, 0 (r.7) // get entry point address
+ mtlr r.0 // into Link Reg
+ lwz r.toc, 4 (r.7) // get callee's TOC pointer
+ blrl // 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.
+//
+
+ ori r.toc, r.31, 0 // reload our own TOC pointer
+
+ ori r.31, r.3, 0 // save status value
+UCDloop:
+ bl ..RtlRaiseStatus // raise exception
+ ori r.3, r.31, 0 // set status value
+ b UCDloop // loop on return
+
+ DUMMY_EXIT (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 (r.3) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (r.5) - Supplies a pointer to a context record.
+//
+// DispatcherContext (r.6) - Supplies a pointer to the dispatcher context
+// record.
+//
+// Return Value:
+//
+// ExceptionContinueSearch is returned as the function value.
+//--
+
+ NESTED_ENTRY (KiUserCallbackHandler, HFrameLength, 1, 0)
+
+ ori r.31, r.toc, 0 // save our TOC value in r.31
+
+ PROLOGUE_END (KiUserCallbackHandler)
+
+ lwz r.0, ErExceptionFlags (r.3) // get exception flags
+ andi. r.0, r.0, EXCEPTION_UNWIND // check if unwind in progress
+ beq UCH10 // if eq, no unwind in progress
+
+//
+// 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.
+//
+
+ lwz r.5, ErExceptionCode(r.3) // get exception code
+ li r.3, 0 // set zero buffer address
+ li r.4, 0 // set zero buffer lenfth
+ lwz r.7, [toc]ZwCallbackReturn(r.toc) // get addr of function descriptor
+ lwz r.0, 0 (r.7) // get entry point address
+ mtlr r.0 // into Link Reg
+ lwz r.toc, 4 (r.7) // get callee's TOC pointer
+ blrl // 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.
+//
+
+ ori r.toc, r.31, 0 // reload our own TOC pointer
+
+ ori r.31, r.3, 0 // save status value
+UCHloop:
+ bl ..RtlRaiseStatus // raise exception
+ ori r.3, r.31, 0 // set status value
+ b UCHloop // loop on return
+
+UCH10:
+ li r.3, ExceptionContinueSearch // set disposition value
+
+ NESTED_EXIT (KiUserCallbackHandler, HFrameLength, 1, 0)
+
+//
+// Define layout and length of User Exception Dispatcher stack frame.
+// N.B. This must exactly match the computations in KiDispatchException
+//
+
+ .struct 0
+EDFrame: .space StackFrameHeaderLength
+EDExcept: .space ExceptionRecordLength
+EDContext: .space ContextFrameLength
+EDToc: .long 0
+ .space STK_SLACK_SPACE
+ .align 3
+EDFrameLength:
+
+ .text
+
+// SBTTL("User Exception Dispatcher")
+//++
+//
+// The following code is never executed. Its purpose is to support unwinding
+// through the call to the exception dispatcher.
+//
+//--
+
+ FN_TABLE (KiUserExceptionDispatch, 0, 0)
+
+ DUMMY_ENTRY (KiUserExceptionDispatch)
+
+ stwu r.sp, -EDFrameLength (r.sp) // buy stack frame
+ mflr r.0 // save linkage
+ stw r.0, EDContext + CxLr (r.sp) // regs
+ mflr r.0 // needed by vunwind code
+ stw r.0, EDContext + CxIar (r.sp)
+ stw r.toc, EDToc (r.sp)
+
+ stw r.13, EDContext + CxGpr13 (r.sp) // save non-volatile integer state
+ stw r.14, EDContext + CxGpr14 (r.sp)
+ stw r.15, EDContext + CxGpr15 (r.sp)
+ stw r.16, EDContext + CxGpr16 (r.sp)
+ stw r.17, EDContext + CxGpr17 (r.sp)
+ stw r.18, EDContext + CxGpr18 (r.sp)
+ stw r.19, EDContext + CxGpr19 (r.sp)
+ stw r.20, EDContext + CxGpr20 (r.sp)
+ stw r.21, EDContext + CxGpr21 (r.sp)
+ stw r.22, EDContext + CxGpr22 (r.sp)
+ stw r.23, EDContext + CxGpr23 (r.sp)
+ stw r.24, EDContext + CxGpr24 (r.sp)
+ stw r.25, EDContext + CxGpr25 (r.sp)
+ stw r.26, EDContext + CxGpr26 (r.sp)
+ stw r.27, EDContext + CxGpr27 (r.sp)
+ stw r.28, EDContext + CxGpr28 (r.sp)
+ stw r.29, EDContext + CxGpr29 (r.sp)
+ stw r.30, EDContext + CxGpr30 (r.sp)
+ stw r.31, EDContext + CxGpr31 (r.sp)
+ stfd f.14, EDContext + CxFpr14 (r.sp) // save non-volatile floating point state
+ stfd f.15, EDContext + CxFpr15 (r.sp)
+ stfd f.16, EDContext + CxFpr16 (r.sp)
+ stfd f.17, EDContext + CxFpr17 (r.sp)
+ stfd f.18, EDContext + CxFpr18 (r.sp)
+ stfd f.19, EDContext + CxFpr19 (r.sp)
+ stfd f.20, EDContext + CxFpr20 (r.sp)
+ stfd f.21, EDContext + CxFpr21 (r.sp)
+ stfd f.22, EDContext + CxFpr22 (r.sp)
+ stfd f.23, EDContext + CxFpr23 (r.sp)
+ stfd f.24, EDContext + CxFpr24 (r.sp)
+ stfd f.25, EDContext + CxFpr25 (r.sp)
+ stfd f.26, EDContext + CxFpr26 (r.sp)
+ stfd f.27, EDContext + CxFpr27 (r.sp)
+ stfd f.28, EDContext + CxFpr28 (r.sp)
+ stfd f.29, EDContext + CxFpr29 (r.sp)
+ stfd f.30, EDContext + CxFpr30 (r.sp)
+ stfd f.31, EDContext + CxFpr31 (r.sp)
+
+ PROLOGUE_END (KiUserExceptionDispatch)
+
+//++
+//
+// 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:
+//
+// r.3 - Supplies a pointer to an exception record.
+//
+// r.4 - Supplies a pointer to a context record.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ ALTERNATE_ENTRY(KiUserExceptionDispatcher)
+
+ bl ..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.
+//
+
+ cmpwi r.3, 0 // compare return value to FALSE
+ beq ED10 // if eq, perform last chance processing
+
+//
+// Continue execution.
+//
+
+ lwz r.5, [toc] ZwContinue (r.2) // load pointer to function descriptor
+ la r.3, EDContext (r.sp) // set addr of context frame
+ lwz r.0, 0 (r.5) // load entry point address
+ mtlr r.0 // move into Link Reg
+ li r.4, 0 // set test alert argument false
+ lwz r.2, 4 (r.5) // load ZwContinue's TOC pointer
+ blrl // execute system service to continue
+ lwz r.2, EDToc (r.sp) // reload our own TOC address
+ b ED20 // join common code
+
+//
+// Last chance processing.
+//
+
+ED10:
+ lwz r.6, [toc] ZwRaiseException (r.2) // load pointer to function descriptor
+ la r.3, EDExcept (r.sp) // set address of exception record
+ lwz r.0, 0 (r.6) // load entry point address
+ mtlr r.0 // into link reg
+ la r.4, EDContext (r.sp) // set address of context frame
+ li r.5, 0 // set first chance FALSE
+ lwz r.toc, 4 (r.6) // load callee's TOC addr
+ blrl // perform last chance processing
+ lwz r.toc, EDToc (r.sp) // reload our own TOC address
+
+//
+// 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.
+//
+
+ED20: // status value is in r.3
+ la r.4, EDExcept (r.sp) // point to our exception record
+ bl ..KipUserExceptionDispatcherLoop // call subroutine below
+
+ DUMMY_EXIT (KiUserExceptionDispatch)
+
+//++
+//
+// VOID
+// KiUserExceptionDispatcherLoop (
+// IN ULONG ExceptionStatus
+// )
+//
+// Routine Description:
+//
+// This routine builds an Exception Record and calls RtlRaiseException.
+// On an unsuccessful return, it calls itself recursively; eventually
+// this will terminate when the stack fills up.
+//
+// Arguments:
+//
+// r.3 - Status value
+//
+// Return Value:
+//
+// None. (Does not return.)
+//
+//--
+
+//
+// Stack frame layout for KipUserExceptionDispatchLoop
+//
+
+ .struct 0
+LFrame: .space StackFrameHeaderLength
+LExcept: .space ExceptionRecordLength
+LOldExcept: .long 0
+LSavedLR: .long 0
+ .align 3
+LFrameLength:
+
+ .text
+
+ NESTED_ENTRY (KipUserExceptionDispatcherLoop, LFrameLength, 0, 0)
+
+ PROLOGUE_END (KipUserExceptionDispatcherLoop)
+
+ stw r.4, LOldExcept (r.sp) // save incoming exception rec addr
+ la r.5, LExcept (r.sp) // point to Exception Record
+ stw r.3, ErExceptionCode (r.5) // fill in exception code (incoming status)
+ li r.6, EXCEPTION_NONCONTINUABLE // set non-continuable flag
+ stw r.6, ErExceptionFlags (r.5)
+ stw r.4, ErExceptionRecord (r.5) // set addr of prev. exception record
+ li r.0, 0 // set number of parameters
+ stw r.0, ErNumberParameters (r.5) // to 0
+ ori r.3, r.5, 0 // load 1st parameter pointer
+ bl ..RtlRaiseException // raise an exception
+
+ lwz r.4, LOldExcept (r.sp) // should not return, but if so:
+ bl ..KipUserExceptionDispatcherLoop // keep doing this in a loop
+
+ NESTED_EXIT (KipUserExceptionDispatcherLoop, LFrameLength, 0, 0)
+
+//++
+//
+// NTSTATUS
+// KiRaiseUserExceptionDispatcher (
+// IN NTSTATUS ExceptionCode
+// )
+//
+// Routine Description:
+//
+// This routine is entered on return from kernel mode to raise a user
+// mode exception.
+//
+// Arguments:
+//
+// r3 - Supplies the status code to be raised.
+//
+// Return Value:
+//
+// ExceptionCode
+//
+//--
+
+//
+// N.B. This function is not called in the typical way. Instead of a normal
+// subroutine call to the nested entry point above, the alternate entry point
+// address below is stuffed into the Iar address of the trap frame. Thus when
+// the kernel returns from the trap, the following code is executed directly.
+//
+
+ .struct 0
+ruedFrame: .space StackFrameHeaderLength
+ruedExr: .space ExceptionRecordLength
+ruedR3: .space 4
+ruedLr: .space 4
+ .align 3
+ruedFrameLength:
+
+ SPECIAL_ENTRY(KiRaiseUserExceptionDispatcher)
+
+ mflr r4 // get return address (also exception address)
+ stwu sp,-ruedFrameLength(sp) // allocate stack frame
+ li r0,0 // get a 0
+ stw r4,ruedLr(sp) // save return address
+
+ PROLOGUE_END(KiRaiseUserExceptionDispatcher)
+
+ stw r3,ruedR3(sp) // save function return status
+ stw r3,ErExceptionCode+ruedExr(sp) // set exception code
+ la r3,ruedExr(sp) // compute exception record address
+ lwz r4,ruedLr(sp) // get exception address
+ stw r0,ErExceptionFlags(r3) // set exception flags
+ stw r0,ErExceptionRecord(r3) // set exception record
+ stw r0,ErNumberParameters(r3) // set number of parameters
+ stw r4,ErExceptionAddress(r3) // set exception address
+
+ bl ..RtlRaiseException // attempt to raise the exception
+
+ lwz r3,ruedR3(sp) // restore function status
+
+ NESTED_EXIT (KiRaiseUserExceptionDispatcher, ruedFrameLength, 0, 0)
+
diff --git a/private/ntos/rtl/ppc/vunwind.c b/private/ntos/rtl/ppc/vunwind.c
new file mode 100644
index 000000000..148910e2a
--- /dev/null
+++ b/private/ntos/rtl/ppc/vunwind.c
@@ -0,0 +1,1056 @@
+/*++
+
+Copyright (c) 1993 IBM Corporation and Microsoft Corporation
+
+Module Name:
+
+ vunwind.c
+
+Abstract:
+
+ This module contains the instruction classifying and virtual
+ unwinding routines for structured exception handling on PowerPC.
+
+ Virtual Unwind was moved to this file from exdsptch.c so that it
+ can be used directly in the kernel debugger.
+
+ WARNING!
+
+ The kernel debugger and windbg need to be modified if the number
+ and type of parameters for READ_ULONG and READ_DOUBLE are
+ modified.
+
+ Use CAUTION if you add include statements
+
+Author:
+
+ Rick Simpson 16-Aug-1993
+
+ based on MIPS version by David N. Cutler (davec) 11-Sep-1990
+
+Environment:
+
+ Any mode.
+
+Revision History:
+
+ Tom Wood (twood) 1-Nov-1993
+ Add back changes to RtlVirtualUnwind made when the MIPS version
+ was ported.
+
+ Tom Wood (twood) 1-Feb-1994
+ Change to using a function table entry for register save/restore
+ millicode. Add forward execution of register restore millicode.
+ Add the ITERATOR abstraction.
+
+ Peter Johnston (plj@vnet.ibm.com) 14-Feb-1994
+ Added InstrGetOut classification to allow a simulated return from
+ dummy prologues such as those in system exception handling.
+
+ Tom Wood (twood) 28-Feb-1994
+ Added the WINDBG interface.
+
+ Tom Wood (twood) 8-Jun-1994
+ Added the _IMAGEHLP_SOURCE_ interface.
+
+ Tom Wood (twood) 8-Jun-1994
+ Removed the WINDBG interface. Updated the _IMAGEHLP_SOURCE_
+ interface to deal with the fact that ExceptionHandler and HandlerData
+ cannot be relied upon. Also, the FunctionEntry value may be a static
+ buffer returned by RtlLookupFunctionEntry (i.e. FunctionTableAccess).
+ The copy of vunwind.c in ntos/rtl/ppc is older and should be replaced
+ with this version.
+
+ Tom Wood (twood) 9-Aug-1994
+ Added support for the new glue code sequences. Added InstrGlue and
+ InstrTocRestore. The former replaces InstrGetOut. RtlVirtualUnwind
+ is now required to be called when there is no function table entry.
+
+ --*/
+
+typedef DOUBLE *PDOUBLE;
+
+#ifdef ROS_DEBUG
+#include "ntrtlp.h"
+#define READ_ULONG(addr,dest) dest = (*((PULONG)(addr)))
+#define READ_DOUBLE(addr,dest) dest = (*((PDOUBLE)(addr)))
+#endif
+
+#ifdef _IMAGEHLP_SOURCE_
+#define FUNCTION_ENTRY_IS_IMAGE_STYLE
+#define NOT_IMAGEHLP(E)
+#else
+#define NOT_IMAGEHLP(E) E
+#endif
+
+#ifdef KERNEL_DEBUGGER
+#define FUNCTION_ENTRY_IS_IMAGE_STYLE
+#define RtlVirtualUnwind VirtualUnwind
+#endif
+
+//
+// The `ClassifyInstruction' function returns an enum that identifies
+// the type of processing needed for an instruction.
+//
+
+typedef enum _INSTR_CLASS {
+ InstrIgnore, // Do not process
+ InstrMFLR, // Move from Link Register
+ InstrMFCR, // Move from Condition Register
+ InstrSTW, // Store word
+ InstrSTWU, // Store word with update
+ InstrSTWUr12, // Store word with update during UnwindR12
+ InstrSTFD, // Store float double
+ InstrMR, // Move register
+ InstrMRr12, // Move register during UnwindR12
+ InstrMRfwd, // Move register during UnwindForward
+ InstrADDIr12, // Add immediate during UnwindR12
+ InstrADDIfwd, // Add immediate during UnwindForward
+ InstrSaveCode, // Branch and link to GPR or FPR saving millicode
+ InstrRestoreCode, // Branch to GPR or FPR saving millicode
+ InstrGlue, // Branch or Branch and link to glue code
+ InstrBLR, // Branch to Link Register
+ InstrTOCRestore, // Load that restores the TOC [after a call to glue]
+ InstrSetEstablisher // Special instruction used to set establisher frame
+} INSTR_CLASS;
+
+//
+// If `ClassifyInstruction' returns `InstrSaveCode' or `InstrRestoreCode',
+// the following information is completed.
+//
+
+typedef struct _MILLICODE_INFO {
+ ULONG TargetPc; // Millicode entry point
+ PRUNTIME_FUNCTION FunctionEntry; // Millicode function table entry
+} MILLICODE_INFO, *PMILLICODE_INFO;
+
+//
+// `ClassifyInstruction' interprets the instruction based on the intent.
+//
+
+typedef enum _UNWIND_INTENT {
+ UnwindForward, // Performing a forward execution
+ UnwindR12, // Performing a reverse execution to get r.12
+ UnwindReverse, // Performing a reverse execution
+ UnwindReverseR12 // Performing a reverse execution allowing r.12
+} UNWIND_INTENT;
+
+//
+// The simulated execution by `RtlVirtualUnwind' is controlled by this
+// data type.
+//
+
+typedef struct _ITERATOR {
+ ULONG BeginPc; // Address of first instruction to simulate
+ ULONG EndPc; // Address after the last instruction to simulate
+ LONG Increment; // Simulation direction
+ UNWIND_INTENT Intent; // Simulation intent
+} ITERATOR, *PITERATOR;
+
+#define GPR1 1 // GPR 1 in an RA, RB, RT, etc. field
+#define GPR2 2 // GPR 2 in an RA, RB, RT, etc. field
+#define GPR12 12 // GPR 12 in an RA, RB, RT, etc. field
+#define LINKREG 0x100 // Link Reg in a MFSPR instruction
+#define COUNTREG 0x120 // Count Reg in a MFSPR instruction
+
+//
+// TryReadUlong attempts to read memory from a possibly unsafe address.
+// It is in a seperate routine to avoid optimization deficiencies caused
+// by use of try/except.
+//
+
+#ifndef _IMAGEHLP_SOURCE_
+
+static NTSTATUS
+TryReadUlong(IN ULONG NextPc,
+ OUT PULONG Value)
+{
+ try {
+ READ_ULONG (NextPc, *Value);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ return GetExceptionCode();
+ }
+ return STATUS_SUCCESS;
+}
+
+#endif
+
+static INSTR_CLASS
+ClassifyInstruction (PPC_INSTRUCTION *I,
+ UNWIND_INTENT Intent,
+#ifdef _IMAGEHLP_SOURCE_
+ HANDLE hProcess,
+ PREAD_PROCESS_MEMORY_ROUTINE ReadMemory,
+ PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccess,
+#endif
+ ULONG Pc,
+ PMILLICODE_INFO Info)
+
+/*++
+
+Routine description:
+
+ This function inspects the instruction identified by the "Pc"
+ argument and determines what sort of processing is needed in order
+ to simulate its execution. Some instructions can be safely
+ ignored altogether, in which case "InstrIgnore" is returned. For
+ others, a value is returned indicating what kind of instruction
+ was found. The interpreation depends on the value of "Intent".
+
+Arguments:
+
+ I - Address of a struct containing the instruction to be examined.
+ Intent - Type of unwinding being performed.
+ Pc - Address of the instruction, used for computing relative branch
+ addresses.
+ Info - Address to store a description of the register save/restore
+ millicode.
+
+Return value:
+
+ One of the enum values defined above is returned.
+
+ --*/
+
+{
+// Unique value combining an opcode and an UNWIND_INTENT value.
+#define OP_INTENT(OP,INTENT) ((OP) << 2 | (INTENT))
+
+#ifdef _IMAGEHLP_SOURCE_
+ DWORD ImagehlpCb = 0;
+#endif
+
+ switch (OP_INTENT (I->Primary_Op, Intent)) {
+
+ //
+ // Store word: recognize "stw r.n, disp(r.1)". Allow a base of
+ // r.12 if we have computed its value.
+ //
+ case OP_INTENT (STW_OP, UnwindReverseR12):
+ if (I->Dform_RA == GPR12)
+ return InstrSTW;
+ // fall thru
+ case OP_INTENT (STW_OP, UnwindReverse):
+ if (I->Dform_RA == GPR1)
+ return InstrSTW;
+ break;
+
+ //
+ // Load word: recognize "lwz r.n, disp(r.x)" in epilogue millicode.
+ //
+ case OP_INTENT (LWZ_OP, UnwindForward):
+ return InstrSTW;
+
+ //
+ // Load word: recognize "lwz r.toc, disp(r.1)" as TOC restore
+ // instruction.
+ //
+ case OP_INTENT (LWZ_OP, UnwindReverse):
+ if (I->Dform_RA == GPR1 &&
+ I->Dform_RS == GPR2)
+ return InstrTOCRestore;
+
+ //
+ // Store word with update: recognize "stwu r.1, r.1, disp"
+ //
+ case OP_INTENT (STWU_OP, UnwindReverse):
+ case OP_INTENT (STWU_OP, UnwindReverseR12):
+ case OP_INTENT (STWU_OP, UnwindR12):
+ if (I->Dform_RS == GPR1 &&
+ I->Dform_RA == GPR1)
+ return (Intent == UnwindR12 ? InstrSTWUr12 : InstrSTWU);
+ break;
+
+ //
+ // Store float double: recognize "stfd f.n, disp(r.1)". Allow a
+ // base of r.12 if we have computed its value.
+ //
+ case OP_INTENT (STFD_OP, UnwindReverseR12):
+ if (I->Dform_RA == GPR12)
+ return InstrSTFD;
+ // fall thru
+ case OP_INTENT (STFD_OP, UnwindReverse):
+ if (I->Dform_RA == GPR1)
+ return InstrSTFD;
+ break;
+
+ //
+ // Load float double: recognize "lfd f.n, disp(r.x)"
+ //
+ case OP_INTENT (LFD_OP, UnwindForward):
+ return InstrSTFD;
+
+ //
+ // Add immediate: recognize "addi r.12, r.1, delta"
+ //
+ case OP_INTENT (ADDI_OP, UnwindR12):
+ if (I->Dform_RS == GPR12 &&
+ I->Dform_RA == GPR1)
+ return InstrADDIr12;
+ break;
+ case OP_INTENT (ADDI_OP, UnwindForward):
+ return InstrADDIfwd;
+
+ //
+ // Branch (long form): recognize "bl[a] saveregs"and "b[a] restregs"
+ //
+ case OP_INTENT (B_OP, UnwindReverse):
+ //
+ // Compute branch target address, allowing for branch-relative
+ // and branch-absolute.
+ //
+ Pc = ((LONG)(I->Iform_LI) << 2) + (I->Iform_AA ? 0 : Pc);
+
+ //
+ // Quickly distinguish "bl subroutine" from "bl[a] saveregs".
+ // "mtlr" is not a valid instruction in register save millicode and
+ // is usually the first instruction in "bl subroutine".
+ //
+ if (I->Iform_LK) {
+ PPC_INSTRUCTION TempI;
+ READ_ULONG (Pc, TempI.Long);
+ if (TempI.Primary_Op == X31_OP &&
+ TempI.Xform_XO == MFSPR_OP &&
+ TempI.XFXform_spr == LINKREG) {
+ break;
+ }
+ }
+
+ //
+ // Determine whether the target address is part of a register
+ // save or register restore sequence or is a direct branch out
+ // by checking it's function table entry.
+ //
+ if ((Info->FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc)) != NULL
+#ifndef FUNCTION_ENTRY_IS_IMAGE_STYLE
+ && Info->FunctionEntry->ExceptionHandler == 0
+#endif
+ ) {
+ Info->TargetPc = Pc;
+ switch (
+#ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE
+ Info->FunctionEntry->BeginAddress -
+ Info->FunctionEntry->PrologEndAddress
+#else
+ (ULONG)Info->FunctionEntry->HandlerData
+#endif
+ ) {
+ case 1:
+ if (I->Iform_LK)
+ return InstrSaveCode;
+ break;
+ case 2:
+ if (!I->Iform_LK)
+ return InstrRestoreCode;
+ break;
+#ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE
+ default:
+ if ((Info->FunctionEntry->PrologEndAddress & 3) == 1)
+#else
+ case 3:
+#endif
+ return InstrGlue;
+ break;
+ }
+ }
+ break; // unrecognized entry point
+
+ //
+ // Extended ops -- primary opcode 19
+ //
+ case OP_INTENT (X19_OP, UnwindForward):
+
+ //
+ // BLR: recognized "bclr 20,0".
+ //
+ if (I->Long == RETURN_INSTR)
+ return InstrBLR;
+
+ break;
+
+ case OP_INTENT (X19_OP, UnwindR12):
+ case OP_INTENT (X19_OP, UnwindReverse):
+ case OP_INTENT (X19_OP, UnwindReverseR12):
+ //
+ // RFI: this instruction is used in special kernel fake prologues
+ // to indicate that the establisher frame address should be
+ // updated using the current value of sp.
+ //
+ if (I->Xform_XO == RFI_OP) {
+ return InstrSetEstablisher;
+ }
+
+ break;
+
+ //
+ // Extended ops -- primary opcode 31
+ //
+ case OP_INTENT (X31_OP, UnwindForward):
+ case OP_INTENT (X31_OP, UnwindR12):
+ case OP_INTENT (X31_OP, UnwindReverse):
+ case OP_INTENT (X31_OP, UnwindReverseR12):
+ switch (OP_INTENT (I->Xform_XO, Intent)) {
+
+ //
+ // OR register: recognize "or r.x, r.y, r.y" as move-reg
+ //
+ case OP_INTENT (OR_OP, UnwindR12):
+ if (I->Xform_RS == I->Xform_RB &&
+ I->Xform_RA == GPR12 &&
+ I->Xform_RB == GPR1)
+ return InstrMRr12;
+ break;
+ case OP_INTENT (OR_OP, UnwindReverse):
+ case OP_INTENT (OR_OP, UnwindReverseR12):
+ if (I->Xform_RS == I->Xform_RB &&
+ I->Xform_RB != GPR1)
+ return InstrMR;
+ break;
+ case OP_INTENT (OR_OP, UnwindForward):
+ if (I->Xform_RS == I->Xform_RB)
+ return InstrMRfwd;
+ break;
+
+ //
+ // Store word with update indexed: recognize "stwux r.1, r.1, r.x"
+ //
+ case OP_INTENT (STWUX_OP, UnwindReverse):
+ case OP_INTENT (STWUX_OP, UnwindReverseR12):
+ case OP_INTENT (STWUX_OP, UnwindR12):
+ if (I->Xform_RS == GPR1 && I->Xform_RA == GPR1)
+ return (Intent == UnwindR12 ? InstrSTWUr12 : InstrSTWU);
+ break;
+
+ //
+ // Move to/from special-purpose reg: recognize "mflr", "mtlr"
+ //
+ case OP_INTENT (MFSPR_OP, UnwindReverse):
+ case OP_INTENT (MTSPR_OP, UnwindForward):
+ if (I->XFXform_spr == LINKREG)
+ return InstrMFLR;
+ break;
+
+ //
+ // Move from Condition Register: "mfcr r.x"
+ //
+ case OP_INTENT (MFCR_OP, UnwindReverse):
+ case OP_INTENT (MFCR_OP, UnwindReverseR12):
+ return InstrMFCR;
+
+ //
+ // Move to Condition Register: "mtcrf 255,r.x"
+ //
+ case OP_INTENT (MTCRF_OP, UnwindForward):
+ if (I->XFXform_FXM == 255)
+ return InstrMFCR;
+ break;
+
+ default: // unrecognized
+ break;
+ }
+
+ default: // unrecognized
+ break;
+ }
+
+ //
+ // Instruction not recognized; just ignore it and carry on
+ //
+ return InstrIgnore;
+#undef OP_INTENT
+}
+
+#ifdef _IMAGEHLP_SOURCE_
+static
+#endif
+ULONG
+RtlVirtualUnwind (
+
+#ifdef _IMAGEHLP_SOURCE_
+ HANDLE hProcess,
+ DWORD ControlPc,
+ PRUNTIME_FUNCTION FunctionEntry,
+ PCONTEXT ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE ReadMemory,
+ PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccess
+#define ContextPointers ((PKNONVOLATILE_CONTEXT_POINTERS)0)
+#else
+ IN ULONG ControlPc,
+ IN PRUNTIME_FUNCTION FunctionEntry,
+ IN OUT PCONTEXT ContextRecord,
+ OUT PBOOLEAN InFunction,
+ OUT PULONG EstablisherFrame,
+ IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL,
+ IN ULONG LowStackLimit,
+ IN ULONG HighStackLimit
+#endif
+ )
+
+/*++
+
+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.
+
+ If the function is register save millicode, it is treated as a leaf
+ function. If the function is register restore millicode, the remaining
+ body is executed forwards and the address where control left the
+ previous frame is obtained from the final blr instruction.
+
+ If the function was called via glue code and is not that glue code,
+ the prologe of the glue code is executed backwards in addition to the
+ above actions.
+
+ 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.
+
+Arguments:
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function or NULL if the function is a leaf 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.
+
+ LowStackLimit, HighStackLimit - Range of valid values for the stack
+ pointer. This indicates whether it is valid to examine NextPc.
+
+Return Value:
+
+ The address where control left the previous frame is returned as the
+ function value.
+
+ --*/
+
+{
+ ITERATOR Iterator[8];
+ PITERATOR Piterator;
+ ULONG Address;
+ PDOUBLE FloatingRegister;
+ PPC_INSTRUCTION I;
+ PULONG IntegerRegister;
+ ULONG NextPc, Pc;
+ BOOLEAN RestoredLr = FALSE;
+ BOOLEAN RestoredSp = FALSE;
+ BOOLEAN ComputedSp = FALSE;
+ ULONG Rt;
+ MILLICODE_INFO Info;
+ INSTR_CLASS InstrClass;
+#ifdef _IMAGEHLP_SOURCE_
+ DWORD ImagehlpCb = 0;
+ RUNTIME_FUNCTION SavedFunctionEntry;
+#else
+ ULONG EstablisherFrameValue;
+#endif
+
+ //
+ // Set the base address of the integer and floating register arrays.
+ //
+
+ FloatingRegister = &ContextRecord->Fpr0;
+ IntegerRegister = &ContextRecord->Gpr0;
+
+ //
+ // If the function is a leaf function, perform the default unwinding
+ // action and check to see if the function was called via glue.
+ //
+ if (FunctionEntry == NULL) {
+ //
+ // Set point at which control left the previous routine.
+ //
+ NextPc = ContextRecord->Lr - 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)
+ return NextPc;
+
+ goto CheckForGlue;
+ }
+#ifdef _IMAGEHLP_SOURCE_
+ else {
+ SavedFunctionEntry = *FunctionEntry;
+ FunctionEntry = &SavedFunctionEntry;
+ }
+#endif
+ //
+ // Set initial values for EstablisherFrame and Offset.
+ // (this may need more careful planning IBMPLJ).
+ //
+
+ NOT_IMAGEHLP (*EstablisherFrame =
+ EstablisherFrameValue = ContextRecord->Gpr1);
+
+ READ_ULONG (ControlPc, I.Long);
+ if (I.Long == RETURN_INSTR) {
+ //
+ // If the instruction at the point where control left the specified
+ // function is a return, then any saved registers have been restored
+ // and the control PC is not considered to be in the function
+ // (i.e., in an epilogue).
+ //
+ NOT_IMAGEHLP(*InFunction = FALSE);
+ NextPc = ContextRecord->Lr;
+ goto CheckForGlue;
+ }
+ InstrClass = ClassifyInstruction(&I, UnwindReverse,
+#ifdef _IMAGEHLP_SOURCE_
+ hProcess, ReadMemory, FunctionTableAccess,
+#endif
+ ControlPc, &Info);
+ if (InstrClass == InstrRestoreCode) {
+ //
+ // If the instruction at the point where control left the
+ // specified function is a branch to register restore
+ // millicode, the state is restored by simulating the
+ // execution of the restore millicode. The control PC is in
+ // an epilogue.
+ //
+ Iterator[0].BeginPc = Info.TargetPc;
+ Iterator[0].EndPc = Info.FunctionEntry->EndAddress;
+ Iterator[0].Increment = 4;
+ Iterator[0].Intent = UnwindForward;
+ NOT_IMAGEHLP(*InFunction = FALSE);
+
+ } else if (
+#ifdef FUNCTION_ENTRY_IS_IMAGE_STYLE
+ (FunctionEntry->BeginAddress -
+ FunctionEntry->PrologEndAddress) == 2
+#else
+ FunctionEntry->ExceptionHandler == 0 &&
+ (ULONG)FunctionEntry->HandlerData == 2
+#endif
+ ) {
+ //
+ // If the address is in register restore millicode, the state
+ // is restored by completing the execution of the restore
+ // millicode. The control PC is in an epilogue.
+ //
+ Iterator[0].BeginPc = ControlPc;
+ Iterator[0].EndPc = FunctionEntry->EndAddress;
+ Iterator[0].Increment = 4;
+ Iterator[0].Intent = UnwindForward;
+ NOT_IMAGEHLP(*InFunction = FALSE);
+
+ } else {
+ //
+ // If the address where control left the specified function is a
+ // TOC restore instruction and the previous instruction is a call
+ // via glue instruction, we must forward execute the TOC restore
+ // instruction.
+ //
+ if (InstrClass == InstrTOCRestore) {
+ PPC_INSTRUCTION Iprev;
+ READ_ULONG (ControlPc - 4, Iprev.Long);
+ if (ClassifyInstruction (&Iprev, UnwindReverse,
+#ifdef _IMAGEHLP_SOURCE_
+ hProcess, ReadMemory, FunctionTableAccess,
+#endif
+ ControlPc - 4, &Info) == InstrGlue) {
+ //
+ // Forward execute the TOC restore. We assume (reasonably)
+ // that the next instruction is covered by the same function
+ // table entry and it isn't one of the above special cases
+ // (InstrRestoreCode or InstrBLR).
+ //
+ ControlPc += 4;
+ Address = IntegerRegister[I.Dform_RA] + I.Dform_D;
+ Rt = I.Dform_RT;
+ READ_ULONG (Address, IntegerRegister[Rt]);
+ if (ARGUMENT_PRESENT (ContextPointers))
+ ContextPointers->IntegerContext[Rt] = (PULONG) Address;
+ }
+ }
+
+ //
+ // 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., in the prologue).
+ //
+ Iterator[0].EndPc = FunctionEntry->BeginAddress - 4;
+ Iterator[0].Increment = -4;
+ Iterator[0].Intent = UnwindReverse;
+ if ((ControlPc < FunctionEntry->BeginAddress) ||
+ (ControlPc >= (FunctionEntry->PrologEndAddress & ~3))) {
+ NOT_IMAGEHLP(*InFunction = TRUE);
+ Iterator[0].BeginPc = ((FunctionEntry->PrologEndAddress & ~3) - 4);
+ } else {
+ NOT_IMAGEHLP(*InFunction = FALSE);
+ Iterator[0].BeginPc = ControlPc - 4;
+ }
+ }
+
+ //
+ // Scan through the given instructions and reload callee registers
+ // as indicated.
+ //
+ NextPc = ContextRecord->Lr - 4;
+ UnwindGlue:
+ for (Piterator = Iterator; Piterator >= Iterator; Piterator--) {
+ for (Pc = Piterator->BeginPc;
+ Pc != Piterator->EndPc;
+ Pc += Piterator->Increment) {
+
+ READ_ULONG (Pc, I.Long);
+ Address = IntegerRegister[I.Dform_RA] + I.Dform_D;
+ Rt = I.Dform_RT;
+ switch (ClassifyInstruction (&I, Piterator->Intent,
+#ifdef _IMAGEHLP_SOURCE_
+ hProcess, ReadMemory, FunctionTableAccess,
+#endif
+ Pc, &Info)) {
+
+ //
+ // Move from Link Register (save LR in a GPR)
+ //
+ // In the usual case, the link register gets set by a call
+ // instruction so the PC value should point to the
+ // instruction that sets the link register. In an interrupt
+ // or exception frame, the link register and PC value are
+ // independent. By convention, fake prologues for these
+ // frames store the link register twice: once to the link
+ // register location, once to the faulting PC location.
+ //
+ // If 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 and the link register
+ // value should be updated.
+ //
+ case InstrMFLR:
+ ContextRecord->Lr = IntegerRegister[Rt];
+ if ( RestoredLr == FALSE ) {
+ NextPc = ContextRecord->Lr - 4;
+ RestoredLr = TRUE;
+ } else {
+ NextPc += 4;
+ }
+ continue; // Next PC
+
+ //
+ // Branch to Link Register (forward execution).
+ //
+ case InstrBLR:
+ NextPc = ContextRecord->Lr - 4;
+ break; // Terminate simulation--start next iterator.
+
+ //
+ // Move from Condition Register (save CR in a GPR)
+ //
+ case InstrMFCR:
+ ContextRecord->Cr = IntegerRegister[Rt];
+ continue; // Next PC
+
+ //
+ // Store word (save a GPR)
+ //
+ case InstrSTW:
+ //
+ // Even though a stw r.sp, xxxx in general is an invalid
+ // proloque instruction there are places in the kernel
+ // fake prologues (KiExceptionExit) where we must use this,
+ // so handle it.
+ //
+ READ_ULONG (Address, IntegerRegister[Rt]);
+ if (ARGUMENT_PRESENT (ContextPointers))
+ ContextPointers->IntegerContext[Rt] = (PULONG) Address;
+ continue; // Next PC
+
+ //
+ // Store word with update, Store word with update indexed
+ // (buy stack frame, updating stack pointer and link
+ // cell in storage)
+ //
+ case InstrSTWU:
+ Address = IntegerRegister[GPR1];
+ READ_ULONG(Address,IntegerRegister[GPR1]);
+ if (RestoredSp == FALSE) {
+ NOT_IMAGEHLP (*EstablisherFrame =
+ EstablisherFrameValue = ContextRecord->Gpr1);
+ RestoredSp = TRUE;
+ }
+ if (ARGUMENT_PRESENT (ContextPointers))
+ ContextPointers->IntegerContext[Rt] = (PULONG) Address;
+ continue; // Next PC
+
+ //
+ // Store floating point double (save an FPR)
+ //
+ case InstrSTFD:
+ READ_DOUBLE (Address, FloatingRegister[Rt]);
+ if (ARGUMENT_PRESENT (ContextPointers))
+ ContextPointers->FloatingContext[Rt] = (PDOUBLE) Address;
+ continue; // Next PC
+
+ //
+ // Move register. Certain forms are ignored based on the intent.
+ //
+ case InstrMR:
+ IntegerRegister[I.Xform_RA] = IntegerRegister[Rt];
+ continue; // Next PC
+ case InstrMRfwd:
+ IntegerRegister[Rt] = IntegerRegister[I.Xform_RA];
+ continue; // Next PC
+ case InstrMRr12:
+ IntegerRegister[Rt] = IntegerRegister[I.Xform_RA];
+ break; // Terminate search--start next iterator.
+
+ //
+ // Add immediate. Certain forms are ignored based on the intent.
+ //
+ case InstrADDIfwd:
+ IntegerRegister[Rt] = Address;
+ continue; // Next PC
+ case InstrADDIr12:
+ if (!ComputedSp) {
+ // No intervening instruction changes r.1, so compute
+ // addi r.12,r.1,N instead of addi r.12,r.12,N.
+ IntegerRegister[Rt] = IntegerRegister[GPR1];
+ }
+ IntegerRegister[Rt] += I.Dform_SI;
+ break; // Terminate search--start next iterator.
+
+ //
+ // Store with update while searching for the value of r.12
+ //
+ case InstrSTWUr12:
+ ComputedSp = TRUE;
+ Address = IntegerRegister[GPR1];
+ READ_ULONG(Address,IntegerRegister[GPR12]);
+ continue; // Next PC
+
+ //
+ // A call to a register save millicode.
+ //
+ case InstrSaveCode:
+ //
+ // Push an iterator to incorporate the actions of the
+ // millicode.
+ //
+ Piterator++;
+ Piterator->BeginPc = Info.FunctionEntry->EndAddress - 4;
+ Piterator->EndPc = Info.TargetPc - 4;
+ Piterator->Increment = -4;
+ Piterator->Intent = UnwindReverseR12;
+ //
+ // Push an iterator to determine the current value of r.12
+ //
+ Piterator++;
+ Piterator->BeginPc = Pc - 4;
+ Piterator->EndPc = Piterator[-2].EndPc;
+ Piterator->Increment = -4;
+ Piterator->Intent = UnwindR12;
+ ComputedSp = FALSE;
+ //
+ // Update the start of the original iterator so it can later
+ // resume where it left off.
+ //
+ Piterator[-2].BeginPc = Pc - 4;
+ Piterator++;
+ break; // Start the next iterator.
+
+ //
+ // A branch was encountered in the prologue to a routine
+ // identified as glue code. This should only happen from
+ // fake prologues such as those in the system exception
+ // handler.
+ //
+ // We handle it by pushing an iterator to incorporate
+ // the actions of the glue code prologue.
+ //
+ case InstrGlue:
+ //
+ // Check that we don't nest too deeply. Verify that
+ // we can push this iterator and the iterators for a
+ // glue sequence. There's no need to make this check
+ // elsewhere because B_OP is only recognized during
+ // UnwindReverse. Returing zero is the only error action
+ // we have.
+ //
+ if (Piterator - Iterator + 4
+ > sizeof (Iterator) / sizeof (Iterator[0]))
+ return 0;
+ //
+ // Push an iterator to incorporate the actions of the glue
+ // code's prologue. Check that we don't nest too deeply.
+ // Verify that we can push this iterator and the iterators
+ // for a glue sequence.
+ //
+ Piterator++;
+ Piterator->BeginPc
+ = (Info.FunctionEntry->PrologEndAddress & ~3) - 4;
+ Piterator->EndPc = Info.FunctionEntry->BeginAddress - 4;
+ Piterator->Increment = -4;
+ Piterator->Intent = UnwindReverse;
+ //
+ // Update the start of the original iterator so it can later
+ // resume where it left off.
+ //
+ Piterator[-1].BeginPc = Pc - 4;
+ Piterator++;
+ break; // Start the next iterator.
+
+ //
+ // Special "set establisher" instruction (rfi).
+ //
+ // Kernel fake prologues that can't use stwu (KiExceptionExit,
+ // KiAlternateExit) use an rfi instruction to tell the
+ // unwinder to update the establisher frame pointer using
+ // the current value of sp.
+ //
+ case InstrSetEstablisher:
+ NOT_IMAGEHLP (*EstablisherFrame =
+ EstablisherFrameValue = ContextRecord->Gpr1);
+ continue; // Next PC
+
+ //
+ // None of the above. Just ignore the instruction. It
+ // is presumed to be non-prologue code that has been
+ // merged into the prologue for scheduling purposes. It
+ // may also be improper code in a register save/restore
+ // millicode routine or unimportant code when
+ // determining the value of r.12.
+ //
+ case InstrIgnore:
+ default:
+ continue; // Next PC
+ }
+ break; // Start the next iterator.
+ } // end foreach Pc
+ } // end foreach Iterator
+
+ CheckForGlue:
+ //
+ // Check that we aren't at the end of the call chain. We now require
+ // that the link register at program start-up be zero. Unfortunately,
+ // this isn't always true. Also verify that the stack pointer remains
+ // valid.
+ //
+ if (NextPc == 0 || NextPc + 4 == 0
+#ifdef _IMAGEHLP_SOURCE_
+ || NextPc == 1
+#else
+ || EstablisherFrameValue < LowStackLimit
+ || EstablisherFrameValue > HighStackLimit
+ || (EstablisherFrameValue & 0x7) != 0
+#endif
+ )
+ return NextPc;
+
+ //
+ // Is the instruction at NextPc an branch?
+ //
+#ifdef _IMAGEHLP_SOURCE_
+ READ_ULONG (NextPc, I.Long);
+#else
+ if ( !NT_SUCCESS(TryReadUlong(NextPc, &I.Long)) ) {
+ return NextPc;
+ }
+#endif
+ if (I.Primary_Op != B_OP)
+ return NextPc;
+
+ //
+ // Compute branch target address, allowing for branch-relative
+ // and branch-absolute.
+ //
+ Pc = ((LONG)(I.Iform_LI) << 2) + (I.Iform_AA ? 0 : NextPc);
+
+ //
+ // If the branch target is contained in this function table entry,
+ // either this function is glue or it wasn't called via glue. This
+ // is the usual case.
+ //
+ if (FunctionEntry != NULL
+ && Pc >= FunctionEntry->BeginAddress
+ && Pc < FunctionEntry->EndAddress)
+ return NextPc;
+
+ //
+ // Allow for a stub glue in the thunk and a common ptrgl function
+ // where the stub glue does not have a function table entry.
+ //
+
+ if ((FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc)) != NULL) {
+ //
+ // The target instruction must be "lwz r.x,disp(r.2)".
+ //
+ READ_ULONG (Pc, I.Long);
+ if (I.Primary_Op == LWZ_OP && I.Dform_RA == GPR2) {
+ //
+ // The next instruction must be "b glue-code".
+ //
+ READ_ULONG (Pc + 4, I.Long);
+ if (I.Primary_Op == B_OP && I.Iform_LK) {
+ //
+ // Compute branch target address, allowing for
+ // branch-relative and branch-absolute.
+ //
+ Pc = ((LONG)(I.Iform_LI) << 2) + (I.Iform_AA ? 0 : Pc + 4);
+ FunctionEntry = (PRUNTIME_FUNCTION)RtlLookupFunctionEntry(Pc);
+ }
+ }
+ }
+
+ //
+ // Determine whether the branch target is glue code.
+ //
+ if (!(FunctionEntry != NULL
+#ifndef FUNCTION_ENTRY_IS_IMAGE_STYLE
+ && FunctionEntry->ExceptionHandler == 0
+ && (ULONG)FunctionEntry->HandlerData == 3
+#else
+ && (FunctionEntry->BeginAddress <
+ FunctionEntry->PrologEndAddress)
+ && (FunctionEntry->PrologEndAddress & 3) == 1
+#endif
+ ))
+ return NextPc;
+
+ //
+ // Unwind the glue code prologue. We won't loop because
+ // next time through, the branch target will be contained in
+ // the function table entry.
+ //
+#ifdef _IMAGEHLP_SOURCE_
+ SavedFunctionEntry = *FunctionEntry;
+ FunctionEntry = &SavedFunctionEntry;
+#endif
+ Iterator[0].EndPc = FunctionEntry->BeginAddress - 4;
+ Iterator[0].Increment = -4;
+ Iterator[0].Intent = UnwindReverse;
+ Iterator[0].BeginPc = ((FunctionEntry->PrologEndAddress & ~3) - 4);
+ goto UnwindGlue;
+}
+
+#undef NOT_IMAGEHLP
diff --git a/private/ntos/rtl/ppc/xcptmisc.s b/private/ntos/rtl/ppc/xcptmisc.s
new file mode 100644
index 000000000..bfd5bb405
--- /dev/null
+++ b/private/ntos/rtl/ppc/xcptmisc.s
@@ -0,0 +1,810 @@
+// 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 capture
+// and restore the caller's context, 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:
+//
+// Rick Simpson 10-Sep-1993
+//
+// Based on the MIPS routines xxcaptur.s and xcptmisc.s, by
+// David N. Cutler (davec) 12-Sep-1990
+//
+// Environment:
+//
+// Any mode.
+//
+// Revision History:
+//
+// Tom Wood (twood) 1-Nov-1993
+// Rewrite RtlpExecuteHandlerForException, RtlpExceptionHandler,
+// RtlpExecuteHandlerForUnwind, and RtlpUnwindHandler to be more
+// like the MIPS versions.
+//--
+//list(off)
+#include "ksppc.h"
+//list(on)
+ .extern NtContinue
+
+ .set PCR_SAVE4, PcGprSave + 8
+ .set PCR_SAVE5, PcGprSave + 12
+ .set PCR_SAVE6, PcGprSave + 16
+ .set sprg.1, 1
+
+//++
+//
+// VOID
+// RtlCaptureContext (
+// OUT PCONTEXT ContextRecord
+// )
+//
+// Routine Description:
+//
+// This function captures the context of the caller in the specified
+// context record.
+//
+// N.B. The context IS guaranteed to be doubleword (8-byte) aligned,
+// as are all structs in PowerPC containing "double"s.
+//
+// Arguments:
+//
+// ContextRecord (r.3) - Supplies the address of a context record.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+// Presumed user-mode MSR value -- ILE | EE | PR | FP | ME | FE0 | IR |
+// FE1 | DR | LE
+
+ .set UserMSR, 0x0001F931
+
+ LEAF_ENTRY(RtlCaptureContext)
+
+//
+// Save the floating-point context
+//
+
+ stfd f.0, CxFpr0 (r.3)
+ mffs f.0 // fetch FPSCR into f.0
+ stfd f.1, CxFpr1 (r.3)
+ stfd f.2, CxFpr2 (r.3)
+ stfd f.3, CxFpr3 (r.3)
+ stfd f.4, CxFpr4 (r.3)
+ stfd f.5, CxFpr5 (r.3)
+ stfd f.6, CxFpr6 (r.3)
+ stfd f.7, CxFpr7 (r.3)
+ stfd f.8, CxFpr8 (r.3)
+ stfd f.9, CxFpr9 (r.3)
+ stfd f.10, CxFpr10 (r.3)
+ stfd f.11, CxFpr11 (r.3)
+ stfd f.12, CxFpr12 (r.3)
+ stfd f.13, CxFpr13 (r.3)
+ stfd f.14, CxFpr14 (r.3)
+ stfd f.15, CxFpr15 (r.3)
+ stfd f.16, CxFpr16 (r.3)
+ stfd f.17, CxFpr17 (r.3)
+ stfd f.18, CxFpr18 (r.3)
+ stfd f.19, CxFpr19 (r.3)
+ stfd f.20, CxFpr20 (r.3)
+ stfd f.21, CxFpr21 (r.3)
+ stfd f.22, CxFpr22 (r.3)
+ stfd f.23, CxFpr23 (r.3)
+ stfd f.24, CxFpr24 (r.3)
+ stfd f.25, CxFpr25 (r.3)
+ stfd f.26, CxFpr26 (r.3)
+ stfd f.27, CxFpr27 (r.3)
+ stfd f.28, CxFpr28 (r.3)
+ stfd f.29, CxFpr29 (r.3)
+ stfd f.30, CxFpr30 (r.3)
+ stfd f.31, CxFpr31 (r.3)
+ stfd f.0, CxFpscr (r.3) // store FPSCR in context record
+
+//
+// Save the integer context
+//
+
+ stw r.0, CxGpr0 (r.3)
+ mfcr r.0
+ stw r.1, CxGpr1 (r.3)
+ stw r.2, CxGpr2 (r.3)
+ stw r.3, CxGpr3 (r.3)
+ stw r.4, CxGpr4 (r.3)
+ mfxer r.4
+ stw r.5, CxGpr5 (r.3)
+ mflr r.5
+ stw r.6, CxGpr6 (r.3)
+ mfctr r.6
+ stw r.7, CxGpr7 (r.3)
+ stw r.8, CxGpr8 (r.3)
+ stw r.9, CxGpr9 (r.3)
+ stw r.10, CxGpr10 (r.3)
+ stw r.11, CxGpr11 (r.3)
+ stw r.12, CxGpr12 (r.3)
+ stw r.13, CxGpr13 (r.3)
+ stw r.14, CxGpr14 (r.3)
+ stw r.15, CxGpr15 (r.3)
+ stw r.16, CxGpr16 (r.3)
+ stw r.17, CxGpr17 (r.3)
+ stw r.18, CxGpr18 (r.3)
+ stw r.19, CxGpr19 (r.3)
+ stw r.20, CxGpr20 (r.3)
+ stw r.21, CxGpr21 (r.3)
+ stw r.22, CxGpr22 (r.3)
+ stw r.23, CxGpr23 (r.3)
+ stw r.24, CxGpr24 (r.3)
+ stw r.25, CxGpr25 (r.3)
+ stw r.26, CxGpr26 (r.3)
+ stw r.27, CxGpr27 (r.3)
+
+//
+// Test high-order bit of stack pointer value
+// 0 => caller in user mode (address <= 0x7FFFFFFF)
+// 1 => caller in kernel mode (address >= 0x80000000)
+
+ cmpwi r.1, 0
+
+//
+// Save special registers (some integer, some control)
+//
+
+ stw r.0, CxCr (r.3) // condition register
+ stw r.4, CxXer (r.3) // fixed point exception register
+ stw r.5, CxIar (r.3) // instruction address (current PC)
+ stw r.5, CxLr (r.3) // link register (return address)
+ stw r.6, CxCtr (r.3) // count register
+
+//
+// Save actual MSR value if caller is kernel, otherwise save
+// canonical user-mode MSR value
+//
+
+ lis r.0, UserMSR > 16
+ ori r.0, r.0, UserMSR & 0xFFFF
+ bnl Cap1 // branch around if user mode
+ mfmsr r.0
+Cap1:
+ stw r.28, CxGpr28 (r.3)
+ stw r.29, CxGpr29 (r.3)
+ stw r.30, CxGpr30 (r.3)
+ stw r.31, CxGpr31 (r.3)
+
+ stw r.0, CxMsr (r.3) // save MSR value
+
+//
+// Set context record flags, and exit
+//
+
+ lis r.0, CONTEXT_FULL > 16
+ ori r.0, r.0, CONTEXT_FULL & 0xFFFF
+ stw r.0, CxContextFlags (r.3)
+
+ LEAF_EXIT (RtlCaptureContext)
+
+//++
+//
+// VOID
+// RtlpRestoreContext (
+// IN PCONTEXT ContextRecord,
+// IN PEXCEPTION_RECORD ExceptionRecord OPTIONAL
+// )
+//
+// Routine Description:
+//
+// This function restores the context of the caller from the specified
+// context record.
+//
+// N.B. The context IS guaranteed to be doubleword (8-byte) aligned,
+// as are all structs in PowerPC containing "double"s.
+//
+// N.B. This is a special routine that is used by RtlUnwind to restore
+// context in the current mode.
+//
+// Arguments:
+//
+// ContextRecord (r.3) - Supplies the address of a context record.
+//
+// ExceptionRecord (r.4) - 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.
+//
+
+ cmplwi cr.1, r.4, 0 // ExceptionRecord supplied?
+ cmpwi cr.0, r.1, 0 // test kernel/user mode
+ beq cr.1, NoExcpRec // if eq, no exception record
+ lwz r.6, ErExceptionCode(r.4) // get exception code
+ LWI(r.8,STATUS_LONGJUMP) // get long jump status code
+ cmplw cr.1, r.6, r.8 // is long jump status?
+ lwz r.5, ErExceptionInformation(r.4)// get address of jump buffer
+ bne cr.1, NoExcpRec // if ne, not a long jump
+
+// Get non-volatile control context
+
+ lwz r.7, JbIar(r.5)
+ lwz r.8, JbCr(r.5)
+ lwz r.9, JbGpr1(r.5)
+ lwz r.10,JbGpr2(r.5)
+
+// Get non-volatile Floating Point context from jump buffer
+
+ lfd f.14, JbFpr14(r.5)
+ lfd f.15, JbFpr15(r.5)
+ lfd f.16, JbFpr16(r.5)
+ lfd f.17, JbFpr17(r.5)
+ lfd f.18, JbFpr18(r.5)
+ lfd f.19, JbFpr19(r.5)
+ lfd f.20, JbFpr20(r.5)
+ lfd f.21, JbFpr21(r.5)
+ lfd f.22, JbFpr22(r.5)
+ lfd f.23, JbFpr23(r.5)
+ lfd f.24, JbFpr24(r.5)
+ lfd f.25, JbFpr25(r.5)
+ lfd f.26, JbFpr26(r.5)
+ lfd f.27, JbFpr27(r.5)
+ lfd f.28, JbFpr28(r.5)
+ lfd f.29, JbFpr29(r.5)
+ lfd f.30, JbFpr30(r.5)
+ lfd f.31, JbFpr31(r.5)
+
+// Get non-volatile Integer context from jump buffer
+
+ lwz r.14, JbGpr14(r.5)
+ lwz r.15, JbGpr15(r.5)
+ lwz r.16, JbGpr16(r.5)
+ lwz r.17, JbGpr17(r.5)
+ lwz r.18, JbGpr18(r.5)
+ lwz r.19, JbGpr19(r.5)
+ lwz r.20, JbGpr20(r.5)
+ lwz r.21, JbGpr21(r.5)
+ lwz r.22, JbGpr22(r.5)
+ lwz r.23, JbGpr23(r.5)
+ lwz r.24, JbGpr24(r.5)
+ lwz r.25, JbGpr25(r.5)
+ lwz r.26, JbGpr26(r.5)
+ lwz r.27, JbGpr27(r.5)
+ lwz r.28, JbGpr28(r.5)
+ lwz r.29, JbGpr29(r.5)
+ lwz r.30, JbGpr30(r.3)
+ lwz r.31, JbGpr31(r.3)
+
+//
+// Save non-volatile control context in context record
+//
+
+ stw r.7, CxIar(r.3)
+ stw r.8, CxCr(r.3)
+ stw r.9, CxGpr1(r.3)
+ stw r.10,CxGpr2(r.3)
+
+//
+// Save non-volatile Floating Point and Integer registers in context record
+// plj note: do we really need to do this if we are in kernel mode?
+//
+
+ stfd f.14, CxFpr14(r.3)
+ stfd f.15, CxFpr15(r.3)
+ stfd f.16, CxFpr16(r.3)
+ stfd f.17, CxFpr17(r.3)
+ stfd f.18, CxFpr18(r.3)
+ stfd f.19, CxFpr19(r.3)
+ stfd f.20, CxFpr20(r.3)
+ stfd f.21, CxFpr21(r.3)
+ stfd f.22, CxFpr22(r.3)
+ stfd f.23, CxFpr23(r.3)
+ stfd f.24, CxFpr24(r.3)
+ stfd f.25, CxFpr25(r.3)
+ stfd f.26, CxFpr26(r.3)
+ stfd f.27, CxFpr27(r.3)
+ stfd f.28, CxFpr28(r.3)
+ stfd f.29, CxFpr29(r.3)
+ stfd f.30, CxFpr30(r.3)
+ stfd f.31, CxFpr31(r.3)
+ stw r.14, CxGpr14(r.3)
+ stw r.15, CxGpr15(r.3)
+ stw r.16, CxGpr16(r.3)
+ stw r.17, CxGpr17(r.3)
+ stw r.18, CxGpr18(r.3)
+ stw r.19, CxGpr19(r.3)
+ stw r.20, CxGpr20(r.3)
+ stw r.21, CxGpr21(r.3)
+ stw r.22, CxGpr22(r.3)
+ stw r.23, CxGpr23(r.3)
+ stw r.24, CxGpr24(r.3)
+ stw r.25, CxGpr25(r.3)
+ stw r.26, CxGpr26(r.3)
+ stw r.27, CxGpr27(r.3)
+ stw r.28, CxGpr28(r.3)
+ stw r.29, CxGpr29(r.3)
+ stw r.30, CxGpr30(r.3)
+ stw r.31, CxGpr31(r.3)
+
+//
+// If from kernel mode, continue restoration with volatile registers.
+//
+
+ blt cr.0, ResKrnlVol
+
+//
+// If called in user mode (stack pointer in r.1 is < 0x80000000), branch
+// directly to the "continue" system service to continue execution.
+//
+// If we fell thru from above, the blt below will fall thru also, and it's
+// free on PowerPC.
+//
+
+NoExcpRec:
+
+ blt cr.0, ResKrnlAll // branch if kernel mode
+
+ lwz r.5, [toc] NtContinue (r.toc) // get function desc. addr
+ lwz r.0, 0(r.5) // get entry point address
+ li r.4, 0 // set "test alert" arg FALSE
+ mtctr r.0 // into Ctr
+ lwz r.2, 4(r.5) // get TOC address
+ bctr // branch to system service
+
+ResKrnlAll:
+
+//
+// Restore the non-volatile Floating Point context
+//
+
+ lfd f.14, CxFpr14 (r.3)
+ lfd f.15, CxFpr15 (r.3)
+ lfd f.16, CxFpr16 (r.3)
+ lfd f.17, CxFpr17 (r.3)
+ lfd f.18, CxFpr18 (r.3)
+ lfd f.19, CxFpr19 (r.3)
+ lfd f.20, CxFpr20 (r.3)
+ lfd f.21, CxFpr21 (r.3)
+ lfd f.22, CxFpr22 (r.3)
+ lfd f.23, CxFpr23 (r.3)
+ lfd f.24, CxFpr24 (r.3)
+ lfd f.25, CxFpr25 (r.3)
+ lfd f.26, CxFpr26 (r.3)
+ lfd f.27, CxFpr27 (r.3)
+ lfd f.28, CxFpr28 (r.3)
+ lfd f.29, CxFpr29 (r.3)
+ lfd f.30, CxFpr30 (r.3)
+ lfd f.31, CxFpr31 (r.3)
+
+//
+// Restore the non-volatile Integer context
+//
+
+ lwz r.14, CxGpr14 (r.3)
+ lwz r.15, CxGpr15 (r.3)
+ lwz r.16, CxGpr16 (r.3)
+ lwz r.17, CxGpr17 (r.3)
+ lwz r.18, CxGpr18 (r.3)
+ lwz r.19, CxGpr19 (r.3)
+ lwz r.20, CxGpr20 (r.3)
+ lwz r.21, CxGpr21 (r.3)
+ lwz r.22, CxGpr22 (r.3)
+ lwz r.23, CxGpr23 (r.3)
+ lwz r.24, CxGpr24 (r.3)
+ lwz r.25, CxGpr25 (r.3)
+ lwz r.26, CxGpr26 (r.3)
+ lwz r.27, CxGpr27 (r.3)
+ lwz r.28, CxGpr28 (r.3)
+ lwz r.29, CxGpr29 (r.3)
+ lwz r.30, CxGpr30 (r.3)
+ lwz r.31, CxGpr31 (r.3)
+
+//
+// Restore the volatile floating-point context
+//
+
+ResKrnlVol:
+
+ lfd f.13, CxFpscr (r.3) // fetch FPSCR into f.13
+ lfd f.0, CxFpr0 (r.3)
+ lfd f.1, CxFpr1 (r.3)
+ lfd f.2, CxFpr2 (r.3)
+ lfd f.3, CxFpr3 (r.3)
+ lfd f.4, CxFpr4 (r.3)
+ lfd f.5, CxFpr5 (r.3)
+ lfd f.6, CxFpr6 (r.3)
+ lfd f.7, CxFpr7 (r.3)
+ lfd f.8, CxFpr8 (r.3)
+ lfd f.9, CxFpr9 (r.3)
+ lfd f.10, CxFpr10 (r.3)
+ lfd f.11, CxFpr11 (r.3)
+ lfd f.12, CxFpr12 (r.3)
+ mtfsf 0xff, f.13 // set the FPSCR value
+ lfd f.13, CxFpr13 (r.3)
+
+//
+// Restore the volatile integer and control context
+//
+
+ lwz r.0, CxCr (r.3) // condition register
+ lwz r.4, CxXer (r.3) // fixed point exception register
+ lwz r.5, CxLr (r.3) // link register (return address)
+ lwz r.6, CxCtr (r.3) // count register
+
+ mtcrf 0xff, r.0 // restore CR
+ mtxer r.4 // restore XER
+ mtlr r.5 // restore LR
+ mtctr r.6 // restore CTR
+
+ lwz r.0, CxGpr0 (r.3) // restore GPRs
+ lwz r.2, CxGpr2 (r.3)
+ lwz r.4, CxGpr4 (r.3)
+ lwz r.5, CxGpr5 (r.3)
+ lwz r.6, CxGpr6 (r.3)
+ lwz r.9, CxGpr9 (r.3)
+ lwz r.10, CxGpr10 (r.3)
+ lwz r.11, CxGpr11 (r.3)
+ lwz r.12, CxGpr12 (r.3)
+
+ mfmsr r.8 // fetch current MSR value
+ rlwinm r.8, r.8, 0, ~MASK_SPR(MSR_EE,1) // turn off EE bit
+ mtmsr r.8 // disable interrupts
+ cror 0,0,0 // N.B. 603e/ev Errata 15
+
+ lwz r.1, CxGpr3 (r.3)
+ lwz r.7, CxGpr7 (r.3)
+ stw r.1, KiPcr+PCR_SAVE4 (r.0)
+ lwz r.1, CxGpr8 (r.3)
+ stw r.7, KiPcr+PCR_SAVE5 (r.0)
+ stw r.1, KiPcr+PCR_SAVE6 (r.0)
+
+ lwz r.1, CxGpr1 (r.3) // This MUST BE AFTER
+ // interrupts disabled
+ lwz r.7, CxIar (r.3) // instruction address (current PC)
+ lwz r.3, CxMsr (r.3) // machine state register
+
+ mfsprg r.8, sprg.1
+
+ DUMMY_ENTRY(RtlpRestoreContextRfiJump)
+ b $ // This is changed to be a branch
+ // to KSEG0 code at init time
+RtlpRestoreContext.end:
+
+//
+// Define call frame for calling exception handlers.
+//
+
+ .struct 0
+CfBackChain: .space 4 // chain to previous call frame
+ .space 5*4 // remaining part of frame header
+ .space 8*4 // 8 words space for call args
+CfDispContext: .space 4 // space to save the incoming
+ // Dispatcher Context ptr
+CfSavedRtoc: .space 4 // space to save rtoc
+ .space 4 // space to save LR
+ .align 3 // force frame length to multiple of
+CfEnd: // eight bytes
+
+//++
+//
+// 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 (r.3) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
+// of the exception handler that is to be called.
+//
+// ContextRecord (r.5) - Supplies a pointer to a context record.
+//
+// DispatcherContext (r.6) - Supplies a pointer to the dispatcher context
+// record.
+//
+// ExceptionRoutine (r.7) - supplies a pointer to the function descriptor
+// for 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.
+//
+//--
+
+ NESTED_ENTRY_EX (RtlpExecuteHandlerForException,CfEnd,0,0,RtlpExceptionHandler,0)
+
+ stw r.toc,CfSavedRtoc(r.sp) // save rtoc
+
+ PROLOGUE_END (RtlpExecuteHandlerForException)
+
+ lwz r.0, 0 (r.7) // get entry point addr from descriptor
+ mtlr r.0 // LR <- entry point addr
+ lwz r.toc,4(r.7) // rtoc <- TOC addr from descriptor
+ stw r.6, CfDispContext (r.sp) // save Dispatcher Context where
+ // RtlpExectionHandler can find it
+
+ blrl // call the exception handler
+
+ lwz r.toc,CfSavedRtoc(r.sp) // restore rtoc
+ NESTED_EXIT (RtlpExecuteHandlerForException,CfEnd,0,0)
+
+//++
+//
+// 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 (r.3) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (r.5) - Supplies a pointer to a context record.
+//
+// DispatcherContext (r.6) - 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)
+ lwz r.0, ErExceptionFlags (r.3) // get exception flags
+ li r.3, ExceptionContinueSearch // set usual disposition value
+ andi. r.0, r.0, EXCEPTION_UNWIND // check if unwind in progress
+ bnelr // if neq, unwind in progress
+ // return continue search disposition
+
+//
+// Unwind is not in progress - return nested exception disposition.
+//
+
+ lwz r.7,CfDispContext-CfEnd(r.4) // get dispatcher context address
+ li r.3, ExceptionNestedException // set disposition value
+ lwz r.0, DcEstablisherFrame(r.7) // copy the establisher environment
+ stw r.0, DcEstablisherFrame (r.6) // to current dispatcher context
+ LEAF_EXIT (RtlpExceptionHandler)
+
+//++
+//
+// 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 (r.3) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
+// of the exception handler that is to be called.
+//
+// ContextRecord (r.5) - Supplies a pointer to a context record.
+//
+// DispatcherContext (r.6) - Supplies a pointer to the dispatcher context
+// record.
+//
+// ExceptionRoutine (r.7) - 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.
+//
+//--
+
+ NESTED_ENTRY_EX (RtlpExecuteHandlerForUnwind,CfEnd,0,0,RtlpUnwindHandler,0)
+
+ stw r.toc, CfSavedRtoc(r.sp) // save rtoc
+
+ PROLOGUE_END (RtlpExecuteHandlerForUnwind)
+
+ lwz r.0, 0 (r.7) // get entry point addr from descriptor
+ mtlr r.0 // LR <- entry point addr
+ lwz r.toc, 4 (r.7) // rtoc <- TOC addr from descriptor
+ stw r.6, CfDispContext (r.sp) // save Dispatcher Context where
+ // RtlpExectionHandler can find it
+
+ blrl // call the exception handler
+
+ lwz r.toc, CfSavedRtoc(r.sp) // restore rtoc
+
+ NESTED_EXIT (RtlpExecuteHandlerForUnwind,CfEnd,0,0)
+
+//++
+//
+// 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 (r.3) - Supplies a pointer to an exception record.
+//
+// EstablisherFrame (r.4) - Supplies the frame pointer of the establisher
+// of this exception handler.
+//
+// ContextRecord (r.5) - Supplies a pointer to a context record.
+//
+// DispatcherContext (r.6) - 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)
+
+ lwz r.0, ErExceptionFlags (r.3) // get exception flags
+ li r.3, ExceptionContinueSearch // set usual disposition value
+ andi. r.0, r.0, EXCEPTION_UNWIND // check if unwind in progress
+ beqlr // if eq, unwind not in progress
+ // return continue search disposition
+
+//
+// Unwind is in progress - return collided exception disposition.
+//
+
+ lwz r.7, CfDispContext-CfEnd (r.4) // get dispatcher context address
+ lwz r.0, DcControlPc (r.7) // copy the establisher frame's
+ lwz r.3, DcFunctionEntry (r.7) // dispatcher context to the
+ lwz r.4, DcEstablisherFrame (r.7) // current dispatcher context
+ lwz r.5, DcContextRecord (r.7)
+ stw r.0, DcControlPc (r.6)
+ stw r.3, DcFunctionEntry (r.6)
+ stw r.4, DcEstablisherFrame (r.6)
+ stw r.5, DcContextRecord (r.6)
+ li r.3, ExceptionCollidedUnwind // return collided unwind disposition
+ LEAF_EXIT (RtlpUnwindHandler)
+
+//++
+//
+// 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 (r.3) - Supplies a pointer to a variable that is to receive
+// the low limit of the stack.
+//
+// HighLimit (r.4) - Supplies a pointer to a variable that is to receive
+// the high limit of the stack.
+//
+// Return Value:
+//
+// None.
+//
+//--
+
+ LEAF_ENTRY (RtlpGetStackLimits)
+
+#ifdef ROS_DEBUG
+ li r.0, 0 // force return low=0, high=2GB for debug
+ stw r.0, 0(r.3)
+ lis r.0, 0x7FFF
+ ori r.0, r.0, 0xFFFF
+ stw r.0, 0(r.4)
+ blr
+#endif
+
+ cmpwi r.sp, 0 // if stack ptr < 0x80000000, user mode
+ bnl Sl10 // branch if user mode
+
+//
+// Current mode is kernel - compute stack limits.
+//
+
+ lwz r.6, KiPcr+PcInitialStack(r.0) // get high limit of kernel stack
+ lwz r.7, KiPcr+PcStackLimit(r.0) // get low limit of kernel stack
+ stw r.6, 0 (r.4) // store high limit
+ stw r.7, 0 (r.3) // store low limit
+ ALTERNATE_EXIT (RtlpGetStackLimits)
+
+//
+// Current mode is user - get stack limits from the TEB.
+//
+
+Sl10:
+
+// Fast-path system service returns TEB address in r.3, having killed only CR
+
+ lwz r.0, TeStackLimit (r.13) // get low limit of user stack
+ lwz r.6, TeStackBase (r.13) // get high limit of user stack
+ stw r.0, 0 (r.3) // store low stack limit
+ stw r.6, 0 (r.4) // store high stack limit
+ LEAF_EXIT (RtlpGetStackLimits)
diff --git a/private/ntos/rtl/prefix.c b/private/ntos/rtl/prefix.c
new file mode 100644
index 000000000..bef8028c3
--- /dev/null
+++ b/private/ntos/rtl/prefix.c
@@ -0,0 +1,2401 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Prefix.c
+
+Abstract:
+
+ This module implements the prefix table utility. The two structures
+ used in a prefix table are the PREFIX_TABLE and PREFIX_TABLE_ENTRY.
+ Each table has one prefix table and multiple prefix table entries
+ corresponding to each prefix stored in the table.
+
+ A prefix table is a list of prefix trees, where each tree contains
+ the prefixes corresponding to a particular name length (i.e., all
+ prefixes of length 1 are stored in one tree, prefixes of length 2
+ are stored in another tree, and so forth). A prefixes name length
+ is the number of separate names that appear in the string, and not
+ the number of characters in the string (e.g., Length("\alpha\beta") = 2).
+
+ The elements of each tree are ordered lexicalgraphically (case blind)
+ using a splay tree data structure. If two or more prefixes are identical
+ except for case then one of the corresponding table entries is actually
+ in the tree, while the other entries are in a circular linked list joined
+ with the tree member.
+
+Author:
+
+ Gary Kimura [GaryKi] 3-Aug-1989
+
+Environment:
+
+ Pure utility routine
+
+Revision History:
+
+ 08-Mar-1993 JulieB Moved Upcase Macro to ntrtlp.h.
+
+--*/
+
+#include "ntrtlp.h"
+
+//
+// Local procedures and types used only in this package
+//
+
+typedef enum _COMPARISON {
+ IsLessThan,
+ IsPrefix,
+ IsEqual,
+ IsGreaterThan
+} COMPARISON;
+
+CLONG
+ComputeNameLength(
+ IN PSTRING Name
+ );
+
+COMPARISON
+CompareNamesCaseSensitive (
+ IN PSTRING Prefix,
+ IN PSTRING Name
+ );
+
+CLONG
+ComputeUnicodeNameLength(
+ IN PUNICODE_STRING Name
+ );
+
+COMPARISON
+CompareUnicodeStrings (
+ IN PUNICODE_STRING Prefix,
+ IN PUNICODE_STRING Name,
+ IN ULONG CaseInsensitiveIndex
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,ComputeNameLength)
+#pragma alloc_text(PAGE,CompareNamesCaseSensitive)
+#pragma alloc_text(PAGE,PfxInitialize)
+#pragma alloc_text(PAGE,PfxInsertPrefix)
+#pragma alloc_text(PAGE,PfxRemovePrefix)
+#pragma alloc_text(PAGE,PfxFindPrefix)
+#pragma alloc_text(PAGE,ComputeUnicodeNameLength)
+#pragma alloc_text(PAGE,CompareUnicodeStrings)
+#pragma alloc_text(PAGE,RtlInitializeUnicodePrefix)
+#pragma alloc_text(PAGE,RtlInsertUnicodePrefix)
+#pragma alloc_text(PAGE,RtlRemoveUnicodePrefix)
+#pragma alloc_text(PAGE,RtlFindUnicodePrefix)
+#pragma alloc_text(PAGE,RtlNextUnicodePrefix)
+#endif
+
+
+//
+// The node type codes for the prefix data structures
+//
+
+#define RTL_NTC_PREFIX_TABLE ((CSHORT)0x0200)
+#define RTL_NTC_ROOT ((CSHORT)0x0201)
+#define RTL_NTC_INTERNAL ((CSHORT)0x0202)
+
+
+VOID
+PfxInitialize (
+ IN PPREFIX_TABLE PrefixTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes a prefix table record to the empty state.
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table being initialized
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ PrefixTable->NodeTypeCode = RTL_NTC_PREFIX_TABLE;
+
+ PrefixTable->NameLength = 0;
+
+ PrefixTable->NextPrefixTree = (PPREFIX_TABLE_ENTRY)PrefixTable;
+
+ //
+ // return to our caller
+ //
+
+ return;
+}
+
+
+BOOLEAN
+PfxInsertPrefix (
+ IN PPREFIX_TABLE PrefixTable,
+ IN PSTRING Prefix,
+ IN PPREFIX_TABLE_ENTRY PrefixTableEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine inserts a new prefix into the specified prefix table
+
+Arguments:
+
+ PrefixTable - Supplies the target prefix table
+
+ Prefix - Supplies the string to be inserted in the prefix table
+
+ PrefixTableEntry - Supplies the entry to use to insert the prefix
+
+Return Value:
+
+ BOOLEAN - TRUE if the Prefix is not already in the table, and FALSE
+ otherwise
+
+--*/
+
+{
+ ULONG PrefixNameLength;
+
+ PPREFIX_TABLE_ENTRY PreviousTree;
+ PPREFIX_TABLE_ENTRY CurrentTree;
+ PPREFIX_TABLE_ENTRY NextTree;
+
+ PPREFIX_TABLE_ENTRY Node;
+
+ COMPARISON Comparison;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Determine the name length of the input string
+ //
+
+ PrefixNameLength = ComputeNameLength(Prefix);
+
+ //
+ // Setup parts of the prefix table entry that we will always need
+ //
+
+ PrefixTableEntry->NameLength = (CSHORT)PrefixNameLength;
+ PrefixTableEntry->Prefix = Prefix;
+
+ RtlInitializeSplayLinks(&PrefixTableEntry->Links);
+
+ //
+ // find the corresponding tree, or find where the tree should go
+ //
+
+ PreviousTree = (PPREFIX_TABLE_ENTRY)PrefixTable;
+ CurrentTree = PreviousTree->NextPrefixTree;
+
+ while (CurrentTree->NameLength > (CSHORT)PrefixNameLength) {
+
+ PreviousTree = CurrentTree;
+ CurrentTree = CurrentTree->NextPrefixTree;
+
+ }
+
+ //
+ // If the name length of the current tree is not equal to the
+ // prefix name length then the tree does not exist and we need
+ // to make a new tree node.
+ //
+
+ if (CurrentTree->NameLength != (CSHORT)PrefixNameLength) {
+
+ //
+ // Insert the new prefix entry to the list between
+ // previous and current tree
+ //
+
+ PreviousTree->NextPrefixTree = PrefixTableEntry;
+ PrefixTableEntry->NextPrefixTree = CurrentTree;
+
+ //
+ // And set the node type code
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_ROOT;
+
+ //
+ // And tell our caller everything worked fine
+ //
+
+ return TRUE;
+
+ }
+
+ //
+ // The tree does exist so now search the tree for our
+ // position in it. We only exit the loop if we've inserted
+ // a new node, and node is left is left pointing to the
+ // tree position
+ //
+
+ Node = CurrentTree;
+
+ while (TRUE) {
+
+ //
+ // Compare the prefix in the tree with the prefix we want
+ // to insert
+ //
+
+ Comparison = CompareNamesCaseSensitive(Node->Prefix, Prefix);
+
+ //
+ // If we do match case sensitive then we cannot add
+ // this prefix so we return false. Note this is the
+ // only condition where we return false
+ //
+
+ if (Comparison == IsEqual) {
+
+ return FALSE;
+ }
+
+ //
+ // If the tree prefix is greater than the new prefix then
+ // we go down the left subtree
+ //
+
+ if (Comparison == IsGreaterThan) {
+
+ //
+ // We want to go down the left subtree, first check to see
+ // if we have a left subtree
+ //
+
+ if (RtlLeftChild(&Node->Links) == NULL) {
+
+ //
+ // there isn't a left child so we insert ourselves as the
+ // new left child
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_INTERNAL;
+ PrefixTableEntry->NextPrefixTree = NULL;
+
+ RtlInsertAsLeftChild(&Node->Links, &PrefixTableEntry->Links);
+
+ //
+ // and exit the while loop
+ //
+
+ break;
+
+ } else {
+
+ //
+ // there is a left child so simply go down that path, and
+ // go back to the top of the loop
+ //
+
+ Node = CONTAINING_RECORD( RtlLeftChild(&Node->Links),
+ PREFIX_TABLE_ENTRY,
+ Links );
+
+ }
+
+ } else {
+
+ //
+ // The tree prefix is either less than or a proper prefix
+ // of the new string. We treat both cases a less than when
+ // we do insert. So we want to go down the right subtree,
+ // first check to see if we have a right subtree
+ //
+
+ if (RtlRightChild(&Node->Links) == NULL) {
+
+ //
+ // These isn't a right child so we insert ourselves as the
+ // new right child
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_INTERNAL;
+ PrefixTableEntry->NextPrefixTree = NULL;
+
+ RtlInsertAsRightChild(&Node->Links, &PrefixTableEntry->Links);
+
+ //
+ // and exit the while loop
+ //
+
+ break;
+
+ } else {
+
+ //
+ // there is a right child so simply go down that path, and
+ // go back to the top of the loop
+ //
+
+ Node = CONTAINING_RECORD( RtlRightChild(&Node->Links),
+ PREFIX_TABLE_ENTRY,
+ Links );
+ }
+
+ }
+
+ }
+
+ //
+ // Now that we've inserted the new node we can splay the tree.
+ // To do this we need to remember how we find this tree in the root
+ // tree list, set the root to be an internal, splay, the tree, and
+ // then setup the new root node. Note: we cannot splay the prefix table
+ // entry because it might be a case match node so we only splay
+ // the Node variable, which for case match insertions is the
+ // internal node for the case match and for non-case match insertions
+ // the Node variable is the parent node.
+ //
+
+ //
+ // Save a pointer to the next tree, we already have the previous tree
+ //
+
+ NextTree = CurrentTree->NextPrefixTree;
+
+ //
+ // Reset the current root to be an internal node
+ //
+
+ CurrentTree->NodeTypeCode = RTL_NTC_INTERNAL;
+ CurrentTree->NextPrefixTree = NULL;
+
+ //
+ // Splay the tree and get the root
+ //
+
+ Node = CONTAINING_RECORD(RtlSplay(&Node->Links), PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Set the new root's node type code and make it part of the
+ // root tree list
+ //
+
+ Node->NodeTypeCode = RTL_NTC_ROOT;
+ PreviousTree->NextPrefixTree = Node;
+ Node->NextPrefixTree = NextTree;
+
+ //
+ // tell our caller everything worked fine
+ //
+
+ return TRUE;
+}
+
+
+VOID
+PfxRemovePrefix (
+ IN PPREFIX_TABLE PrefixTable,
+ IN PPREFIX_TABLE_ENTRY PrefixTableEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes the indicated prefix table entry from
+ the prefix table
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table affected
+
+ PrefixTableEntry - Supplies the prefix entry to remove
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Links;
+
+ PPREFIX_TABLE_ENTRY Root;
+ PPREFIX_TABLE_ENTRY NewRoot;
+
+ PPREFIX_TABLE_ENTRY PreviousTree;
+
+ RTL_PAGED_CODE();
+
+ //
+ // case on the type of node that we are trying to delete
+ //
+
+ switch (PrefixTableEntry->NodeTypeCode) {
+
+ case RTL_NTC_INTERNAL:
+ case RTL_NTC_ROOT:
+
+ //
+ // The node is internal or root node so we need to delete it from
+ // the tree, but first find the root of the tree
+ //
+
+ Links = &PrefixTableEntry->Links;
+
+ while (!RtlIsRoot(Links)) {
+
+ Links = RtlParent(Links);
+ }
+
+ Root = CONTAINING_RECORD( Links, PREFIX_TABLE_ENTRY, Links );
+
+ //
+ // Now delete the node
+ //
+
+ Links = RtlDelete(&PrefixTableEntry->Links);
+
+ //
+ // Now see if the tree is deleted
+ //
+
+ if (Links == NULL) {
+
+ //
+ // The tree is now empty so remove this tree from
+ // the tree list, by first finding the previous tree that
+ // references us
+ //
+
+ PreviousTree = Root->NextPrefixTree;
+
+ while ( PreviousTree->NextPrefixTree != Root ) {
+
+ PreviousTree = PreviousTree->NextPrefixTree;
+ }
+
+ //
+ // We've located the previous tree so now just have it
+ // point around the deleted node
+ //
+
+ PreviousTree->NextPrefixTree = Root->NextPrefixTree;
+
+ //
+ // and return the our caller
+ //
+
+ return;
+ }
+
+ //
+ // The tree is not deleted but see if we changed roots
+ //
+
+ if (&Root->Links != Links) {
+
+ //
+ // Get a pointer to the new root
+ //
+
+ NewRoot = CONTAINING_RECORD(Links, PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // We changed root so we better need to make the new
+ // root part of the prefix data structure, by
+ // first finding the previous tree that
+ // references us
+ //
+
+ PreviousTree = Root->NextPrefixTree;
+
+ while ( PreviousTree->NextPrefixTree != Root ) {
+
+ PreviousTree = PreviousTree->NextPrefixTree;
+ }
+
+ //
+ // Set the new root
+ //
+
+ NewRoot->NodeTypeCode = RTL_NTC_ROOT;
+
+ PreviousTree->NextPrefixTree = NewRoot;
+ NewRoot->NextPrefixTree = Root->NextPrefixTree;
+
+ //
+ // Set the old root to be an internal node
+ //
+
+ Root->NodeTypeCode = RTL_NTC_INTERNAL;
+
+ Root->NextPrefixTree = NULL;
+
+ //
+ // And return to our caller
+ //
+
+ return;
+ }
+
+ //
+ // We didn't change roots so everything is fine and we can
+ // simply return to our caller
+ //
+
+ return;
+
+ default:
+
+ //
+ // If we get here then there was an error and the node type
+ // code is unknown
+ //
+
+ return;
+ }
+}
+
+
+PPREFIX_TABLE_ENTRY
+PfxFindPrefix (
+ IN PPREFIX_TABLE PrefixTable,
+ IN PSTRING FullName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine finds if a full name has a prefix in a prefix table.
+ It returns a pointer to the largest proper prefix found if one exists.
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table to search
+
+ FullString - Supplies the name to search for
+
+Return Value:
+
+ PPREFIX_TABLE_ENTRY - a pointer to the longest prefix found if one
+ exists, and NULL otherwise
+
+--*/
+
+{
+ CLONG NameLength;
+
+ PPREFIX_TABLE_ENTRY PreviousTree;
+ PPREFIX_TABLE_ENTRY CurrentTree;
+ PPREFIX_TABLE_ENTRY NextTree;
+
+ PRTL_SPLAY_LINKS Links;
+
+ PPREFIX_TABLE_ENTRY Node;
+
+ COMPARISON Comparison;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Determine the name length of the input string
+ //
+
+ NameLength = ComputeNameLength(FullName);
+
+ //
+ // Locate the first tree that can contain a prefix
+ //
+
+ PreviousTree = (PPREFIX_TABLE_ENTRY)PrefixTable;
+ CurrentTree = PreviousTree->NextPrefixTree;
+
+ while (CurrentTree->NameLength > (CSHORT)NameLength) {
+
+ PreviousTree = CurrentTree;
+ CurrentTree = CurrentTree->NextPrefixTree;
+ }
+
+ //
+ // Now search for a prefix until we find one or until we exhaust
+ // the prefix trees
+ //
+
+ while (CurrentTree->NameLength > 0) {
+
+ Links = &CurrentTree->Links;
+
+ while (Links != NULL) {
+
+ Node = CONTAINING_RECORD(Links, PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Compare the prefix in the tree with the full name
+ //
+
+ Comparison = CompareNamesCaseSensitive(Node->Prefix, FullName);
+
+ //
+ // See if they don't match
+ //
+
+ if (Comparison == IsGreaterThan) {
+
+ //
+ // The prefix is greater than the full name
+ // so we go down the left child
+ //
+
+ Links = RtlLeftChild(Links);
+
+ //
+ // And continue searching down this tree
+ //
+
+ } else if (Comparison == IsLessThan) {
+
+ //
+ // The prefix is less than the full name
+ // so we go down the right child
+ //
+
+ Links = RtlRightChild(Links);
+
+ //
+ // And continue searching down this tree
+ //
+
+ } else {
+
+ //
+ // We found it.
+ //
+ // Now that we've located the node we can splay the tree.
+ // To do this we need to remember how we find this tree in the root
+ // tree list, set the root to be an internal, splay, the tree, and
+ // then setup the new root node.
+ //
+
+ if (Node->NodeTypeCode == RTL_NTC_INTERNAL) {
+
+ //DbgPrint("PrefixTable = %08lx\n", PrefixTable);
+ //DbgPrint("Node = %08lx\n", Node);
+ //DbgPrint("CurrentTree = %08lx\n", CurrentTree);
+ //DbgPrint("PreviousTree = %08lx\n", PreviousTree);
+ //DbgBreakPoint();
+
+ //
+ // Save a pointer to the next tree, we already have the previous tree
+ //
+
+ NextTree = CurrentTree->NextPrefixTree;
+
+ //
+ // Reset the current root to be an internal node
+ //
+
+ CurrentTree->NodeTypeCode = RTL_NTC_INTERNAL;
+ CurrentTree->NextPrefixTree = NULL;
+
+ //
+ // Splay the tree and get the root
+ //
+
+ Node = CONTAINING_RECORD(RtlSplay(&Node->Links), PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Set the new root's node type code and make it part of the
+ // root tree list
+ //
+
+ Node->NodeTypeCode = RTL_NTC_ROOT;
+ PreviousTree->NextPrefixTree = Node;
+ Node->NextPrefixTree = NextTree;
+ }
+
+ return Node;
+ }
+ }
+
+ //
+ // This tree is done so now find the next tree
+ //
+
+ PreviousTree = CurrentTree;
+ CurrentTree = CurrentTree->NextPrefixTree;
+ }
+
+ //
+ // We sesarched everywhere and didn't find a prefix so tell the
+ // caller none was found
+ //
+
+ return NULL;
+}
+
+
+CLONG
+ComputeNameLength(
+ IN PSTRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This routine counts the number of names appearing in the input string.
+ It does this by simply counting the number of backslashes in the string.
+ To handle ill-formed names (i.e., names that do not contain a backslash)
+ this routine really returns the number of backslashes plus 1.
+
+Arguments:
+
+ Name - Supplies the input name to examine
+
+Returns Value:
+
+ CLONG - the number of names in the input string
+
+--*/
+
+{
+ ULONG NameLength;
+ ULONG i;
+ ULONG Count;
+
+ extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
+ extern BOOLEAN NlsMbCodePageTag;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Save the name length, this should make the compiler be able to
+ // optimize not having to reload the length each time
+ //
+
+ NameLength = Name->Length - 1;
+
+ //
+ // Now loop through the input string counting back slashes
+ //
+
+ if (NlsMbCodePageTag) {
+
+ //
+ // ComputeNameLength() skip DBCS character when counting '\'
+ //
+
+ for (i = 0, Count = 1; i < NameLength; ) {
+
+ if (NlsLeadByteInfo[(UCHAR)Name->Buffer[i]]) {
+
+ i += 2;
+
+ } else {
+
+ if (Name->Buffer[i] == '\\') {
+
+ Count += 1;
+ }
+
+ i += 1;
+ }
+ }
+
+ } else {
+
+ for (i = 0, Count = 1; i < NameLength; i += 1) {
+
+ //
+ // check for a back slash
+ //
+
+ if (Name->Buffer[i] == '\\') {
+
+ Count += 1;
+ }
+ }
+ }
+
+ //
+ // return the number of back slashes we found
+ //
+
+ //DbgPrint("ComputeNameLength(%s) = %x\n", Name->Buffer, Count);
+
+ return Count;
+}
+
+
+COMPARISON
+CompareNamesCaseSensitive (
+ IN PSTRING Prefix,
+ IN PSTRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes a prefix string and a full name string and determines
+ if the prefix string is a proper prefix of the name string (case sensitive)
+
+Arguments:
+
+ Prefix - Supplies the input prefix string
+
+ Name - Supplies the full name input string
+
+Return Value:
+
+ COMPARISON - returns
+
+ IsLessThan if Prefix < Name lexicalgraphically,
+ IsPrefix if Prefix is a proper prefix of Name
+ IsEqual if Prefix is equal to Name, and
+ IsGreaterThan if Prefix > Name lexicalgraphically
+
+--*/
+
+{
+ ULONG PrefixLength;
+ ULONG NameLength;
+ ULONG MinLength;
+ ULONG i;
+
+ UCHAR PrefixChar;
+ UCHAR NameChar;
+
+ extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
+ extern BOOLEAN NlsMbCodePageTag;
+
+ RTL_PAGED_CODE();
+
+ //DbgPrint("CompareNamesCaseSensitive(\"%s\", \"%s\") = ", Prefix->Buffer, Name->Buffer);
+
+ //
+ // Save the length of the prefix and name string, this should allow
+ // the compiler to not need to reload the length through a pointer every
+ // time we need their values
+ //
+
+ PrefixLength = Prefix->Length;
+ NameLength = Name->Length;
+
+ //
+ // Special case the situation where the prefix string is simply "\" and
+ // the name starts with an "\"
+ //
+
+ if ((Prefix->Length == 1) && (Prefix->Buffer[0] == '\\') &&
+ (Name->Length > 1) && (Name->Buffer[0] == '\\')) {
+ //DbgPrint("IsPrefix\n");
+ return IsPrefix;
+ }
+
+ //
+ // Figure out the minimum of the two lengths
+ //
+
+ MinLength = (PrefixLength < NameLength ? PrefixLength : NameLength);
+
+ //
+ // Loop through looking at all of the characters in both strings
+ // testing for equalilty, less than, and greater than
+ //
+
+ i = RtlCompareMemory( &Prefix->Buffer[0], &Name->Buffer[0], MinLength );
+
+ if (i < MinLength) {
+
+ UCHAR c;
+
+ //
+ // Get both characters to examine and keep their case
+ //
+
+ PrefixChar = ((c = Prefix->Buffer[i]) == '\\' ? (CHAR)0 : c);
+ NameChar = ((c = Name->Buffer[i]) == '\\' ? (CHAR)0 : c);
+
+ //
+ // Unfortunately life is not so easy in DBCS land.
+ //
+
+ if (NlsMbCodePageTag) {
+
+ //
+ // CompareNamesCaseSensitive(): check backslash in trailing bytes
+ //
+
+ if (Prefix->Buffer[i] == '\\') {
+
+ ULONG j;
+ extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
+
+ for (j = 0; j < i;) {
+
+ j += NlsLeadByteInfo[(UCHAR)Prefix->Buffer[j]] ? 2 : 1;
+ }
+
+ if (j != i) {
+
+ PrefixChar = '\\';
+ //DbgPrint("RTL:CompareNamesCaseSensitive encountered a fake backslash!\n");
+ }
+ }
+
+ if (Name->Buffer[i] == '\\') {
+
+ ULONG j;
+ extern PUSHORT NlsLeadByteInfo; // Lead byte info. for ACP ( nlsxlat.c )
+
+ for (j = 0; j < i;) {
+
+ j += NlsLeadByteInfo[(UCHAR)Name->Buffer[j]] ? 2 : 1;
+ }
+
+ if (j != i) {
+
+ NameChar = '\\';
+ //DbgPrint("RTL:CompareNamesCaseSensitive encountered a fake backslash!\n");
+ }
+ }
+ }
+
+ //
+ // Now compare the characters
+ //
+
+ if (PrefixChar < NameChar) {
+
+ return IsLessThan;
+
+ } else if (PrefixChar > NameChar) {
+
+ return IsGreaterThan;
+ }
+ }
+
+ //
+ // They match upto the minimum length so now figure out the largest string
+ // and see if one is a proper prefix of the other
+ //
+
+ if (PrefixLength < NameLength) {
+
+ //
+ // The prefix string is shorter so if it is a proper prefix we
+ // return prefix otherwise we return less than (e.g., "\a" < "\ab")
+ //
+
+ if (Name->Buffer[PrefixLength] == '\\') {
+
+ return IsPrefix;
+
+ } else {
+
+ return IsLessThan;
+ }
+
+ } else if (PrefixLength > NameLength) {
+
+ //
+ // The Prefix string is longer so we say that the prefix is
+ // greater than the name (e.g., "\ab" > "\a")
+ //
+
+ return IsGreaterThan;
+
+ } else {
+
+ //
+ // They lengths are equal so the strings are equal
+ //
+
+ return IsEqual;
+ }
+}
+
+
+//
+// The node type codes for the prefix data structures
+//
+
+#define RTL_NTC_UNICODE_PREFIX_TABLE ((CSHORT)0x0800)
+#define RTL_NTC_UNICODE_ROOT ((CSHORT)0x0801)
+#define RTL_NTC_UNICODE_INTERNAL ((CSHORT)0x0802)
+#define RTL_NTC_UNICODE_CASE_MATCH ((CSHORT)0x0803)
+
+
+VOID
+RtlInitializeUnicodePrefix (
+ IN PUNICODE_PREFIX_TABLE PrefixTable
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes a unicode prefix table record to the empty state.
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table being initialized
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ PrefixTable->NodeTypeCode = RTL_NTC_UNICODE_PREFIX_TABLE;
+ PrefixTable->NameLength = 0;
+ PrefixTable->NextPrefixTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
+ PrefixTable->LastNextEntry = NULL;
+
+ //
+ // return to our caller
+ //
+
+ return;
+}
+
+
+BOOLEAN
+RtlInsertUnicodePrefix (
+ IN PUNICODE_PREFIX_TABLE PrefixTable,
+ IN PUNICODE_STRING Prefix,
+ IN PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine inserts a new unicode prefix into the specified prefix table
+
+Arguments:
+
+ PrefixTable - Supplies the target prefix table
+
+ Prefix - Supplies the string to be inserted in the prefix table
+
+ PrefixTableEntry - Supplies the entry to use to insert the prefix
+
+Return Value:
+
+ BOOLEAN - TRUE if the Prefix is not already in the table, and FALSE
+ otherwise
+
+--*/
+
+{
+ ULONG PrefixNameLength;
+
+ PUNICODE_PREFIX_TABLE_ENTRY PreviousTree;
+ PUNICODE_PREFIX_TABLE_ENTRY CurrentTree;
+ PUNICODE_PREFIX_TABLE_ENTRY NextTree;
+
+ PUNICODE_PREFIX_TABLE_ENTRY Node;
+
+ COMPARISON Comparison;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Determine the name length of the input string
+ //
+
+ PrefixNameLength = ComputeUnicodeNameLength(Prefix);
+
+ //
+ // Setup parts of the prefix table entry that we will always need
+ //
+
+ PrefixTableEntry->NameLength = (CSHORT)PrefixNameLength;
+ PrefixTableEntry->Prefix = Prefix;
+
+ RtlInitializeSplayLinks(&PrefixTableEntry->Links);
+
+ //
+ // find the corresponding tree, or find where the tree should go
+ //
+
+ PreviousTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
+ CurrentTree = PreviousTree->NextPrefixTree;
+
+ while (CurrentTree->NameLength > (CSHORT)PrefixNameLength) {
+
+ PreviousTree = CurrentTree;
+ CurrentTree = CurrentTree->NextPrefixTree;
+ }
+
+ //
+ // If the name length of the current tree is not equal to the
+ // prefix name length then the tree does not exist and we need
+ // to make a new tree node.
+ //
+
+ if (CurrentTree->NameLength != (CSHORT)PrefixNameLength) {
+
+ //
+ // Insert the new prefix entry to the list between
+ // previous and current tree
+ //
+
+ PreviousTree->NextPrefixTree = PrefixTableEntry;
+ PrefixTableEntry->NextPrefixTree = CurrentTree;
+
+ //
+ // And set the node type code, case match for the root tree node
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
+ PrefixTableEntry->CaseMatch = PrefixTableEntry;
+
+ //
+ // And tell our caller everything worked fine
+ //
+
+ return TRUE;
+ }
+
+ //
+ // The tree does exist so now search the tree for our
+ // position in it. We only exit the loop if we've inserted
+ // a new node, and node is left is left pointing to the
+ // tree position
+ //
+
+ Node = CurrentTree;
+
+ while (TRUE) {
+
+ //
+ // Compare the prefix in the tree with the prefix we want
+ // to insert. Do the compare case blind
+ //
+
+ Comparison = CompareUnicodeStrings(Node->Prefix, Prefix, 0);
+
+ //
+ // If they are equal then this node gets added as a case
+ // match, provided it doesn't case sensitive match anyone
+ //
+
+ if (Comparison == IsEqual) {
+
+ PUNICODE_PREFIX_TABLE_ENTRY Next;
+
+ //
+ // Loop through the case match list checking to see if we
+ // match case sensitive with anyone. Get the first node
+ //
+
+ Next = Node;
+
+ //
+ // And loop checking each node until we're back to where
+ // we started
+ //
+
+ do {
+
+ //
+ // If we do match case sensitive then we cannot add
+ // this prefix so we return false. Note this is the
+ // only condition where we return false
+ //
+
+ if (CompareUnicodeStrings(Next->Prefix, Prefix, MAXULONG) == IsEqual) {
+
+ return FALSE;
+ }
+
+ //
+ // Get the next node in the case match list
+ //
+
+ Next = Next->CaseMatch;
+
+ //
+ // And continue looping until we're back where we started
+ //
+
+ } while ( Next != Node );
+
+ //
+ // We've searched the case match and didn't find an exact match
+ // so we can insert this node in the case match list
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_CASE_MATCH;
+ PrefixTableEntry->NextPrefixTree = NULL;
+
+ PrefixTableEntry->CaseMatch = Node->CaseMatch;
+ Node->CaseMatch = PrefixTableEntry;
+
+ //
+ // And exit out of the while loop
+ //
+
+ break;
+ }
+
+ //
+ // If the tree prefix is greater than the new prefix then
+ // we go down the left subtree
+ //
+
+ if (Comparison == IsGreaterThan) {
+
+ //
+ // We want to go down the left subtree, first check to see
+ // if we have a left subtree
+ //
+
+ if (RtlLeftChild(&Node->Links) == NULL) {
+
+ //
+ // there isn't a left child so we insert ourselves as the
+ // new left child
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
+ PrefixTableEntry->NextPrefixTree = NULL;
+ PrefixTableEntry->CaseMatch = PrefixTableEntry;
+
+ RtlInsertAsLeftChild(&Node->Links, &PrefixTableEntry->Links);
+
+ //
+ // and exit the while loop
+ //
+
+ break;
+
+ } else {
+
+ //
+ // there is a left child so simply go down that path, and
+ // go back to the top of the loop
+ //
+
+ Node = CONTAINING_RECORD( RtlLeftChild(&Node->Links),
+ UNICODE_PREFIX_TABLE_ENTRY,
+ Links );
+ }
+
+ } else {
+
+ //
+ // The tree prefix is either less than or a proper prefix
+ // of the new string. We treat both cases a less than when
+ // we do insert. So we want to go down the right subtree,
+ // first check to see if we have a right subtree
+ //
+
+ if (RtlRightChild(&Node->Links) == NULL) {
+
+ //
+ // These isn't a right child so we insert ourselves as the
+ // new right child
+ //
+
+ PrefixTableEntry->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
+ PrefixTableEntry->NextPrefixTree = NULL;
+ PrefixTableEntry->CaseMatch = PrefixTableEntry;
+
+ RtlInsertAsRightChild(&Node->Links, &PrefixTableEntry->Links);
+
+ //
+ // and exit the while loop
+ //
+
+ break;
+
+ } else {
+
+ //
+ // there is a right child so simply go down that path, and
+ // go back to the top of the loop
+ //
+
+ Node = CONTAINING_RECORD( RtlRightChild(&Node->Links),
+ UNICODE_PREFIX_TABLE_ENTRY,
+ Links );
+ }
+ }
+ }
+
+ //
+ // Now that we've inserted the new node we can splay the tree.
+ // To do this we need to remember how we find this tree in the root
+ // tree list, set the root to be an internal, splay, the tree, and
+ // then setup the new root node. Note: we cannot splay the prefix table
+ // entry because it might be a case match node so we only splay
+ // the Node variable, which for case match insertions is the
+ // internal node for the case match and for non-case match insertions
+ // the Node variable is the parent node.
+ //
+
+ //
+ // Save a pointer to the next tree, we already have the previous tree
+ //
+
+ NextTree = CurrentTree->NextPrefixTree;
+
+ //
+ // Reset the current root to be an internal node
+ //
+
+ CurrentTree->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
+ CurrentTree->NextPrefixTree = NULL;
+
+ //
+ // Splay the tree and get the root
+ //
+
+ Node = CONTAINING_RECORD(RtlSplay(&Node->Links), UNICODE_PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Set the new root's node type code and make it part of the
+ // root tree list
+ //
+
+ Node->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
+ PreviousTree->NextPrefixTree = Node;
+ Node->NextPrefixTree = NextTree;
+
+ //
+ // tell our caller everything worked fine
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlRemoveUnicodePrefix (
+ IN PUNICODE_PREFIX_TABLE PrefixTable,
+ IN PUNICODE_PREFIX_TABLE_ENTRY PrefixTableEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes the indicated prefix table entry from
+ the prefix table
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table affected
+
+ PrefixTableEntry - Supplies the prefix entry to remove
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PUNICODE_PREFIX_TABLE_ENTRY PreviousCaseMatch;
+
+ PRTL_SPLAY_LINKS Links;
+
+ PUNICODE_PREFIX_TABLE_ENTRY Root;
+ PUNICODE_PREFIX_TABLE_ENTRY NewRoot;
+
+ PUNICODE_PREFIX_TABLE_ENTRY PreviousTree;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Wipe out the next last entry field of the prefix table
+ //
+
+ PrefixTable->LastNextEntry = NULL;
+
+ //
+ // case on the type of node that we are trying to delete
+ //
+
+ switch (PrefixTableEntry->NodeTypeCode) {
+
+ case RTL_NTC_UNICODE_CASE_MATCH:
+
+ //
+ // The prefix entry is a case match record so
+ // we only need to remove it from the case match list.
+ // Locate the previous PrefixTableEntry that reference this
+ // case match record
+ //
+
+ PreviousCaseMatch = PrefixTableEntry->CaseMatch;
+
+ while ( PreviousCaseMatch->CaseMatch != PrefixTableEntry ) {
+
+ PreviousCaseMatch = PreviousCaseMatch->CaseMatch;
+ }
+
+ //
+ // Now that we have the previous record just have it point
+ // around the case match that is being deleted
+ //
+
+ PreviousCaseMatch->CaseMatch = PrefixTableEntry->CaseMatch;
+
+ //
+ // And return to our caller
+ //
+
+ return;
+
+ case RTL_NTC_UNICODE_INTERNAL:
+ case RTL_NTC_UNICODE_ROOT:
+
+ //
+ // The prefix entry is an internal/root node so check to see if it
+ // has any case match nodes with it
+ //
+
+ if (PrefixTableEntry->CaseMatch != PrefixTableEntry) {
+
+ //
+ // There is at least one case match that goes with this
+ // node, so we need to make the next case match the
+ // new node and remove this node.
+ // Locate the previous prefix table entry that references this
+ // case match record
+ //
+
+ PreviousCaseMatch = PrefixTableEntry->CaseMatch;
+
+ while ( PreviousCaseMatch->CaseMatch != PrefixTableEntry ) {
+
+ PreviousCaseMatch = PreviousCaseMatch->CaseMatch;
+ }
+
+ //
+ // Now that we have the previous record just have it point
+ // around the node being deleted
+ //
+
+ PreviousCaseMatch->CaseMatch = PrefixTableEntry->CaseMatch;
+
+ //
+ // Now make the previous case match in the new node
+ //
+
+ PreviousCaseMatch->NodeTypeCode = PrefixTableEntry->NodeTypeCode;
+ PreviousCaseMatch->NextPrefixTree = PrefixTableEntry->NextPrefixTree;
+ PreviousCaseMatch->Links = PrefixTableEntry->Links;
+
+ //
+ // Now take care of the back pointers to this new internal
+ // node in the splay tree, first do the parent's pointer to us.
+ //
+
+ if (RtlIsRoot(&PrefixTableEntry->Links)) {
+
+ //
+ // This is the root so make this new node the root
+ //
+
+ PreviousCaseMatch->Links.Parent = &PreviousCaseMatch->Links;
+
+ //
+ // Fix up the root tree list, by first finding the previous
+ // pointer to us
+
+ PreviousTree = PrefixTableEntry->NextPrefixTree;
+
+ while ( PreviousTree->NextPrefixTree != PrefixTableEntry ) {
+
+ PreviousTree = PreviousTree->NextPrefixTree;
+ }
+
+ //
+ // We've located the previous tree so now have the previous
+ // tree point to our new root
+ //
+
+ PreviousTree->NextPrefixTree = PreviousCaseMatch;
+
+ } else if (RtlIsLeftChild(&PrefixTableEntry->Links)) {
+
+ //
+ // The node was the left child so make the new node the
+ // left child
+ //
+
+ RtlParent(&PrefixTableEntry->Links)->LeftChild = &PreviousCaseMatch->Links;
+
+ } else {
+
+ //
+ // The node was the right child so make the new node the
+ // right child
+ //
+
+ RtlParent(&PrefixTableEntry->Links)->RightChild = &PreviousCaseMatch->Links;
+ }
+
+ //
+ // Now update the parent pointer for our new children
+ //
+
+ if (RtlLeftChild(&PreviousCaseMatch->Links) != NULL) {
+
+ RtlLeftChild(&PreviousCaseMatch->Links)->Parent = &PreviousCaseMatch->Links;
+ }
+
+ if (RtlRightChild(&PreviousCaseMatch->Links) != NULL) {
+
+ RtlRightChild(&PreviousCaseMatch->Links)->Parent = &PreviousCaseMatch->Links;
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return;
+ }
+
+ //
+ // The node is internal or root node and does not have any case match
+ // nodes so we need to delete it from the tree, but first find
+ // the root of the tree
+ //
+
+ Links = &PrefixTableEntry->Links;
+
+ while (!RtlIsRoot(Links)) {
+
+ Links = RtlParent(Links);
+ }
+
+ Root = CONTAINING_RECORD( Links, UNICODE_PREFIX_TABLE_ENTRY, Links );
+
+ //
+ // Now delete the node
+ //
+
+ Links = RtlDelete(&PrefixTableEntry->Links);
+
+ //
+ // Now see if the tree is deleted
+ //
+
+ if (Links == NULL) {
+
+ //
+ // The tree is now empty so remove this tree from
+ // the tree list, by first finding the previous tree that
+ // references us
+ //
+
+ PreviousTree = Root->NextPrefixTree;
+
+ while ( PreviousTree->NextPrefixTree != Root ) {
+
+ PreviousTree = PreviousTree->NextPrefixTree;
+ }
+
+ //
+ // We've located the previous tree so now just have it
+ // point around the deleted node
+ //
+
+ PreviousTree->NextPrefixTree = Root->NextPrefixTree;
+
+ //
+ // and return the our caller
+ //
+
+ return;
+ }
+
+ //
+ // The tree is not deleted but see if we changed roots
+ //
+
+ if (&Root->Links != Links) {
+
+ //
+ // Get a pointer to the new root
+ //
+
+ NewRoot = CONTAINING_RECORD(Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // We changed root so we better need to make the new
+ // root part of the prefix data structure, by
+ // first finding the previous tree that
+ // references us
+ //
+
+ PreviousTree = Root->NextPrefixTree;
+
+ while ( PreviousTree->NextPrefixTree != Root ) {
+
+ PreviousTree = PreviousTree->NextPrefixTree;
+ }
+
+ //
+ // Set the new root
+ //
+
+ NewRoot->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
+
+ PreviousTree->NextPrefixTree = NewRoot;
+ NewRoot->NextPrefixTree = Root->NextPrefixTree;
+
+ //
+ // Set the old root to be an internal node
+ //
+
+ Root->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
+
+ Root->NextPrefixTree = NULL;
+
+ //
+ // And return to our caller
+ //
+
+ return;
+ }
+
+ //
+ // We didn't change roots so everything is fine and we can
+ // simply return to our caller
+ //
+
+ return;
+
+ default:
+
+ //
+ // If we get here then there was an error and the node type
+ // code is unknown
+ //
+
+ return;
+ }
+}
+
+
+PUNICODE_PREFIX_TABLE_ENTRY
+RtlFindUnicodePrefix (
+ IN PUNICODE_PREFIX_TABLE PrefixTable,
+ IN PUNICODE_STRING FullName,
+ IN ULONG CaseInsensitiveIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine finds if a full name has a prefix in a prefix table.
+ It returns a pointer to the largest proper prefix found if one exists.
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table to search
+
+ FullString - Supplies the name to search for
+
+ CaseInsensitiveIndex - Indicates the wchar index at which to do a case
+ insensitive search. All characters before the index are searched
+ case sensitive and all characters at and after the index are searched
+ insensitive.
+
+Return Value:
+
+ PPREFIX_TABLE_ENTRY - a pointer to the longest prefix found if one
+ exists, and NULL otherwise
+
+--*/
+
+{
+ CLONG NameLength;
+
+ PUNICODE_PREFIX_TABLE_ENTRY PreviousTree;
+ PUNICODE_PREFIX_TABLE_ENTRY CurrentTree;
+ PUNICODE_PREFIX_TABLE_ENTRY NextTree;
+
+ PRTL_SPLAY_LINKS Links;
+
+ PUNICODE_PREFIX_TABLE_ENTRY Node;
+ PUNICODE_PREFIX_TABLE_ENTRY Next;
+
+ COMPARISON Comparison;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Determine the name length of the input string
+ //
+
+ NameLength = ComputeUnicodeNameLength(FullName);
+
+ //
+ // Locate the first tree that can contain a prefix
+ //
+
+ PreviousTree = (PUNICODE_PREFIX_TABLE_ENTRY)PrefixTable;
+ CurrentTree = PreviousTree->NextPrefixTree;
+
+ while (CurrentTree->NameLength > (CSHORT)NameLength) {
+
+ PreviousTree = CurrentTree;
+ CurrentTree = CurrentTree->NextPrefixTree;
+ }
+
+ //
+ // Now search for a prefix until we find one or until we exhaust
+ // the prefix trees
+ //
+
+ while (CurrentTree->NameLength > 0) {
+
+ Links = &CurrentTree->Links;
+
+ while (Links != NULL) {
+
+ Node = CONTAINING_RECORD(Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Compare the prefix in the tree with the full name, do the
+ // compare case blind
+ //
+
+ Comparison = CompareUnicodeStrings(Node->Prefix, FullName, 0);
+
+ //
+ // See if they don't match
+ //
+
+ if (Comparison == IsGreaterThan) {
+
+ //
+ // The prefix is greater than the full name
+ // so we go down the left child
+ //
+
+ Links = RtlLeftChild(Links);
+
+ //
+ // And continue searching down this tree
+ //
+
+ } else if (Comparison == IsLessThan) {
+
+ //
+ // The prefix is less than the full name
+ // so we go down the right child
+ //
+
+ Links = RtlRightChild(Links);
+
+ //
+ // And continue searching down this tree
+ //
+
+ } else {
+
+ //
+ // We have either a prefix or a match either way
+ // we need to check if we should do case sensitive
+ // seearches
+ //
+
+ if (CaseInsensitiveIndex == 0) {
+
+ //
+ // The caller wants case insensitive so we'll
+ // return the first one we found
+ //
+ // Now that we've located the node we can splay the tree.
+ // To do this we need to remember how we find this tree in the root
+ // tree list, set the root to be an internal, splay, the tree, and
+ // then setup the new root node.
+ //
+
+ if (Node->NodeTypeCode == RTL_NTC_UNICODE_INTERNAL) {
+
+ //DbgPrint("PrefixTable = %08lx\n", PrefixTable);
+ //DbgPrint("Node = %08lx\n", Node);
+ //DbgPrint("CurrentTree = %08lx\n", CurrentTree);
+ //DbgPrint("PreviousTree = %08lx\n", PreviousTree);
+ //DbgBreakPoint();
+
+ //
+ // Save a pointer to the next tree, we already have the previous tree
+ //
+
+ NextTree = CurrentTree->NextPrefixTree;
+
+ //
+ // Reset the current root to be an internal node
+ //
+
+ CurrentTree->NodeTypeCode = RTL_NTC_UNICODE_INTERNAL;
+ CurrentTree->NextPrefixTree = NULL;
+
+ //
+ // Splay the tree and get the root
+ //
+
+ Node = CONTAINING_RECORD(RtlSplay(&Node->Links), UNICODE_PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Set the new root's node type code and make it part of the
+ // root tree list
+ //
+
+ Node->NodeTypeCode = RTL_NTC_UNICODE_ROOT;
+ PreviousTree->NextPrefixTree = Node;
+ Node->NextPrefixTree = NextTree;
+ }
+
+ //
+ // Now return the root to our caller
+ //
+
+ return Node;
+ }
+
+ //
+ // The caller wants an exact match so search the case match
+ // until we find a complete match. Get the first node
+ //
+
+ Next = Node;
+
+ //
+ // Loop through the case match list checking to see if we
+ // match case sensitive with anyone.
+ //
+
+ do {
+
+ //
+ // If we do match case sensitive then we found one
+ // and we return it to our caller
+ //
+
+ Comparison = CompareUnicodeStrings( Next->Prefix,
+ FullName,
+ CaseInsensitiveIndex );
+
+ if ((Comparison == IsEqual) || (Comparison == IsPrefix)) {
+
+ //
+ // We found a good one, so return it to our caller
+ //
+
+ return Next;
+ }
+
+ //
+ // Get the next case match record
+ //
+
+ Next = Next->CaseMatch;
+
+ //
+ // And continue the loop until we reach the original
+ // node again
+ //
+
+ } while ( Next != Node );
+
+ //
+ // We found a case blind prefix but the caller wants
+ // case sensitive and we weren't able to find one of those
+ // so we need to go on to the next tree, by breaking out
+ // of the inner while-loop
+ //
+
+ break;
+ }
+ }
+
+ //
+ // This tree is done so now find the next tree
+ //
+
+ PreviousTree = CurrentTree;
+ CurrentTree = CurrentTree->NextPrefixTree;
+ }
+
+ //
+ // We sesarched everywhere and didn't find a prefix so tell the
+ // caller none was found
+ //
+
+ return NULL;
+}
+
+
+PUNICODE_PREFIX_TABLE_ENTRY
+RtlNextUnicodePrefix (
+ IN PUNICODE_PREFIX_TABLE PrefixTable,
+ IN BOOLEAN Restart
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the next prefix entry stored in the prefix table
+
+Arguments:
+
+ PrefixTable - Supplies the prefix table to enumerate
+
+ Restart - Indicates if the enumeration should start over
+
+Return Value:
+
+ PPREFIX_TABLE_ENTRY - A pointer to the next prefix table entry if
+ one exists otherwise NULL
+
+--*/
+
+{
+ PUNICODE_PREFIX_TABLE_ENTRY Node;
+
+ PRTL_SPLAY_LINKS Links;
+
+ RTL_PAGED_CODE();
+
+ //
+ // See if we are restarting the sequence
+ //
+
+ if (Restart || (PrefixTable->LastNextEntry == NULL)) {
+
+ //
+ // we are restarting the sequence so locate the first entry
+ // in the first tree
+ //
+
+ Node = PrefixTable->NextPrefixTree;
+
+ //
+ // Make sure we've pointing at a prefix tree
+ //
+
+ if (Node->NodeTypeCode == RTL_NTC_UNICODE_PREFIX_TABLE) {
+
+ //
+ // No we aren't so the table must be empty
+ //
+
+ return NULL;
+ }
+
+ //
+ // Find the first node in the tree
+ //
+
+ Links = &Node->Links;
+
+ while (RtlLeftChild(Links) != NULL) {
+
+ Links = RtlLeftChild(Links);
+ }
+
+ //
+ // Set it as our the node we're returning
+ //
+
+ Node = CONTAINING_RECORD( Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
+
+ } else if (PrefixTable->LastNextEntry->CaseMatch->NodeTypeCode == RTL_NTC_UNICODE_CASE_MATCH) {
+
+ //
+ // The last node has a case match that we should be returning
+ // this time around
+ //
+
+ Node = PrefixTable->LastNextEntry->CaseMatch;
+
+ } else {
+
+ //
+ // Move over the last node returned by the case match link, this
+ // will enable us to finish off the last case match node if there
+ // was one, and go to the next internal/root node. If this node
+ // does not have a case match then we simply circle back to ourselves
+ //
+
+ Node = PrefixTable->LastNextEntry->CaseMatch;
+
+ //
+ // Find the successor for the last node we returned
+ //
+
+ Links = RtlRealSuccessor(&Node->Links);
+
+ //
+ // If links is null then we've exhausted this tree and need to
+ // the the next tree to use
+ //
+
+ if (Links == NULL) {
+
+ Links = &PrefixTable->LastNextEntry->Links;
+
+ while (!RtlIsRoot(Links)) {
+
+ Links = RtlParent(Links);
+ }
+
+ Node = CONTAINING_RECORD(Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
+
+ //
+ // Now we've found the root see if there is another
+ // tree to enumerate
+ //
+
+ Node = Node->NextPrefixTree;
+
+ if (Node->NameLength <= 0) {
+
+ //
+ // We've run out of tree so tell our caller there
+ // are no more
+ //
+
+ return NULL;
+ }
+
+ //
+ // We have another tree to go down
+ //
+
+ Links = &Node->Links;
+
+ while (RtlLeftChild(Links) != NULL) {
+
+ Links = RtlLeftChild(Links);
+ }
+ }
+
+ //
+ // Set it as our the node we're returning
+ //
+
+ Node = CONTAINING_RECORD( Links, UNICODE_PREFIX_TABLE_ENTRY, Links);
+ }
+
+ //
+ // Save node as the last next entry
+ //
+
+ PrefixTable->LastNextEntry = Node;
+
+ //
+ // And return this entry to our caller
+ //
+
+ return Node;
+}
+
+
+CLONG
+ComputeUnicodeNameLength(
+ IN PUNICODE_STRING Name
+ )
+
+/*++
+
+Routine Description:
+
+ This routine counts the number of names appearing in the input string.
+ It does this by simply counting the number of backslashes in the string.
+ To handle ill-formed names (i.e., names that do not contain a backslash)
+ this routine really returns the number of backslashes plus 1.
+
+Arguments:
+
+ Name - Supplies the input name to examine
+
+Returns Value:
+
+ CLONG - the number of names in the input string
+
+--*/
+
+{
+ WCHAR UnicodeBackSlash = '\\';
+ ULONG NameLength;
+ ULONG i;
+ ULONG Count;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Save the name length, this should make the compiler be able to
+ // optimize not having to reload the length each time
+ //
+
+ NameLength = (ULONG)Name->Length/2;
+
+ //
+ // Now loop through the input string counting back slashes
+ //
+
+ for (i = 0, Count = 1; i < (ULONG)NameLength - 1; i += 1) {
+
+ //
+ // check for a back slash
+ //
+
+ if (Name->Buffer[i] == UnicodeBackSlash) {
+
+ Count += 1;
+ }
+ }
+
+ //
+ // return the number of back slashes we found
+ //
+
+ //DbgPrint("ComputeUnicodeNameLength(%Z) = %x\n", Name, Count);
+
+ return Count;
+}
+
+
+COMPARISON
+CompareUnicodeStrings (
+ IN PUNICODE_STRING Prefix,
+ IN PUNICODE_STRING Name,
+ IN ULONG CaseInsensitiveIndex
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes a prefix string and a full name string and determines
+ if the prefix string is a proper prefix of the name string (case sensitive)
+
+Arguments:
+
+ Prefix - Supplies the input prefix string
+
+ Name - Supplies the full name input string
+
+ CaseInsensitiveIndex - Indicates the wchar index at which to do a case
+ insensitive search. All characters before the index are searched
+ case sensitive and all characters at and after the index are searched
+
+Return Value:
+
+ COMPARISON - returns
+
+ IsLessThan if Prefix < Name lexicalgraphically,
+ IsPrefix if Prefix is a proper prefix of Name
+ IsEqual if Prefix is equal to Name, and
+ IsGreaterThan if Prefix > Name lexicalgraphically
+
+--*/
+
+{
+ WCHAR UnicodeBackSlash = '\\';
+ ULONG PrefixLength;
+ ULONG NameLength;
+ ULONG MinLength;
+ ULONG i;
+
+ WCHAR PrefixChar;
+ WCHAR NameChar;
+
+ RTL_PAGED_CODE();
+
+ //DbgPrint("CompareUnicodeStrings(\"%Z\", \"%Z\") = ", Prefix, Name);
+
+ //
+ // Save the length of the prefix and name string, this should allow
+ // the compiler to not need to reload the length through a pointer every
+ // time we need their values
+ //
+
+ PrefixLength = (ULONG)Prefix->Length/2;
+ NameLength = (ULONG)Name->Length/2;
+
+ //
+ // Special case the situation where the prefix string is simply "\" and
+ // the name starts with an "\"
+ //
+
+ if ((PrefixLength == 1) && (Prefix->Buffer[0] == UnicodeBackSlash) &&
+ (NameLength > 1) && (Name->Buffer[0] == UnicodeBackSlash)) {
+ //DbgPrint("IsPrefix\n");
+ return IsPrefix;
+ }
+
+ //
+ // Figure out the minimum of the two lengths
+ //
+
+ MinLength = (PrefixLength < NameLength ? PrefixLength : NameLength);
+
+ //
+ // Loop through looking at all of the characters in both strings
+ // testing for equalilty. First to the CaseSensitive part, then the
+ // CaseInsensitive part.
+ //
+
+ if (CaseInsensitiveIndex > MinLength) {
+
+ CaseInsensitiveIndex = MinLength;
+ }
+
+ //
+ // CaseSensitive compare
+ //
+
+ for (i = 0; i < CaseInsensitiveIndex; i += 1) {
+
+ PrefixChar = Prefix->Buffer[i];
+ NameChar = Name->Buffer[i];
+
+ if (PrefixChar != NameChar) {
+
+ break;
+ }
+ }
+
+ //
+ // If we didn't break out of the above loop, do the
+ // CaseInsensitive compare.
+ //
+
+ if (i == CaseInsensitiveIndex) {
+
+ WCHAR *s1 = &Prefix->Buffer[i];
+ WCHAR *s2 = &Name->Buffer[i];
+
+ for (; i < MinLength; i += 1) {
+
+ PrefixChar = *s1++;
+ NameChar = *s2++;
+
+ if (PrefixChar != NameChar) {
+
+ PrefixChar = NLS_UPCASE(PrefixChar);
+ NameChar = NLS_UPCASE(NameChar);
+
+ if (PrefixChar != NameChar) {
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // If we broke out of the above loop because of a mismatch, determine
+ // the result of the comparison.
+ //
+
+ if (i < MinLength) {
+
+ //
+ // We also need to treat "\" as less than all other characters, so
+ // if the char is a "\" we'll drop it down to a value of zero.
+ //
+
+ if (PrefixChar == UnicodeBackSlash) {
+
+ return IsLessThan;
+ }
+
+ if (NameChar == UnicodeBackSlash) {
+
+ return IsGreaterThan;
+ }
+
+ //
+ // Now compare the characters
+ //
+
+ if (PrefixChar < NameChar) {
+
+ return IsLessThan;
+
+ } else if (PrefixChar > NameChar) {
+
+ return IsGreaterThan;
+ }
+ }
+
+ //
+ // They match upto the minimum length so now figure out the largest string
+ // and see if one is a proper prefix of the other
+ //
+
+ if (PrefixLength < NameLength) {
+
+ //
+ // The prefix string is shorter so if it is a proper prefix we
+ // return prefix otherwise we return less than (e.g., "\a" < "\ab")
+ //
+
+ if (Name->Buffer[PrefixLength] == UnicodeBackSlash) {
+
+ //DbgPrint("IsPrefix\n");
+
+ return IsPrefix;
+
+ } else {
+
+ //DbgPrint("IsLessThan\n");
+
+ return IsLessThan;
+ }
+
+ } else if (PrefixLength > NameLength) {
+
+ //
+ // The Prefix string is longer so we say that the prefix is
+ // greater than the name (e.g., "\ab" > "\a")
+ //
+
+ //DbgPrint("IsGreaterThan\n");
+
+ return IsGreaterThan;
+
+ } else {
+
+ //
+ // They lengths are equal so the strings are equal
+ //
+
+ //DbgPrint("IsEqual\n");
+
+ return IsEqual;
+ }
+}
+
diff --git a/private/ntos/rtl/prodtype.c b/private/ntos/rtl/prodtype.c
new file mode 100644
index 000000000..6822be1a3
--- /dev/null
+++ b/private/ntos/rtl/prodtype.c
@@ -0,0 +1,184 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ prodtype.c
+
+Abstract:
+
+ This module defines a function to determine the product type.
+
+Author:
+
+ Cliff Van Dyke (CliffV) 20-Mar-1992
+
+Revision History:
+
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlGetNtProductType)
+#endif
+
+
+BOOLEAN
+RtlGetNtProductType(
+ OUT PNT_PRODUCT_TYPE NtProductType
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the product type of the current system.
+
+Arguments:
+
+ NtProductType - Returns the product type. Either NtProductWinNt or
+ NtProductLanManNt.
+
+Return Value:
+
+ TRUE on success, FALSE on failure
+ The product type will be set to WinNt on failure
+
+--*/
+
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE KeyHandle;
+ PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
+ ULONG KeyValueInfoLength;
+ ULONG ResultLength;
+ UNICODE_STRING KeyPath;
+ UNICODE_STRING ValueName;
+ UNICODE_STRING Value;
+ UNICODE_STRING WinNtValue;
+ UNICODE_STRING LanmanNtValue;
+ UNICODE_STRING ServerNtValue;
+ BOOLEAN Result;
+
+ RTL_PAGED_CODE();
+
+ //
+ // if we are in gui setup mode, product type is read from the registry since
+ // gui setup mode is the only time product type can be changed.
+ // All other times, the "captured at boot" version of product type is used
+ //
+
+ if ( USER_SHARED_DATA->ProductTypeIsValid ) {
+ *NtProductType = USER_SHARED_DATA->NtProductType;
+ return TRUE;
+ }
+
+ //
+ // Prepare default value for failure case
+ //
+
+ *NtProductType = NtProductWinNt;
+ Result = FALSE;
+
+ RtlInitUnicodeString( &KeyPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\ProductOptions" );
+ RtlInitUnicodeString( &ValueName, L"ProductType" );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ Status = NtOpenKey( &KeyHandle,
+ MAXIMUM_ALLOWED,
+ &ObjectAttributes
+ );
+ KeyValueInformation = NULL;
+ if (NT_SUCCESS( Status )) {
+ KeyValueInfoLength = 256;
+ KeyValueInformation = RtlAllocateHeap( RtlProcessHeap(), 0,
+ KeyValueInfoLength
+ );
+ if (KeyValueInformation == NULL) {
+ Status = STATUS_NO_MEMORY;
+ } else {
+ Status = NtQueryValueKey( KeyHandle,
+ &ValueName,
+ KeyValueFullInformation,
+ KeyValueInformation,
+ KeyValueInfoLength,
+ &ResultLength
+ );
+ }
+ } else {
+ KeyHandle = NULL;
+ }
+
+ if (NT_SUCCESS( Status ) && KeyValueInformation->Type == REG_SZ) {
+
+ //
+ // Decide which product we are installed as
+ //
+
+ Value.Buffer = (PWSTR)((PCHAR)KeyValueInformation + KeyValueInformation->DataOffset);
+ Value.Length = (USHORT)(KeyValueInformation->DataLength - sizeof( UNICODE_NULL ));
+ Value.MaximumLength = (USHORT)(KeyValueInformation->DataLength);
+ RtlInitUnicodeString(&WinNtValue, L"WinNt");
+ RtlInitUnicodeString(&LanmanNtValue, L"LanmanNt");
+ RtlInitUnicodeString(&ServerNtValue, L"ServerNt");
+
+ if (RtlEqualUnicodeString(&Value, &WinNtValue, TRUE)) {
+ *NtProductType = NtProductWinNt;
+ Result = TRUE;
+ } else if (RtlEqualUnicodeString(&Value, &LanmanNtValue, TRUE)) {
+ *NtProductType = NtProductLanManNt;
+ Result = TRUE;
+ } else if (RtlEqualUnicodeString(&Value, &ServerNtValue, TRUE)) {
+ *NtProductType = NtProductServer;
+ Result = TRUE;
+ } else {
+#if DBG
+ DbgPrint("RtlGetNtProductType: Product type unrecognised <%wZ>\n", &Value);
+#endif // DBG
+ }
+ } else {
+#if DBG
+ DbgPrint("RtlGetNtProductType: %wZ\\%wZ not found or invalid type\n", &KeyPath, &ValueName );
+#endif // DBG
+ }
+
+ //
+ // Clean up our resources.
+ //
+
+ if (KeyValueInformation != NULL) {
+ RtlFreeHeap( RtlProcessHeap(), 0, KeyValueInformation );
+ }
+
+ if (KeyHandle != NULL) {
+ NtClose( KeyHandle );
+ }
+
+ //
+ // Return result.
+ //
+
+ return(Result);
+
+
+
+
+
+
+
+
+
+
+
+
+}
diff --git a/private/ntos/rtl/random.c b/private/ntos/rtl/random.c
new file mode 100644
index 000000000..a5629862f
--- /dev/null
+++ b/private/ntos/rtl/random.c
@@ -0,0 +1,111 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Random.c
+
+Abstract:
+
+ This module implements a simple random number generator
+
+Author:
+
+ Gary Kimura [GaryKi] 26-May-1989
+
+Environment:
+
+ Pure utility routine
+
+Revision History:
+
+--*/
+
+#include <ntrtlp.h>
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE, RtlRandom)
+#endif
+
+#define Multiplier ((ULONG)(0x80000000ul - 19)) // 2**31 - 19
+#define Increment ((ULONG)(0x80000000ul - 61)) // 2**31 - 61
+#define Modulus ((ULONG)(0x80000000ul - 1)) // 2**31 - 1
+
+#if !defined(NTOS_KERNEL_RUNTIME)
+ULONG
+RtlUniform (
+ IN OUT PULONG Seed
+ )
+
+/*++
+
+Routine Description:
+
+ A simple uniform random number generator, based on D.H. Lehmer's 1948
+ alrogithm.
+
+Arguments:
+
+ Seed - Supplies a pointer to the random number generator seed.
+
+Return Value:
+
+ ULONG - returns a random number uniformly distributed over [0..MAXLONG]
+
+--*/
+
+{
+ *Seed = ((Multiplier * (*Seed)) + Increment) % Modulus;
+ return *Seed;
+}
+#endif
+
+#define UniformMacro(Seed) ( \
+ *Seed = (((Multiplier * (*Seed)) + Increment) % Modulus) \
+ )
+
+
+extern ULONG V[];
+
+ULONG
+RtlRandom (
+ IN OUT PULONG Seed
+ )
+
+/*++
+
+Routine Description:
+
+ An every better random number generator based on MacLaren and Marsaglia.
+
+Arguments:
+
+ Seed - Supplies a pointer to the random number generator seed.
+
+Return Value:
+
+ ULONG - returns a random number uniformly distributed over [0..MAXLONG]
+
+--*/
+
+{
+ ULONG X;
+ ULONG Y;
+ ULONG j;
+ ULONG Result;
+
+ RTL_PAGED_CODE();
+
+ X = UniformMacro(Seed);
+ Y = UniformMacro(Seed);
+
+ j = Y % 128;
+
+ Result = V[j];
+
+ V[j] = X;
+
+ return Result;
+
+}
diff --git a/private/ntos/rtl/recip.c b/private/ntos/rtl/recip.c
new file mode 100644
index 000000000..603aeea24
--- /dev/null
+++ b/private/ntos/rtl/recip.c
@@ -0,0 +1,92 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ recip.c
+
+Abstract:
+
+ This module generates reciprocol fractions for implementing integer
+ division by multiplication.
+
+Author:
+
+ David N. Cutler (davec) 13-May-1989
+
+Environment:
+
+ User mode.
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+typedef struct _large_integer {
+ unsigned long LowPart;
+ long HighPart;
+ } large_integer;
+
+//long Divisors[] = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0};
+long Divisors[] = {10, 10000, 10000000, 86400000, 0};
+
+void
+main (argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ large_integer Fraction;
+ long Index;
+ long NumberBits;
+ long Remainder;
+
+ long i;
+
+ //
+ // Compute first few reciprocols.
+ //
+
+ for (Index = Divisors[i = 0]; Index != 0L; Index = Divisors[++i]) {
+ NumberBits = 0L;
+ Remainder = 1L;
+ Fraction.LowPart = 0L;
+ Fraction.HighPart = 0L;
+ while (Fraction.HighPart >= 0L) {
+ NumberBits += 1L;
+ Fraction.HighPart <<= 1L;
+ if ((Fraction.LowPart & 0x80000000) != 0L) {
+ Fraction.HighPart += 1L;
+ }
+ Fraction.LowPart <<= 1L;
+ Remainder <<= 1L;
+ if (Remainder >= Index) {
+ Remainder -= Index;
+ Fraction.LowPart |= 1L;
+ }
+ }
+ if (Remainder) {
+ if ((Fraction.LowPart == -1L) && (Fraction.HighPart == -1L)) {
+ Fraction.LowPart = 0L;
+ Fraction.HighPart = 0x80000000;
+ NumberBits -= 1L;
+ } else {
+ if (Fraction.LowPart == -1L) {
+ Fraction.LowPart = 0L;
+ Fraction.HighPart += 1L;
+ } else {
+ Fraction.LowPart += 1L;
+ }
+ }
+ }
+
+ printf("Divisor %2ld, Fraction %8lx, %8lx Shift %ld\n", Index,
+ Fraction.HighPart, Fraction.LowPart, NumberBits - 64L);
+ }
+
+ return;
+}
+
diff --git a/private/ntos/rtl/registry.c b/private/ntos/rtl/registry.c
new file mode 100644
index 000000000..a70209536
--- /dev/null
+++ b/private/ntos/rtl/registry.c
@@ -0,0 +1,633 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+
+Module Name:
+
+ registry.c
+
+Abstract:
+
+ (This file has been copied from the temporary hack that BryanWi and
+ ScottBi did in kernel mode. I saw no need to have it be in kernel
+ mode and it had many bugs caused as a result of being in kernel mode,
+ so I made it caller mode. Jim Kelly).
+
+
+
+ This module represents a quick and dirty Nt level registry. Each key
+ in the Registry is implemented as a file directory within a directory
+ tree whose root is the directory "\Registry" on the system disk.
+ A key's data is stored within a file called "Data.Reg" in the key's
+ directory, and a key's attributes is stored as the file "Attr.Reg"
+ within the directory.
+
+
+
+
+
+
+Author:
+
+ Bryan M. Willman (bryanwi) 30-Apr-1991
+ Scott Birrell (ScottBi) 6-Jun-1991
+
+Environment:
+
+ callable from Kernel or user mode.
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlpNtOpenKey)
+#pragma alloc_text(PAGE,RtlpNtCreateKey)
+#pragma alloc_text(PAGE,RtlpNtQueryValueKey)
+#pragma alloc_text(PAGE,RtlpNtSetValueKey)
+#pragma alloc_text(PAGE,RtlpNtMakeTemporaryKey)
+#pragma alloc_text(PAGE,RtlpNtEnumerateSubKey)
+#endif
+
+#define REG_INVALID_ATTRIBUTES (OBJ_EXCLUSIVE | OBJ_PERMANENT)
+
+
+
+
+
+//
+// Temporary Registry User APIs.
+//
+// NOTE: These are temporary implementations. Although there is no code
+// within that requires these API to be implemented as system services, the
+// eventual replacements for these routines will use the Object Manager and
+// hence require to be system services.
+//
+
+
+NTSTATUS
+RtlpNtOpenKey(
+ OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN ULONG Options
+ )
+
+/*++
+
+Routine Description:
+
+ This function opens a key in the Registry. The key must already exist.
+
+Arguments:
+
+ KeyHandle - Receives a value called a Handle which is used to access
+ the specified key in the Registration Database.
+
+ DesiredAccess - Specifies the Accesses desired
+
+ REG_KEY_READ - Generic Read access to key
+ REG_KEY_QUERY_VALUE - Query Key's value
+ REG_KEY_WRITE - Generic Write access to key
+ REG_KEY_SET_VALUE - Set Key's value
+
+ ObjectAttributes - Specifies the attributes of the key being opened.
+ Note that a key name must be specified. If a Root Directory
+ is specified, the name is relative to the root. The name of the
+ object must be within the name space allocated to the Registry, that
+ is, all names beginning "\Registry". RootHandle, if present, must
+ be a handle to "\", or "\Registry", or a key under "\Registry".
+
+ Options - REG_OPTION_READ_FUZZY - Allow Read access on handle even if
+ it is open for Read/Write access.
+
+Return Value:
+
+ NTSTATUS - Result code from call. The following are returned
+
+ STATUS_SUCCESS - The open was successful.
+
+ STATUS_INVALID_PARAMETER - A parameter other that object name was
+ invalid.
+
+ STATUS_OBJECT_NAME_INVALID - The key name has invalid syntax
+
+ STATUS_OBJECT_NAME_NOT_FOUND - No key of the given name exists
+
+ STATUS_ACCESS_DENIED - Caller does not have the requested access
+ to the specified key.
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if (ARGUMENT_PRESENT(ObjectAttributes)) {
+ ObjectAttributes->Attributes &= ~(REG_INVALID_ATTRIBUTES);
+ }
+
+ return( NtOpenKey( KeyHandle,
+ DesiredAccess,
+ ObjectAttributes
+ ) );
+
+ DBG_UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+RtlpNtCreateKey(
+ OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ IN ULONG Options,
+ IN PUNICODE_STRING Provider,
+ OUT OPTIONAL PULONG Disposition
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates or opens the specified key in the Registry. If
+ the key does not exist, it is created. If the key already exists, it
+ is opened.
+
+Arguments:
+
+ KeyHandle - Receives a value called a Handle which is used to access
+ the specified key in the Registration Database.
+
+ DesiredAccess - Specifies the Accesses desired
+
+ REG_KEY_READ - Generic Read access to key
+ REG_KEY_QUERY_VALUE - Query Key's value
+ REG_KEY_WRITE - Generic Write access to key
+ REG_KEY_SET_VALUE - Set Key's value
+
+ ObjectAttributes - Specifies the attributes of the key being opened.
+ Note that a key name must be specified. If a Root Directory
+ is specified, the name is relative to the root. The name of the
+ object must be within the name space allocated to the Registry, that
+ is, all names beginning "\Registry". RootHandle, if present, must
+ be a handle to "\", or "\Registry", or a key under "\Registry".
+
+
+ Options - REG_OPTION_READ_FUZZY - Allow Read access on handle even if it is
+ open for READ_WRITE access.
+
+ REG_OPTION_VOLATILE - Object is not to be stored across boots.
+
+ Provider - This parameter is reserved for future use and must currently
+ be set to NULL. It will be used in the future to specify the name of
+ the provider to be used for operations on this node and its descendant
+ nodes.
+
+ Disposition - This optional parameter is a pointer to a variable that
+ will receive a value indicating whether a new Registry key was
+ created or an existing one opened.
+
+ REG_CREATED_NEW_KEY - A new Registry Key was created
+ REG_OPENED_EXISTING_KEY - An existing Registry Key was opened
+
+Return Value:
+
+ NTSTATUS - Result code from call. The following are returned
+
+ STATUS_SUCCESS - The open was successful.
+
+ STATUS_INVALID_PARAMETER - A parameter other that object name was
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if (ARGUMENT_PRESENT(ObjectAttributes)) {
+ ObjectAttributes->Attributes &= ~(REG_INVALID_ATTRIBUTES);
+ }
+
+
+ return(NtCreateKey( KeyHandle,
+ DesiredAccess,
+ ObjectAttributes,
+ 0, //TitleIndex
+ NULL, //Class OPTIONAL,
+ REG_OPTION_NON_VOLATILE, //CreateOptions,
+ Disposition
+ ) );
+
+ DBG_UNREFERENCED_PARAMETER( Options );
+ DBG_UNREFERENCED_PARAMETER( Provider );
+}
+
+
+
+NTSTATUS
+RtlpNtQueryValueKey(
+ IN HANDLE KeyHandle,
+ OUT OPTIONAL PULONG KeyValueType,
+ OUT OPTIONAL PVOID KeyValue,
+ IN OUT OPTIONAL PULONG KeyValueLength,
+ OUT OPTIONAL PLARGE_INTEGER LastWriteTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function queries the value of a key.
+
+Arguments:
+
+ KeyHandle - Handle of a key opened for GENERIC_READ access via NtOpenKey.
+
+ KeyValueType - Optional pointer to variable that will receive the
+ client-defined type of the key value (if any). If no value has been
+ set for the key, 0 is returned.
+
+ KeyValue - Optional pointer to buffer in which part or all of the key's
+ value (as set on the most recent call to NtSetValueKey) will be
+ returned. If the key's value is too large to fit into the supplied
+ buffer, as much of the value as will fit into the buffer will be
+ returned and the warning STATUS_BUFFER_OVERFLOW is returned. If no
+ value has ever been set, nothing is returned. If NULL is specified
+ for this parameter, no Key Value is returned.
+
+ KeyValueLength - On input, this optional parameter points to a variable
+ that contains the length in bytes of the KeyValue buffer (if any). If
+ no KeyValue buffer is specified, the variable content on entry is
+ ignored. On return, the referenced variable (if any) receives the
+ FULL length in bytes of the key value. If the key's value is too
+ large to fit into the supplied buffer, as much of the value as will
+ fit into the buffer will be returned and the warning
+ STATUS_BUFFER_OVERFLOW is returned.
+
+ The returned length is intended for use by calling code in allocating
+ a buffer of sufficient size to hold the key's value. After receiving
+ STATUS_BUFFER_OVERFLOW from NtQueryValueKey, calling code may make a
+ subsequent call to NtQueryValueKey with a buffer of size equal to the
+ length returned by the prior call.
+
+ If no value has been set for the key, 0 is returned.
+
+ LastWriteTime - Optional parameter to variable which receives a time stamp
+ specifying the last time that the key was written.
+
+Return Value:
+
+ NTSTATUS - Result code
+
+ STATUS_SUCCESS - Call was successful
+
+ STATUS_INVALID_PARAMETER - Invalid parameter
+
+ STATUS_ACCESS_DENIED - Caller does not have GENERIC_READ access to
+ the specified key
+
+ STATUS_BUFFER_OVERFLOW - This is a warning that the key's value
+ is too large for the buffer specified by the KeyValue and
+ KeyValueLength parameters. Use the length returned to
+ determine the size of buffer to allocate for a subsequent
+ call of NtQueryValueKey.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PKEY_VALUE_FULL_INFORMATION KeyValueInformation = NULL;
+ ULONG LocalBufferLength, ResultLength, InputKeyValueLength;
+ UNICODE_STRING NullName;
+
+ RTL_PAGED_CODE();
+
+ InputKeyValueLength = 0;
+ if (ARGUMENT_PRESENT(KeyValueLength)) {
+ InputKeyValueLength = (*KeyValueLength);
+ }
+
+ NullName.Length = 0;
+
+ LocalBufferLength = InputKeyValueLength +
+ FIELD_OFFSET( KEY_VALUE_FULL_INFORMATION, Name );
+ KeyValueInformation = RtlAllocateHeap( RtlProcessHeap(), 0,
+ LocalBufferLength
+ );
+ if (KeyValueInformation == NULL) {
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+
+ Status = NtQueryValueKey( KeyHandle,
+ &NullName, //ValueName,
+ KeyValueFullInformation, //KeyValueInformationClass
+ (PVOID)KeyValueInformation,
+ LocalBufferLength,
+ &ResultLength
+ );
+
+//
+// //
+// // Temporary hack to allow query of header information without
+// // having to retrieve the entire key value.
+// //
+//
+// if (Status == STATUS_BUFFER_OVERFLOW) {
+//
+// //
+// // until BryanWi changes things so that the header information
+// // is always returned, allocate a buffer large enough to get
+// // all this stuff and query the value again.
+// //
+//
+// NTSTATUS TmpStatus;
+// PKEY_VALUE_FULL_INFORMATION TmpValue;
+//
+// TmpValue = RtlAllocateHeap( RtlProcessHeap(), 0, ResultLength);
+// ASSERT(TmpValue != NULL);
+//
+// TmpStatus = NtQueryValueKey( KeyHandle,
+// &NullName, //ValueName,
+// KeyValueFullInformation, //KeyValueInformationClass
+// (PVOID)TmpValue,
+// ResultLength,
+// &ResultLength
+// );
+// ASSERT(NT_SUCCESS(TmpStatus));
+//
+//
+// KeyValueInformation->DataLength = TmpValue->DataLength;
+// KeyValueInformation->Type = TmpValue->Type;
+//
+//
+// RtlFreeHeap( RtlProcessHeap(), 0, TmpValue );
+//
+// }
+
+
+ //
+ // Temporary hack to allow query of "" attribute when it hasn't
+ // yet been set.
+ //
+
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ Status = STATUS_SUCCESS;
+ KeyValueInformation->DataLength = 0;
+ KeyValueInformation->Type = 0;
+
+ }
+
+
+ //
+ // Return out parameter information
+ //
+
+ if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW) {
+
+ if (ARGUMENT_PRESENT(KeyValueLength)) {
+ (*KeyValueLength) = KeyValueInformation->DataLength;
+ }
+
+ if (ARGUMENT_PRESENT(KeyValueType)) {
+ (*KeyValueType) = KeyValueInformation->Type;
+ }
+
+ }
+ if (NT_SUCCESS(Status)) {
+
+ if (ARGUMENT_PRESENT(KeyValue)) {
+
+ RtlMoveMemory( KeyValue,
+ (VOID *)(((PUCHAR)KeyValueInformation) + KeyValueInformation->DataOffset),
+ KeyValueInformation->DataLength
+ );
+ }
+ }
+
+
+
+
+ if (KeyValueInformation != NULL) {
+ RtlFreeHeap( RtlProcessHeap(), 0, KeyValueInformation );
+ }
+
+ return(Status);
+
+ DBG_UNREFERENCED_PARAMETER( LastWriteTime );
+}
+
+
+
+NTSTATUS
+RtlpNtSetValueKey(
+ IN HANDLE KeyHandle,
+ IN ULONG KeyValueType,
+ IN OPTIONAL PVOID KeyValue,
+ IN ULONG KeyValueLength
+ )
+
+/*++
+
+Routine Description:
+
+ This function sets the type and value of a key.
+
+Arguments:
+
+ KeyHandle - Specifies a handle of the key whose type and value are to
+ be set. The key must have been opened with GENERIC_WRITE access.
+
+ KeyValueType - This is a value that the client of the registry defines to
+ distinguish different client-defined types of data value stored
+ with the key. When setting the value of a key that has previously
+ had a Type and Value stored, the Type may be changed.
+
+ KeyValue - Optional pointer to the data to be optionally stored as the
+ value of the key. If NULL is specified for this parameter, only
+ the value type will be written.
+
+ KeyValueLength - Specifies the length in bytes of the data to be stored as
+ the key's value. A zero value indicates that no data is being stored:
+ if zero is specified, the Value parameter will be ignored.
+
+Return Value:
+
+ NTSTATUS - Result code. The following values are returned
+
+ STATUS_SUCCESS - The call was successful
+
+ STATUS_INVALID_PARAMETER - Invalid Parameter(s)
+--*/
+
+{
+ UNICODE_STRING NullName;
+ NullName.Length = 0;
+
+ RTL_PAGED_CODE();
+
+ return( NtSetValueKey( KeyHandle,
+ &NullName, // ValueName
+ 0, // TitleIndex
+ KeyValueType,
+ KeyValue,
+ KeyValueLength
+ ) );
+}
+
+
+
+NTSTATUS
+RtlpNtMakeTemporaryKey(
+ IN HANDLE KeyHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This function makes a Registry key temporary. The key will be deleted
+ when the last handle to it is closed.
+
+Arguments:
+
+ KeyHandle - Specifies the handle of the Key. This is also the handle
+ of the key's directory.
+
+Return Value:
+
+ NTSTATUS - Standard Nt Result Code
+
+ STATUS_INVALID_HANDLE - The specified handle is invalid.
+
+ STATUS_ACCESS_DENIED - The specified handle does not specify delet
+ access.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return( NtDeleteKey(KeyHandle) );
+}
+
+
+NTSTATUS
+RtlpNtEnumerateSubKey(
+ IN HANDLE KeyHandle,
+ OUT PUNICODE_STRING SubKeyName,
+ IN ULONG Index,
+ OUT PLARGE_INTEGER LastWriteTime
+ )
+
+/*++
+
+Routine Description:
+
+ This function finds the name of the next sub key of a given key. By
+ making successive calls, all of the sub keys of a key can be determined.
+
+
+Arguments:
+
+ KeyHandle - Handle of the key whose sub keys are to be enumerated.
+
+ SubKeyName - Pointer to a Unicode String in which the name of the sub
+ key will be returned.
+
+ Index - Specifies the (ZERO-based) number of the sub key to be returned.
+
+
+ LastWriteTime - Receives the time stamp that specifies when the key
+ was last written.
+
+Return Value:
+
+ NTSTATUS - Result code
+
+ STATUS_SUCCESS - The call succeeded
+
+ STATUS_INVALID_PARAMETER - Invalid parameter
+
+ STATUS_NO_MORE_ENTRIES - There is no key having the specified index
+
+ STATUS_BUFFER_OVERFLOW - The buffer of the output string was not
+ large enough to hold the next sub-key name. SubKeyName->Length
+ contains the number of bytes required.
+
+ STATUS_NO_MEMORY - There was not sufficient heap to perform the
+ requested operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PKEY_BASIC_INFORMATION KeyInformation = NULL;
+ ULONG LocalBufferLength, ResultLength;
+
+ RTL_PAGED_CODE();
+
+ LocalBufferLength = 0;
+ if (SubKeyName->MaximumLength > 0) {
+
+ LocalBufferLength = SubKeyName->MaximumLength +
+ FIELD_OFFSET(KEY_BASIC_INFORMATION, Name);
+ KeyInformation = RtlAllocateHeap( RtlProcessHeap(), 0,
+ LocalBufferLength
+ );
+ if (KeyInformation == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+ }
+
+ Status = NtEnumerateKey( KeyHandle,
+ Index,
+ KeyBasicInformation, //KeyInformationClass
+ (PVOID)KeyInformation,
+ LocalBufferLength,
+ &ResultLength
+ );
+
+ if (NT_SUCCESS(Status)) {
+
+ if ( SubKeyName->MaximumLength >= KeyInformation->NameLength) {
+
+ SubKeyName->Length = (USHORT)KeyInformation->NameLength;
+
+ RtlMoveMemory( SubKeyName->Buffer,
+ &KeyInformation->Name[0],
+ SubKeyName->Length
+ );
+ } else {
+ Status = STATUS_BUFFER_OVERFLOW;
+ }
+ }
+
+ //
+ // Return the length required if we failed due to a small buffer
+ //
+
+ if (Status == STATUS_BUFFER_OVERFLOW) {
+ SubKeyName->Length = (USHORT)(ResultLength -
+ FIELD_OFFSET(KEY_BASIC_INFORMATION, Name));
+ }
+
+
+ //
+ // Free up any memory we allocated
+ //
+
+ if (KeyInformation != NULL) {
+
+ RtlFreeHeap( RtlProcessHeap(), 0,
+ KeyInformation
+ );
+ }
+
+
+ return(Status);
+
+ DBG_UNREFERENCED_PARAMETER( LastWriteTime );
+
+}
diff --git a/private/ntos/rtl/regutil.c b/private/ntos/rtl/regutil.c
new file mode 100644
index 000000000..ff7264d41
--- /dev/null
+++ b/private/ntos/rtl/regutil.c
@@ -0,0 +1,1553 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ regutil.c
+
+Abstract:
+
+ This file contains support routines for accessing the registry.
+
+Author:
+
+ Steve Wood (stevewo) 15-Apr-1992
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include <ctype.h>
+
+NTSTATUS
+RtlpGetRegistryHandle(
+ IN ULONG RelativeTo,
+ IN PWSTR KeyName,
+ IN BOOLEAN WriteAccess,
+ OUT PHANDLE Key
+ );
+
+NTSTATUS
+RtlpQueryRegistryDirect(
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN OUT PVOID Destination
+ );
+
+NTSTATUS
+RtlpCallQueryRegistryRoutine(
+ IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
+ IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
+ IN ULONG KeyValueInfoLength,
+ IN PVOID Context,
+ IN PVOID Environment OPTIONAL
+ );
+
+NTSTATUS
+RtlpInitCurrentUserString(
+ OUT PUNICODE_STRING UserString
+ );
+
+
+NTSTATUS
+RtlpGetTimeZoneInfoHandle(
+ IN BOOLEAN WriteAccess,
+ OUT PHANDLE Key
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlpGetRegistryHandle)
+#pragma alloc_text(PAGE,RtlpQueryRegistryDirect)
+#pragma alloc_text(PAGE,RtlpCallQueryRegistryRoutine)
+#pragma alloc_text(PAGE,RtlQueryRegistryValues)
+#pragma alloc_text(PAGE,RtlWriteRegistryValue)
+#pragma alloc_text(PAGE,RtlCheckRegistryKey)
+#pragma alloc_text(PAGE,RtlCreateRegistryKey)
+#pragma alloc_text(PAGE,RtlDeleteRegistryValue)
+#pragma alloc_text(PAGE,RtlExpandEnvironmentStrings_U)
+#pragma alloc_text(PAGE,RtlGetNtGlobalFlags)
+#pragma alloc_text(PAGE,RtlpInitCurrentUserString)
+#pragma alloc_text(PAGE,RtlOpenCurrentUser)
+#pragma alloc_text(PAGE,RtlpGetTimeZoneInfoHandle)
+#pragma alloc_text(PAGE,RtlQueryTimeZoneInformation)
+#pragma alloc_text(PAGE,RtlSetTimeZoneInformation)
+#pragma alloc_text(PAGE,RtlSetActiveTimeBias)
+#endif
+
+extern PWSTR RtlpRegistryPaths[ RTL_REGISTRY_MAXIMUM ];
+
+NTSTATUS
+RtlpGetRegistryHandle(
+ IN ULONG RelativeTo,
+ IN PWSTR KeyName,
+ IN BOOLEAN WriteAccess,
+ OUT PHANDLE Key
+ )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ WCHAR KeyPathBuffer[ MAXIMUM_FILENAME_LENGTH+6 ];
+ UNICODE_STRING KeyPath;
+ UNICODE_STRING CurrentUserKeyPath;
+ BOOLEAN OptionalPath;
+
+ if (RelativeTo & RTL_REGISTRY_HANDLE) {
+ *Key = (HANDLE)KeyName;
+ return STATUS_SUCCESS;
+ }
+
+ if (RelativeTo & RTL_REGISTRY_OPTIONAL) {
+ RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
+ OptionalPath = TRUE;
+ }
+ else {
+ OptionalPath = FALSE;
+ }
+
+ if (RelativeTo >= RTL_REGISTRY_MAXIMUM) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ KeyPath.Buffer = KeyPathBuffer;
+ KeyPath.Length = 0;
+ KeyPath.MaximumLength = sizeof( KeyPathBuffer );
+ if (RelativeTo != RTL_REGISTRY_ABSOLUTE) {
+ if (RelativeTo == RTL_REGISTRY_USER &&
+ NT_SUCCESS( RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath ) )
+ ) {
+ Status = RtlAppendUnicodeStringToString( &KeyPath, &CurrentUserKeyPath );
+ RtlFreeUnicodeString( &CurrentUserKeyPath );
+ }
+ else {
+ Status = RtlAppendUnicodeToString( &KeyPath, RtlpRegistryPaths[ RelativeTo ] );
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ Status = RtlAppendUnicodeToString( &KeyPath, L"\\" );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+ }
+
+ Status = RtlAppendUnicodeToString( &KeyPath, KeyName );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ if (WriteAccess) {
+ Status = ZwCreateKey( Key,
+ GENERIC_WRITE,
+ &ObjectAttributes,
+ 0,
+ (PUNICODE_STRING) NULL,
+ 0,
+ NULL
+ );
+ }
+ else {
+ Status = ZwOpenKey( Key,
+ MAXIMUM_ALLOWED | GENERIC_READ,
+ &ObjectAttributes
+ );
+ }
+#if DBG
+ if (!NT_SUCCESS( Status ) && !OptionalPath) {
+ DbgPrint( "RTL: %wZ key not found - Status == %x \n", &KeyPath, Status );
+ }
+#endif // DBG
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlpQueryRegistryDirect(
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN OUT PVOID Destination
+ )
+{
+ if (ValueType == REG_SZ ||
+ ValueType == REG_EXPAND_SZ ||
+ ValueType == REG_MULTI_SZ
+ ) {
+ PUNICODE_STRING DestinationString;
+
+ DestinationString = (PUNICODE_STRING)Destination;
+ if (DestinationString->Buffer == NULL) {
+ DestinationString->Buffer = RtlAllocateStringRoutine( ValueLength );
+ DestinationString->MaximumLength = (USHORT)ValueLength;
+ }
+ else
+ if (ValueLength > DestinationString->MaximumLength) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ RtlMoveMemory( DestinationString->Buffer, ValueData, ValueLength );
+ DestinationString->Length = (USHORT)(ValueLength - sizeof( UNICODE_NULL ));
+ }
+ else
+ if (ValueLength <= sizeof( ULONG )) {
+ RtlMoveMemory( Destination, ValueData, ValueLength );
+ }
+ else {
+ PULONG DestinationLength;
+
+ DestinationLength = (PULONG)Destination;
+ if ((LONG)*DestinationLength < 0) {
+ ULONG n = -(LONG)*DestinationLength;
+
+ if (n < ValueLength) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+ else {
+ RtlMoveMemory( DestinationLength, ValueData, ValueLength );
+ }
+ }
+ else
+ if (*DestinationLength < (ValueLength + sizeof( ValueLength ) + sizeof( ValueType ))
+ ) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+ else {
+ *DestinationLength++ = ValueLength;
+ *DestinationLength++ = ValueType;
+ RtlMoveMemory( DestinationLength, ValueData, ValueLength );
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+RtlpCallQueryRegistryRoutine(
+ IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
+ IN PKEY_VALUE_FULL_INFORMATION KeyValueInformation,
+ IN ULONG KeyValueInfoLength,
+ IN PVOID Context,
+ IN PVOID Environment OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function implements the caller out the a caller specified
+ routine. It is reponsible for capturing the arguments for the
+ routine and then calling it. If not specifically disabled, this
+ routine will converted REG_EXPAND_SZ Registry values to REG_SZ by
+ calling RtlExpandEnvironmentStrings_U prior to calling the routine.
+ It will also converted REG_MULTI_SZ registry values into multiple
+ REG_SZ calls to the specified routine.
+
+Arguments:
+
+ QueryTable - specifies the current query table entry.
+
+ KeyValueInformation - points to a buffer that contains the information
+ about the current registry value.
+
+ KeyValueInfoLength - specifies the maximum length of the buffer pointed
+ to by the KeyValueInformaiton parameter. This function will use the
+ unused portion at the end of this buffer for storing null terminated
+ value name strings and the expanded version of REG_EXPAND_SZ values.
+
+ Context - specifies a 32-bit quantity that is passed uninterpreted to
+ each QueryRoutine called.
+
+ Environment - optional parameter, that if specified is the environment
+ used when expanding variable values in REG_EXPAND_SZ registry
+ values.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG ValueType;
+ PWSTR ValueName;
+ PVOID ValueData;
+ ULONG ValueLength;
+ PWSTR s;
+ PCHAR FreeMem;
+ ULONG FreeMemSize;
+
+ //
+ // Initially assume the entire KeyValueInformation buffer is unused.
+ //
+
+ FreeMem = (PCHAR)KeyValueInformation;
+ FreeMemSize = KeyValueInfoLength;
+ if (KeyValueInformation->Type == REG_NONE ||
+ (KeyValueInformation->DataLength == 0 &&
+ KeyValueInformation->Type == QueryTable->DefaultType
+ )
+ ) {
+ //
+ // If there is no registry value then see if they want to default
+ // this value.
+ //
+
+ if (QueryTable->DefaultType == REG_NONE) {
+ //
+ // No default value specified. Return success unless this is
+ // a required value.
+ //
+
+ if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) {
+ return( STATUS_SUCCESS );
+ }
+ else {
+ UNICODE_STRING KeyValueName;
+
+ if (QueryTable->Name) {
+ RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
+ }
+ else
+ if (KeyValueInformation->Type != REG_NONE) {
+ KeyValueName.Buffer = KeyValueInformation->Name;
+ KeyValueName.Length = (USHORT)KeyValueInformation->NameLength;
+ KeyValueName.MaximumLength = KeyValueName.Length;
+ }
+ else {
+ RtlInitUnicodeString( &KeyValueName, L"*** Unknown ***" );
+ }
+ return( STATUS_OBJECT_NAME_NOT_FOUND );
+ }
+ }
+
+ //
+ // Default requested. Setup the value data pointers from the
+ // information in the table entry.
+ //
+
+ ValueName = QueryTable->Name,
+ ValueType = QueryTable->DefaultType;
+ ValueData = QueryTable->DefaultData;
+ ValueLength = QueryTable->DefaultLength;
+ if (ValueLength == 0) {
+ //
+ // If the length of the value is zero, then calculate the
+ // actual length for REG_SZ, REG_EXPAND_SZ and REG_MULTI_SZ
+ // value types.
+ //
+
+ s = (PWSTR)ValueData;
+ if (ValueType == REG_SZ || ValueType == REG_EXPAND_SZ) {
+ while (*s++ != UNICODE_NULL) {
+ }
+ ValueLength = (PCHAR)s - (PCHAR)ValueData;
+ }
+ else
+ if (ValueType == REG_MULTI_SZ) {
+ while (*s != UNICODE_NULL) {
+ while (*s++ != UNICODE_NULL) {
+ }
+ }
+
+ ValueLength = ((PCHAR)s - (PCHAR)ValueData) + sizeof( UNICODE_NULL );
+ }
+ }
+ }
+ else {
+ if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)) {
+ //
+ // There is a registry value. Calculate a pointer to the
+ // free memory at the end of the value information buffer,
+ // and its size.
+ //
+
+ FreeMem += KeyValueInformation->DataOffset +
+ KeyValueInformation->DataLength;
+ FreeMem = (PCHAR)(((ULONG)FreeMem + 7) & ~7);
+ FreeMemSize -= (FreeMem - (PCHAR)KeyValueInformation);
+
+ //
+ // See if there is room in the free memory area for a null
+ // terminated copy of the value name string. If not return
+ // and error.
+ //
+
+ if (FreeMemSize <= KeyValueInformation->NameLength) {
+ return( STATUS_NO_MEMORY );
+ }
+
+ //
+ // There is room, so copy the string, and null terminate it.
+ //
+
+ ValueName = (PWSTR)FreeMem;
+ RtlMoveMemory( ValueName,
+ KeyValueInformation->Name,
+ KeyValueInformation->NameLength
+ );
+ *(PWSTR)((PCHAR)ValueName + KeyValueInformation->NameLength) = UNICODE_NULL;
+
+ //
+ // Update the free memory pointer and size to reflect the space we
+ // just used for the null terminated value name.
+ //
+
+ FreeMem += KeyValueInformation->NameLength + sizeof( UNICODE_NULL );
+ FreeMem = (PCHAR)(((ULONG)FreeMem + 7) & ~7);
+ FreeMemSize -= (FreeMem - (PCHAR)KeyValueInformation);
+ }
+ else {
+ ValueName = QueryTable->Name;
+ }
+
+ //
+ // Get the remaining data for the registry value.
+ //
+
+ ValueType = KeyValueInformation->Type;
+ ValueData = (PCHAR)KeyValueInformation + KeyValueInformation->DataOffset;
+ ValueLength = KeyValueInformation->DataLength;
+ }
+
+ //
+ // Unless specifically disabled for this table entry, preprocess
+ // registry values of type REG_EXPAND_SZ and REG_MULTI_SZ
+ //
+
+ if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND)) {
+ if (ValueType == REG_MULTI_SZ) {
+ PWSTR ValueEnd;
+
+ //
+ // For REG_MULTI_SZ value type, call the query routine once
+ // for each null terminated string in the registry value. Fake
+ // like this is multiple REG_SZ values with the same value name.
+ //
+
+ Status = STATUS_SUCCESS;
+ ValueEnd = (PWSTR)((PCHAR)ValueData + ValueLength) - 2;
+ s = (PWSTR)ValueData;
+ while (s < ValueEnd) {
+ while (*s++ != UNICODE_NULL) {
+ }
+
+ ValueLength = (PCHAR)s - (PCHAR)ValueData;
+ if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
+ Status = RtlpQueryRegistryDirect( REG_SZ,
+ ValueData,
+ ValueLength,
+ QueryTable->EntryContext
+ );
+ (PUNICODE_STRING)(QueryTable->EntryContext) += 1;
+ }
+ else {
+ Status = (QueryTable->QueryRoutine)( ValueName,
+ REG_SZ,
+ ValueData,
+ ValueLength,
+ Context,
+ QueryTable->EntryContext
+ );
+ }
+
+ if (!NT_SUCCESS( Status )) {
+#if DBG
+ DbgPrint( "RTL: QueryRoutine( %ws ) failed - Status == %lx\n", ValueName, Status );
+#endif // DBG
+ break;
+ }
+
+ ValueData = (PVOID)s;
+ }
+
+ return( Status );
+ }
+ else
+ if (ValueType == REG_EXPAND_SZ) {
+ //
+ // For REG_EXPAND_SZ value type, expand any environment variable
+ // references in the registry value string using the Rtl function.
+ //
+
+ UNICODE_STRING Source;
+ UNICODE_STRING Destination;
+
+ Source.Buffer = (PWSTR)ValueData;
+ Source.MaximumLength = (USHORT)ValueLength;
+ Source.Length = (USHORT)(Source.MaximumLength - sizeof(WCHAR));
+ Destination.Buffer = (PWSTR)FreeMem;
+ Destination.Length = 0;
+ Destination.MaximumLength = (USHORT)FreeMemSize;
+
+ Status = RtlExpandEnvironmentStrings_U( Environment,
+ &Source,
+ &Destination,
+ NULL
+ );
+ if (!NT_SUCCESS( Status )) {
+#if DBG
+ DbgPrint( "RTL: Expand variables for %wZ failed - Status == %lx\n", &Source, Status );
+#endif // DBG
+ return( Status );
+ }
+
+ ValueData = Destination.Buffer;
+ ValueLength = Destination.Length + sizeof( UNICODE_NULL );
+ ValueType = REG_SZ;
+ }
+ }
+
+ //
+ // No special process of the registry value required so just call
+ // the query routine.
+ //
+
+ if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) {
+ Status = RtlpQueryRegistryDirect( ValueType,
+ ValueData,
+ ValueLength,
+ QueryTable->EntryContext
+ );
+ }
+ else {
+ Status = (QueryTable->QueryRoutine)( ValueName,
+ ValueType,
+ ValueData,
+ ValueLength,
+ Context,
+ QueryTable->EntryContext
+ );
+
+ }
+
+#if DBG
+ if (!NT_SUCCESS( Status )) {
+ DbgPrint( "RTL: QueryRoutine( %ws ) failed - Status == %lx\n", ValueName, Status );
+ }
+#endif
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlQueryRegistryValues(
+ IN ULONG RelativeTo,
+ IN PWSTR Path,
+ IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
+ IN PVOID Context,
+ IN PVOID Environment OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function allows the caller to query multiple values from the registry
+ sub-tree with a single call. The caller specifies an initial key path,
+ and a table. The table contains one or more entries that describe the
+ key values and subkey names the caller is interested in. This function
+ starts at the initial key and enumerates the entries in the table. For
+ each entry that specifies a value name or subkey name that exists in
+ the registry, this function calls the caller's query routine associated
+ with each table entry. The caller's query routine is passed the value
+ name, type, data and data length, to do with what they wish.
+
+Arguments:
+
+ RelativeTo - specifies that the Path parameter is either an absolute
+ registry path, or a path relative to a predefined key path. The
+ following values are defined:
+
+ RTL_REGISTRY_ABSOLUTE - Path is an absolute registry path
+ RTL_REGISTRY_SERVICES - Path is relative to \Registry\Machine\System\CurrentControlSet\Services
+ RTL_REGISTRY_CONTROL - Path is relative to \Registry\Machine\System\CurrentControlSet\Control
+ RTL_REGISTRY_WINDOWS_NT - Path is relative to \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
+ RTL_REGISTRY_DEVICEMAP - Path is relative to \Registry\Machine\Hardware\DeviceMap
+ RTL_REGISTRY_USER - Path is relative to \Registry\User\CurrentUser
+
+ RTL_REGISTRY_OPTIONAL - Bit that specifies the key referenced by
+ this parameter and the Path parameter is
+ optional.
+
+ RTL_REGISTRY_HANDLE - Bit that specifies that the Path parameter
+ is actually a registry handle to use.
+ optional.
+
+ Path - specifies either an absolute registry path, or a path relative to the
+ known location specified by the RelativeTo parameter. If the the
+ RTL_REGISTRY_HANDLE flag is specified, then this parameter is a
+ registry handle to use directly.
+
+ QueryTable - specifies a table of one or more value names and subkey names
+ that the caller is interested. Each table entry contains a query routine
+ that will be called for each value name that exists in the registry.
+ The table is terminated when a NULL table entry is reached. A NULL
+ table entry is defined as a table entry with a NULL QueryRoutine
+ and a NULL Name field.
+
+ QueryTable entry fields:
+
+ PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine - This routine is
+ called with the name, type, data and data length of a
+ registry value. If this field is NULL, then it marks the
+ end of the table.
+
+ ULONG Flags - These flags control how the following fields are
+ interpreted. The following flags are defined:
+
+ RTL_QUERY_REGISTRY_SUBKEY - says the Name field of this
+ table entry is another path to a registry key and all
+ following table entries are for that key rather than the
+ key specified by the Path parameter. This change in
+ focus lasts until the end of the table or another
+ RTL_QUERY_REGISTRY_SUBKEY entry is seen or
+ RTL_QUERY_REGISTRY_TOPKEY entry is seen. Each such
+ entry must specify a path that is relative to the Path
+ specified on the call to this function.
+
+ RTL_QUERY_REGISTRY_TOPKEY - resets the current registry key
+ handle to the original one specified by the RelativeTo
+ and Path parameters. Useful for getting back to the
+ original node after descending into subkeys with the
+ RTL_QUERY_REGISTRY_SUBKEY flag.
+
+ RTL_QUERY_REGISTRY_REQUIRED - specifies that this value is
+ required and if not found then STATUS_OBJECT_NAME_NOT_FOUND
+ is returned. For a table entry that specifies a NULL
+ name so that this function will enumerate all of the
+ value names under a key, STATUS_OBJECT_NAME_NOT_FOUND
+ will be returned only if there are no value keys under
+ the current key.
+
+ RTL_QUERY_REGISTRY_NOVALUE - specifies that even though
+ there is no Name field for this table entry, all the
+ caller wants is a call back, it does NOT want to
+ enumerate all the values under the current key. The
+ query routine is called with NULL for ValueData,
+ REG_NONE for ValueType and zero for ValueLength.
+
+ RTL_QUERY_REGISTRY_NOEXPAND - specifies that if the value
+ type of this registry value is REG_EXPAND_SZ or
+ REG_MULTI_SZ, then this function is NOT to do any
+ preprocessing of the registry values prior to calling
+ the query routine. Default behavior is to expand
+ environment variable references in REG_EXPAND_SZ
+ values and to enumerate the NULL terminated strings
+ in a REG_MULTI_SZ value and call the query routine
+ once for each, making it look like multiple REG_SZ
+ values with the same ValueName.
+
+ RTL_QUERY_REGISTRY_DIRECT QueryRoutine field ignored.
+ EntryContext field points to location to store value.
+ For null terminated strings, EntryContext points to
+ UNICODE_STRING structure that that describes maximum
+ size of buffer. If .Buffer field is NULL then a buffer
+ is allocated.
+
+ PWSTR Name - This field gives the name of a Value the caller
+ wants to query the value of. If this field is NULL, then
+ the QueryRoutine specified for this table entry is called
+ for all values associated with the current registry key.
+
+ PVOID EntryContext - This field is an arbitrary 32-bit field
+ that is passed uninterpreted to each QueryRoutine called.
+
+ ULONG DefaultType
+ PVOID DefaultData
+ ULONG DefaultLength If there is no value name that matches the
+ name given by the Name field, and the DefaultType field is
+ not REG_NONE, then the QueryRoutine for this table entry is
+ called with the contents of the following fields as if the
+ value had been found in the registry. If the DefaultType is
+ REG_SZ, REG_EXPANDSZ or REG_MULTI_SZ and the DefaultLength
+ is 0 then the value of DefaultLength will be computed based
+ on the length of unicode string pointed to by DefaultData
+
+ Context - specifies a 32-bit quantity that is passed uninterpreted to
+ each QueryRoutine called.
+
+ Environment - optional parameter, that if specified is the environment
+ used when expanding variable values in REG_EXPAND_SZ registry
+ values.
+
+Return Value:
+
+ Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING KeyPath, KeyValueName;
+ HANDLE Key, Key1;
+ PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
+ ULONG KeyValueInfoLength;
+ ULONG ValueIndex;
+ ULONG ResultLength;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ KeyValueInformation = NULL;
+ KeyValueInfoLength = 0x10000;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&KeyValueInformation,
+ 0,
+ &KeyValueInfoLength,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
+ ZwClose( Key );
+ }
+
+ return( Status );
+ }
+
+ Key1 = Key;
+ while (QueryTable->QueryRoutine != NULL ||
+ (QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_DIRECT))
+ ) {
+
+ if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
+ (QueryTable->Name == NULL ||
+ (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
+ QueryTable->QueryRoutine != NULL)
+ ) {
+ Status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY | RTL_QUERY_REGISTRY_SUBKEY)) {
+ if (Key1 != Key) {
+ NtClose( Key1 );
+ Key1 = Key;
+ }
+ }
+
+ if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) {
+ if (QueryTable->Name == NULL) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ else {
+ RtlInitUnicodeString( &KeyPath, QueryTable->Name );
+ InitializeObjectAttributes( &ObjectAttributes,
+ &KeyPath,
+ OBJ_CASE_INSENSITIVE,
+ Key,
+ NULL
+ );
+ Status = ZwOpenKey( &Key1,
+ MAXIMUM_ALLOWED,
+ &ObjectAttributes
+ );
+ if (NT_SUCCESS( Status )) {
+ if (QueryTable->QueryRoutine != NULL) {
+ goto enumvalues;
+ }
+ }
+#if DBG
+ else
+ if (!(QueryTable->Flags & RTL_REGISTRY_OPTIONAL)) {
+ DbgPrint( "RTL: %wZ sub key not found.\n", &KeyPath );
+ }
+#endif // DBG
+ }
+ }
+ else
+ if (QueryTable->Name != NULL) {
+ RtlInitUnicodeString( &KeyValueName, QueryTable->Name );
+retryqueryvalue:
+ Status = ZwQueryValueKey( Key1,
+ &KeyValueName,
+ KeyValueFullInformation,
+ KeyValueInformation,
+ KeyValueInfoLength,
+ &ResultLength
+ );
+ if (!NT_SUCCESS( Status )) {
+ if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
+
+ KeyValueInformation->Type = REG_NONE;
+ KeyValueInformation->DataLength = 0;
+ Status = RtlpCallQueryRegistryRoutine( QueryTable,
+ KeyValueInformation,
+ KeyValueInfoLength,
+ Context,
+ Environment
+ );
+ }
+
+ else if (Status == STATUS_BUFFER_OVERFLOW) {
+ PVOID NewBuffer = NULL;
+
+ //
+ // Try to allocate a larger buffer as this is one humongous
+ // value.
+ //
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ &NewBuffer,
+ 0,
+ &ResultLength,
+ MEM_COMMIT,
+ PAGE_READWRITE );
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&KeyValueInformation,
+ &KeyValueInfoLength,
+ MEM_RELEASE );
+ KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION)NewBuffer;
+ KeyValueInfoLength = ResultLength;
+ goto retryqueryvalue;
+ }
+ }
+ else {
+ if ( KeyValueInformation->Type == REG_MULTI_SZ ) {
+ try {
+ RtlZeroMemory((PUCHAR)KeyValueInformation+ResultLength,2);
+ KeyValueInformation->DataLength += 2;
+ }
+ except(EXCEPTION_EXECUTE_HANDLER) {
+ ;
+ }
+ }
+ Status = RtlpCallQueryRegistryRoutine( QueryTable,
+ KeyValueInformation,
+ KeyValueInfoLength,
+ Context,
+ Environment
+ );
+ //
+ // If requested, delete the value key after it has been successfully queried.
+ //
+
+ if (NT_SUCCESS( Status ) && QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
+ ZwDeleteValueKey (Key1, &KeyValueName);
+ }
+ }
+ }
+ else
+ if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE) {
+ Status = (QueryTable->QueryRoutine)( NULL,
+ REG_NONE,
+ NULL,
+ 0,
+ Context,
+ QueryTable->EntryContext
+ );
+ }
+ else {
+enumvalues:
+ for (ValueIndex = 0; TRUE; ValueIndex++) {
+ Status = ZwEnumerateValueKey( Key1,
+ ValueIndex,
+ KeyValueFullInformation,
+ KeyValueInformation,
+ KeyValueInfoLength,
+ &ResultLength
+ );
+ if (Status == STATUS_NO_MORE_ENTRIES) {
+ if (ValueIndex != 0 || !(QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED)) {
+ Status = STATUS_SUCCESS;
+ }
+ else {
+#if DBG
+ DbgPrint( "RTL: No values found under %wZ key.\n", &KeyPath );
+#endif // DBG
+ Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ break;
+ }
+ else
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+
+ Status = RtlpCallQueryRegistryRoutine( QueryTable,
+ KeyValueInformation,
+ KeyValueInfoLength,
+ Context,
+ Environment
+ );
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+
+ //
+ // If requested, delete the value key after it has been successfully queried.
+ //
+
+ if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE) {
+ KeyValueName.Buffer = KeyValueInformation->Name;
+ KeyValueName.Length = (USHORT)KeyValueInformation->NameLength;
+ KeyValueName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
+ Status = ZwDeleteValueKey( Key1,
+ &KeyValueName
+ );
+ if (NT_SUCCESS( Status )) {
+ ValueIndex -= 1;
+ }
+ }
+ }
+ }
+
+ if (!NT_SUCCESS( Status )) {
+ break;
+ }
+
+ QueryTable++;
+ }
+
+ if (Key != NULL && !(RelativeTo & RTL_REGISTRY_HANDLE)) {
+ ZwClose( Key );
+ }
+ if (Key1 != NULL && Key1 != Key) {
+ ZwClose( Key1 );
+ }
+ ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&KeyValueInformation,
+ &KeyValueInfoLength,
+ MEM_RELEASE
+ );
+ return( Status );
+}
+
+
+NTSTATUS
+RtlWriteRegistryValue(
+ IN ULONG RelativeTo,
+ IN PWSTR Path,
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING KeyValueName;
+ HANDLE Key;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ RtlInitUnicodeString( &KeyValueName, ValueName );
+ Status = ZwSetValueKey( Key,
+ &KeyValueName,
+ 0,
+ ValueType,
+ ValueData,
+ ValueLength
+ );
+ if (!(RelativeTo & RTL_REGISTRY_HANDLE)) {
+ ZwClose( Key );
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+RtlCheckRegistryKey(
+ IN ULONG RelativeTo,
+ IN PWSTR Path
+ )
+{
+ NTSTATUS Status;
+ HANDLE Key;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetRegistryHandle( RelativeTo, Path, FALSE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ ZwClose( Key );
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlCreateRegistryKey(
+ IN ULONG RelativeTo,
+ IN PWSTR Path
+ )
+{
+ NTSTATUS Status;
+ HANDLE Key;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ ZwClose( Key );
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlDeleteRegistryValue(
+ IN ULONG RelativeTo,
+ IN PWSTR Path,
+ IN PWSTR ValueName
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING KeyValueName;
+ HANDLE Key;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetRegistryHandle( RelativeTo, Path, TRUE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ RtlInitUnicodeString( &KeyValueName, ValueName );
+ Status = ZwDeleteValueKey( Key, &KeyValueName );
+
+ ZwClose( Key );
+ return Status;
+}
+
+
+NTSTATUS
+RtlExpandEnvironmentStrings_U(
+ IN PVOID Environment OPTIONAL,
+ IN PUNICODE_STRING Source,
+ OUT PUNICODE_STRING Destination,
+ OUT PULONG ReturnedLength OPTIONAL
+ )
+{
+ NTSTATUS Status, Status1;
+ PWCHAR Src, Src1, Dst;
+ UNICODE_STRING VariableName, VariableValue;
+ ULONG SrcLength, DstLength, VarLength, RequiredLength;
+
+ RTL_PAGED_CODE();
+
+ Src = Source->Buffer;
+ SrcLength = Source->Length;
+ Dst = Destination->Buffer;
+ DstLength = Destination->MaximumLength;
+ Status = STATUS_SUCCESS;
+ RequiredLength = 0;
+ while (SrcLength) {
+ if (*Src == L'%') {
+ Src1 = Src + 1;
+ VarLength = 0;
+ VariableName.Length = 0;
+ VariableName.Buffer = Src1;
+ while (VarLength < (SrcLength - sizeof(WCHAR))) {
+ if (*Src1 == L'%') {
+ if (VarLength) {
+ VariableName.Length = (USHORT)VarLength;
+ VariableName.MaximumLength = (USHORT)VarLength;
+ }
+ break;
+ }
+ else {
+ Src1++;
+ VarLength += sizeof(WCHAR);
+ }
+ }
+
+ if (VariableName.Length) {
+ VariableValue.Buffer = Dst;
+ VariableValue.Length = 0;
+ VariableValue.MaximumLength = (USHORT)DstLength;
+ Status1 = RtlQueryEnvironmentVariable_U( Environment,
+ &VariableName,
+ &VariableValue
+ );
+ if (NT_SUCCESS( Status1 ) || Status1 == STATUS_BUFFER_TOO_SMALL) {
+ RequiredLength += VariableValue.Length;
+ Src = Src1 + 1;
+ SrcLength -= (VarLength + 2*sizeof(WCHAR));
+ if (NT_SUCCESS( Status1 )) {
+ DstLength -= VariableValue.Length;
+ Dst += VariableValue.Length / sizeof(WCHAR);
+ }
+ else {
+ Status = Status1;
+ }
+
+ continue;
+ }
+ }
+ }
+
+ if (NT_SUCCESS( Status )) {
+ if (DstLength) {
+ DstLength -= sizeof(WCHAR);
+ *Dst++ = *Src;
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+
+ RequiredLength += sizeof(WCHAR);
+ SrcLength -= sizeof(WCHAR);
+ Src++;
+ }
+
+ if (NT_SUCCESS( Status )) {
+ if (DstLength) {
+ DstLength -= sizeof(WCHAR);
+ *Dst = L'\0';
+ }
+ else {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+ }
+ RequiredLength += sizeof(WCHAR);
+
+ if (ARGUMENT_PRESENT( ReturnedLength )) {
+ *ReturnedLength = RequiredLength;
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Destination->Length = (USHORT)(RequiredLength - sizeof(WCHAR));
+ }
+
+ return( Status );
+}
+
+
+ULONG
+RtlGetNtGlobalFlags( VOID )
+{
+ return( NtGlobalFlag );
+}
+
+
+//
+// Maximum size of TOKEN_USER information.
+//
+
+#define SIZE_OF_TOKEN_INFORMATION \
+ sizeof( TOKEN_USER ) \
+ + sizeof( SID ) \
+ + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
+
+
+NTSTATUS
+RtlFormatCurrentUserKeyPath(
+ OUT PUNICODE_STRING CurrentUserKeyPath
+ )
+
+/*++
+
+Routine Description:
+
+ Initialize the supplied buffer with a string representation
+ of the current user's SID.
+
+Arguments:
+
+ CurrentUserKeyPath - Returns a string that represents the current
+ user's root key in the Registry. Caller must call
+ RtlFreeUnicodeString to free the buffer when done with it.
+
+Return Value:
+
+ NTSTATUS - Returns STATUS_SUCCESS if the user string was
+ succesfully initialized.
+
+--*/
+
+{
+ UNICODE_STRING UserString;
+ HANDLE TokenHandle;
+ UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
+ ULONG ReturnLength;
+ NTSTATUS Status;
+
+ Status = ZwOpenThreadToken( NtCurrentThread(),
+ TOKEN_READ,
+ TRUE,
+ &TokenHandle
+ );
+
+ if( !NT_SUCCESS( Status ) && ( Status != STATUS_NO_TOKEN ) ) {
+ return( Status );
+ }
+
+ if( !NT_SUCCESS( Status ) ) {
+
+ Status = ZwOpenProcessToken( NtCurrentProcess(),
+ TOKEN_READ,
+ &TokenHandle
+ );
+ if( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+ }
+
+ Status = ZwQueryInformationToken( TokenHandle,
+ TokenUser,
+ TokenInformation,
+ sizeof( TokenInformation ),
+ &ReturnLength
+ );
+ if( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ ZwClose( TokenHandle );
+ Status = RtlConvertSidToUnicodeString( &UserString,
+ ((PTOKEN_USER)TokenInformation)->User.Sid,
+ TRUE
+ );
+ if( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ CurrentUserKeyPath->Length = 0;
+ CurrentUserKeyPath->MaximumLength = UserString.Length +
+ sizeof( L"\\REGISTRY\\USER\\" ) +
+ sizeof( UNICODE_NULL );
+ CurrentUserKeyPath->Buffer = (RtlAllocateStringRoutine)( CurrentUserKeyPath->MaximumLength );
+ if (CurrentUserKeyPath->Buffer == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ //
+ // Copy "\REGISTRY\USER" to the current user string.
+ //
+
+ RtlAppendUnicodeToString( CurrentUserKeyPath, L"\\REGISTRY\\USER\\" );
+
+ //
+ // Append the user's <SID> to the current user string.
+ //
+
+ Status = RtlAppendUnicodeStringToString( CurrentUserKeyPath,
+ &UserString
+ );
+ RtlFreeUnicodeString( &UserString );
+ return Status;
+}
+
+
+NTSTATUS
+RtlOpenCurrentUser(
+ IN ULONG DesiredAccess,
+ OUT PHANDLE CurrentUserKey
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to open the the HKEY_CURRENT_USER predefined handle.
+
+Arguments:
+
+ DesiredAccess - Specifies the access to open the key for.
+
+ CurrentUserKey - Returns a handle to the key \REGISTRY\USER\*.
+
+Return Value:
+
+ Returns ERROR_SUCCESS (0) for success; error-code for failure.
+
+--*/
+
+{
+ UNICODE_STRING CurrentUserKeyPath;
+ OBJECT_ATTRIBUTES Obja;
+ NTSTATUS Status;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Format the registry path for the current user.
+ //
+
+ Status = RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath );
+ if( !NT_SUCCESS( Status )) {
+ goto trydefault;
+ }
+
+ InitializeObjectAttributes( &Obja,
+ &CurrentUserKeyPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+ Status = ZwOpenKey( CurrentUserKey,
+ DesiredAccess,
+ &Obja
+ );
+ RtlFreeUnicodeString( &CurrentUserKeyPath );
+
+ if( !NT_SUCCESS( Status )) {
+trydefault:
+ //
+ // Opening \REGISTRY\USER\<SID> failed, try \REGISTRY\USER\.DEFAULT
+ //
+ RtlInitUnicodeString( &CurrentUserKeyPath, RtlpRegistryPaths[ RTL_REGISTRY_USER ] );
+ InitializeObjectAttributes( &Obja,
+ &CurrentUserKeyPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ Status = ZwOpenKey( CurrentUserKey,
+ DesiredAccess,
+ &Obja
+ );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+RtlpGetTimeZoneInfoHandle(
+ IN BOOLEAN WriteAccess,
+ OUT PHANDLE Key
+ )
+{
+ return RtlpGetRegistryHandle( RTL_REGISTRY_CONTROL, L"TimeZoneInformation", WriteAccess, Key );
+}
+
+
+
+extern WCHAR szBias[];
+extern WCHAR szStandardName[];
+extern WCHAR szStandardBias[];
+extern WCHAR szStandardStart[];
+extern WCHAR szDaylightName[];
+extern WCHAR szDaylightBias[];
+extern WCHAR szDaylightStart[];
+
+NTSTATUS
+RtlQueryTimeZoneInformation(
+ OUT PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
+ )
+{
+ NTSTATUS Status;
+ HANDLE Key;
+ UNICODE_STRING StandardName, DaylightName;
+ RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 8 ];
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetTimeZoneInfoHandle( FALSE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ RtlZeroMemory( TimeZoneInformation, sizeof( *TimeZoneInformation ) );
+ RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
+
+ RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 0 ].Name = szBias;
+ RegistryConfigurationTable[ 0 ].EntryContext = &TimeZoneInformation->Bias;
+
+
+ StandardName.Buffer = TimeZoneInformation->StandardName;
+ StandardName.Length = 0;
+ StandardName.MaximumLength = sizeof( TimeZoneInformation->StandardName );
+ RegistryConfigurationTable[ 1 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 1 ].Name = szStandardName;
+ RegistryConfigurationTable[ 1 ].EntryContext = &StandardName;
+
+ RegistryConfigurationTable[ 2 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 2 ].Name = szStandardBias;
+ RegistryConfigurationTable[ 2 ].EntryContext = &TimeZoneInformation->StandardBias;
+
+ RegistryConfigurationTable[ 3 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 3 ].Name = szStandardStart;
+ RegistryConfigurationTable[ 3 ].EntryContext = &TimeZoneInformation->StandardStart;
+ *(PLONG)(RegistryConfigurationTable[ 3 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
+
+ DaylightName.Buffer = TimeZoneInformation->DaylightName;
+ DaylightName.Length = 0;
+ DaylightName.MaximumLength = sizeof( TimeZoneInformation->DaylightName );
+ RegistryConfigurationTable[ 4 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 4 ].Name = szDaylightName;
+ RegistryConfigurationTable[ 4 ].EntryContext = &DaylightName;
+
+ RegistryConfigurationTable[ 5 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 5 ].Name = szDaylightBias;
+ RegistryConfigurationTable[ 5 ].EntryContext = &TimeZoneInformation->DaylightBias;
+
+ RegistryConfigurationTable[ 6 ].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ RegistryConfigurationTable[ 6 ].Name = szDaylightStart;
+ RegistryConfigurationTable[ 6 ].EntryContext = &TimeZoneInformation->DaylightStart;
+ *(PLONG)(RegistryConfigurationTable[ 6 ].EntryContext) = -(LONG)sizeof( TIME_FIELDS );
+
+ Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ RegistryConfigurationTable,
+ NULL,
+ NULL
+ );
+ ZwClose( Key );
+ return Status;
+}
+
+
+NTSTATUS
+RtlSetTimeZoneInformation(
+ IN PRTL_TIME_ZONE_INFORMATION TimeZoneInformation
+ )
+{
+ NTSTATUS Status;
+ HANDLE Key;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szBias,
+ REG_DWORD,
+ &TimeZoneInformation->Bias,
+ sizeof( TimeZoneInformation->Bias )
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szStandardName,
+ REG_SZ,
+ TimeZoneInformation->StandardName,
+ (wcslen( TimeZoneInformation->StandardName ) + 1) * sizeof( WCHAR )
+ );
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szStandardBias,
+ REG_DWORD,
+ &TimeZoneInformation->StandardBias,
+ sizeof( TimeZoneInformation->StandardBias )
+ );
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szStandardStart,
+ REG_BINARY,
+ &TimeZoneInformation->StandardStart,
+ sizeof( TimeZoneInformation->StandardStart )
+ );
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szDaylightName,
+ REG_SZ,
+ TimeZoneInformation->DaylightName,
+ (wcslen( TimeZoneInformation->DaylightName ) + 1) * sizeof( WCHAR )
+ );
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szDaylightBias,
+ REG_DWORD,
+ &TimeZoneInformation->DaylightBias,
+ sizeof( TimeZoneInformation->DaylightBias )
+ );
+ }
+
+ if (NT_SUCCESS( Status )) {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ szDaylightStart,
+ REG_BINARY,
+ &TimeZoneInformation->DaylightStart,
+ sizeof( TimeZoneInformation->DaylightStart )
+ );
+ }
+
+ ZwClose( Key );
+ return Status;
+}
+
+
+NTSTATUS
+RtlSetActiveTimeBias(
+ IN LONG ActiveBias
+ )
+{
+ NTSTATUS Status;
+ HANDLE Key;
+ RTL_QUERY_REGISTRY_TABLE RegistryConfigurationTable[ 2 ];
+ LONG CurrentActiveBias;
+
+ RTL_PAGED_CODE();
+
+ Status = RtlpGetTimeZoneInfoHandle( TRUE, &Key );
+ if (!NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ RtlZeroMemory( RegistryConfigurationTable, sizeof( RegistryConfigurationTable ) );
+ RegistryConfigurationTable[ 0 ].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
+ RegistryConfigurationTable[ 0 ].Name = L"ActiveTimeBias";
+ RegistryConfigurationTable[ 0 ].EntryContext = &CurrentActiveBias;
+
+ Status = RtlQueryRegistryValues( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ RegistryConfigurationTable,
+ NULL,
+ NULL
+ );
+ if ( NT_SUCCESS(Status) && CurrentActiveBias == ActiveBias ) {
+ ;
+ }
+ else {
+ Status = RtlWriteRegistryValue( RTL_REGISTRY_HANDLE,
+ (PWSTR)Key,
+ L"ActiveTimeBias",
+ REG_DWORD,
+ &ActiveBias,
+ sizeof( ActiveBias )
+ );
+ }
+ ZwClose( Key );
+ return Status;
+}
diff --git a/private/ntos/rtl/rtlassig.c b/private/ntos/rtl/rtlassig.c
new file mode 100644
index 000000000..781d90f53
--- /dev/null
+++ b/private/ntos/rtl/rtlassig.c
@@ -0,0 +1,498 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rtlassig.c
+
+Abstract:
+
+ This Module implements many security rtl routines defined in ntseapi.h
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+ Robert Reichel (RobertRe) 1-Mar-1991
+
+Environment:
+
+ Pure Runtime Library Routine
+
+Revision History:
+
+--*/
+
+
+#include "ntrtlp.h"
+#include "seopaque.h"
+#include "sertlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlSelfRelativeToAbsoluteSD)
+#pragma alloc_text(PAGE,RtlMakeSelfRelativeSD)
+#pragma alloc_text(PAGE,RtlpQuerySecurityDescriptor)
+#pragma alloc_text(PAGE,RtlAbsoluteToSelfRelativeSD)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Exported Procedures //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+NTSTATUS
+RtlSelfRelativeToAbsoluteSD(
+ IN OUT PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor,
+ OUT PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor,
+ IN OUT PULONG AbsoluteSecurityDescriptorSize,
+ IN OUT PACL Dacl,
+ IN OUT PULONG DaclSize,
+ IN OUT PACL Sacl,
+ IN OUT PULONG SaclSize,
+ IN OUT PSID Owner,
+ IN OUT PULONG OwnerSize,
+ IN OUT PSID PrimaryGroup,
+ IN OUT PULONG PrimaryGroupSize
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a security descriptor from self-relative format to absolute
+ format
+
+Arguments:
+
+ SecurityDescriptor - Supplies a pointer to a security descriptor in
+ Self-Relative format
+
+ AbsoluteSecurityDescriptor - A pointer to a buffer in which will be
+ placed the main body of the Absolute format security descriptor.
+
+ Dacl - Supplies a pointer to a buffer that will contain the Dacl of the
+ output descriptor. This pointer will be referenced by, not copied
+ into, the output descriptor.
+
+ DaclSize - Supplies the size of the buffer pointed to by Dacl. In case
+ of error, it will return the minimum size necessary to contain the
+ Dacl.
+
+ Sacl - Supplies a pointer to a buffer that will contain the Sacl of the
+ output descriptor. This pointer will be referenced by, not copied
+ into, the output descriptor.
+
+ SaclSize - Supplies the size of the buffer pointed to by Sacl. In case
+ of error, it will return the minimum size necessary to contain the
+ Sacl.
+
+ Owner - Supplies a pointer to a buffer that will contain the Owner of
+ the output descriptor. This pointer will be referenced by, not
+ copied into, the output descriptor.
+
+ OwnerSize - Supplies the size of the buffer pointed to by Owner. In
+ case of error, it will return the minimum size necessary to contain
+ the Owner.
+
+ PrimaryGroup - Supplies a pointer to a buffer that will contain the
+ PrimaryGroup of the output descriptor. This pointer will be
+ referenced by, not copied into, the output descriptor.
+
+ PrimaryGroupSize - Supplies the size of the buffer pointed to by
+ PrimaryGroup. In case of error, it will return the minimum size
+ necessary to contain the PrimaryGroup.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Success
+
+ STATUS_BUFFER_TOO_SMALL - One of the buffers passed was too small.
+
+ STATUS_INVALID_OWNER - There was not a valid owner in the passed
+ security descriptor.
+
+--*/
+
+{
+ ULONG NewDaclSize;
+ ULONG NewSaclSize;
+ ULONG NewBodySize;
+ ULONG NewOwnerSize;
+ ULONG NewGroupSize;
+
+ PSID NewOwner;
+ PSID NewGroup;
+ PACL NewDacl;
+ PACL NewSacl;
+
+ //
+ // typecast security descriptors so we don't have to cast all over the place.
+ //
+
+ PISECURITY_DESCRIPTOR OutSD =
+ AbsoluteSecurityDescriptor;
+
+ PISECURITY_DESCRIPTOR InSD =
+ (PISECURITY_DESCRIPTOR)SelfRelativeSecurityDescriptor;
+
+
+ RTL_PAGED_CODE();
+
+ if ( !RtlpAreControlBitsSet( InSD, SE_SELF_RELATIVE) ) {
+ return( STATUS_BAD_DESCRIPTOR_FORMAT );
+ }
+
+ NewBodySize = sizeof(SECURITY_DESCRIPTOR);
+
+ RtlpQuerySecurityDescriptor(
+ InSD,
+ &NewOwner,
+ &NewOwnerSize,
+ &NewGroup,
+ &NewGroupSize,
+ &NewDacl,
+ &NewDaclSize,
+ &NewSacl,
+ &NewSaclSize
+ );
+
+ if ( (NewBodySize > *AbsoluteSecurityDescriptorSize) ||
+ (NewOwnerSize > *OwnerSize ) ||
+ (NewDaclSize > *DaclSize ) ||
+ (NewSaclSize > *SaclSize ) ||
+ (NewGroupSize > *PrimaryGroupSize ) ) {
+
+ *AbsoluteSecurityDescriptorSize = sizeof(SECURITY_DESCRIPTOR);
+ *PrimaryGroupSize = NewGroupSize;
+ *OwnerSize = NewOwnerSize;
+ *SaclSize = NewSaclSize;
+ *DaclSize = NewDaclSize;
+
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+
+ RtlMoveMemory( OutSD,
+ InSD,
+ sizeof(SECURITY_DESCRIPTOR) );
+
+ OutSD->Owner = NULL;
+ OutSD->Group = NULL;
+ OutSD->Sacl = NULL;
+ OutSD->Dacl = NULL;
+
+ RtlpClearControlBits( OutSD, SE_SELF_RELATIVE );
+
+ if (NewOwner != NULL) {
+ RtlMoveMemory( Owner, NewOwner, RtlLengthSid( NewOwner ));
+ OutSD->Owner = Owner;
+ }
+
+ if (NewGroup != NULL) {
+ RtlMoveMemory( PrimaryGroup, NewGroup, RtlLengthSid( NewGroup ));
+ OutSD->Group = PrimaryGroup;
+ }
+
+ if (NewSacl != NULL) {
+ RtlMoveMemory( Sacl, NewSacl, NewSacl->AclSize );
+ OutSD->Sacl = Sacl;
+ }
+
+ if (NewDacl != NULL) {
+ RtlMoveMemory( Dacl, NewDacl, NewDacl->AclSize );
+ OutSD->Dacl = Dacl;
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlMakeSelfRelativeSD(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN OUT PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor,
+ IN OUT PULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ Makes a copy of a security descriptor. The produced copy will be in self-relative
+ form.
+
+ The security descriptor to be copied may be in either absolute or self-relative
+ form.
+
+Arguments:
+
+ SecurityDescriptor - Pointer to a security descriptor. This descriptor will not
+ be modified.
+
+ SelfRelativeSecurityDescriptor - Pointer to a buffer that will contain
+ the returned self-relative security descriptor.
+
+ BufferLength - Supplies the length of the buffer. If the supplied
+ buffer is not large enough to hold the self-relative security
+ descriptor, an error will be returned, and this field will return
+ the minimum size required.
+
+
+Return Value:
+
+ STATUS_BUFFER_TOO_SMALL - The supplied buffer was too small to contain
+ the resultant security descriptor.
+
+
+--*/
+
+{
+ ULONG NewDaclSize;
+ ULONG NewSaclSize;
+ ULONG NewOwnerSize;
+ ULONG NewGroupSize;
+
+ ULONG AllocationSize;
+
+ PSID NewOwner;
+ PSID NewGroup;
+ PACL NewDacl;
+ PACL NewSacl;
+
+ PCHAR Field;
+ PCHAR Base;
+
+
+ //
+ // Convert security descriptors to new data type so we don't
+ // have to cast all over the place.
+ //
+
+ PISECURITY_DESCRIPTOR IResultantDescriptor =
+ (PISECURITY_DESCRIPTOR)SelfRelativeSecurityDescriptor;
+
+ PISECURITY_DESCRIPTOR IPassedSecurityDescriptor =
+ (PISECURITY_DESCRIPTOR)SecurityDescriptor;
+
+
+ RtlpQuerySecurityDescriptor(
+ IPassedSecurityDescriptor,
+ &NewOwner,
+ &NewOwnerSize,
+ &NewGroup,
+ &NewGroupSize,
+ &NewDacl,
+ &NewDaclSize,
+ &NewSacl,
+ &NewSaclSize
+ );
+
+ RTL_PAGED_CODE();
+
+ AllocationSize = sizeof(SECURITY_DESCRIPTOR) +
+ NewOwnerSize +
+ NewGroupSize +
+ NewDaclSize +
+ NewSaclSize ;
+
+ if (AllocationSize > *BufferLength) {
+ *BufferLength = AllocationSize;
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ RtlZeroMemory( IResultantDescriptor, AllocationSize );
+
+ RtlMoveMemory( IResultantDescriptor,
+ IPassedSecurityDescriptor,
+ sizeof( SECURITY_DESCRIPTOR ));
+
+
+ Base = (PCHAR)(IResultantDescriptor);
+ Field = Base + (ULONG)sizeof(SECURITY_DESCRIPTOR);
+
+ if (NewSaclSize > 0) {
+ RtlMoveMemory( Field, NewSacl, NewSaclSize );
+ IResultantDescriptor->Sacl = (PACL)RtlPointerToOffset(Base,Field);
+ Field += NewSaclSize;
+ }
+
+
+ if (NewDaclSize > 0) {
+ RtlMoveMemory( Field, NewDacl, NewDaclSize );
+ IResultantDescriptor->Dacl = (PACL)RtlPointerToOffset(Base,Field);
+ Field += NewDaclSize;
+ }
+
+
+
+ if (NewOwnerSize > 0) {
+ RtlMoveMemory( Field, NewOwner, NewOwnerSize );
+ IResultantDescriptor->Owner = (PSID)RtlPointerToOffset(Base,Field);
+ Field += NewOwnerSize;
+ }
+
+
+ if (NewGroupSize > 0) {
+ RtlMoveMemory( Field, NewGroup, NewGroupSize );
+ IResultantDescriptor->Group = (PSID)RtlPointerToOffset(Base,Field);
+ }
+
+ RtlpSetControlBits( IResultantDescriptor, SE_SELF_RELATIVE );
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+NTSTATUS
+RtlAbsoluteToSelfRelativeSD(
+ IN PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor,
+ IN OUT PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor,
+ IN OUT PULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ Converts a security descriptor in absolute form to one in self-relative
+ form.
+
+Arguments:
+
+ AbsoluteSecurityDescriptor - Pointer to an absolute format security
+ descriptor. This descriptor will not be modified.
+
+ SelfRelativeSecurityDescriptor - Pointer to a buffer that will contain
+ the returned self-relative security descriptor.
+
+ BufferLength - Supplies the length of the buffer. If the supplied
+ buffer is not large enough to hold the self-relative security
+ descriptor, an error will be returned, and this field will return
+ the minimum size required.
+
+
+Return Value:
+
+ STATUS_BUFFER_TOO_SMALL - The supplied buffer was too small to contain
+ the resultant security descriptor.
+
+ STATUS_BAD_DESCRIPTOR_FORMAT - The supplied security descriptor was not
+ in absolute form.
+
+--*/
+
+{
+ NTSTATUS NtStatus;
+
+ PISECURITY_DESCRIPTOR IAbsoluteSecurityDescriptor =
+ (PISECURITY_DESCRIPTOR)AbsoluteSecurityDescriptor;
+
+
+ RTL_PAGED_CODE();
+
+ //
+ // Make sure the passed SD is absolute format, and then call
+ // RtlMakeSelfRelativeSD() to do all the work.
+ //
+
+ if ( RtlpAreControlBitsSet( IAbsoluteSecurityDescriptor, SE_SELF_RELATIVE) ) {
+ return( STATUS_BAD_DESCRIPTOR_FORMAT );
+ }
+
+ NtStatus = RtlMakeSelfRelativeSD(
+ AbsoluteSecurityDescriptor,
+ SelfRelativeSecurityDescriptor,
+ BufferLength
+ );
+
+ return( NtStatus );
+
+}
+VOID
+RtlpQuerySecurityDescriptor(
+ IN PISECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PSID *Owner,
+ OUT PULONG OwnerSize,
+ OUT PSID *PrimaryGroup,
+ OUT PULONG PrimaryGroupSize,
+ OUT PACL *Dacl,
+ OUT PULONG DaclSize,
+ OUT PACL *Sacl,
+ OUT PULONG SaclSize
+ )
+/*++
+
+Routine Description:
+
+ Returns the pieces of a security descriptor structure.
+
+Arguments:
+
+
+ SecurityDescriptor - Provides the security descriptor of interest.
+
+ Owner - Returns a pointer to the owner information contained in the
+ security descriptor.
+
+ OwnerSize - Returns the size of the owner information.
+
+ PrimaryGroup - Returns a pointer to the primary group information.
+
+ PrimaryGroupSize - Returns the size of the primary group information.
+
+ Dacl - Returns a pointer to the Dacl.
+
+ DaclSize - Returns the size of the Dacl.
+
+ Sacl - Returns a pointer to the Sacl.
+
+ SaclSize - Returns the size of the Sacl.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ RTL_PAGED_CODE();
+
+ *Owner = RtlpOwnerAddrSecurityDescriptor( SecurityDescriptor );
+
+ if (*Owner != NULL) {
+ *OwnerSize = (ULONG)LongAlign(RtlLengthSid(*Owner));
+ } else {
+ *OwnerSize = 0;
+ }
+
+ *Dacl = RtlpDaclAddrSecurityDescriptor ( SecurityDescriptor );
+
+ if (*Dacl !=NULL) {
+ *DaclSize = (ULONG)LongAlign((*Dacl)->AclSize);
+ } else {
+ *DaclSize = 0;
+ }
+
+ *PrimaryGroup = RtlpGroupAddrSecurityDescriptor( SecurityDescriptor );
+
+ if (*PrimaryGroup != NULL) {
+ *PrimaryGroupSize = (ULONG)LongAlign(RtlLengthSid(*PrimaryGroup));
+ } else {
+ *PrimaryGroupSize = 0;
+ }
+
+ *Sacl = RtlpSaclAddrSecurityDescriptor( SecurityDescriptor );
+
+ if (*Sacl != NULL) {
+ *SaclSize = (ULONG)LongAlign((*Sacl)->AclSize);
+ } else {
+ *SaclSize = 0;
+ }
+
+}
diff --git a/private/ntos/rtl/rtldata.c b/private/ntos/rtl/rtldata.c
new file mode 100644
index 000000000..8fc3e40f3
--- /dev/null
+++ b/private/ntos/rtl/rtldata.c
@@ -0,0 +1,68 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Rtldata.c
+
+Abstract:
+
+ This module contains static data used by RLT routines.
+
+Author:
+
+ Bryan Willman (bryanwi) 8-Nov-93
+
+Environment:
+
+
+Revision History:
+
+--*/
+
+#include <ntrtlp.h>
+
+#if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma data_seg("PAGE")
+#endif
+
+ULONG V[128] = {
+ 0x4c8bc0aa, 0x4c022957, 0x2232827a, 0x2f1e7626, 0x7f8bdafb, 0x5c37d02a, 0x0ab48f72, 0x2f0c4ffa,
+ 0x290e1954, 0x6b635f23, 0x5d3885c0, 0x74b49ff8, 0x5155fa54, 0x6214ad3f, 0x111e9c29, 0x242a3a09,
+ 0x75932ae1, 0x40ac432e, 0x54f7ba7a, 0x585ccbd5, 0x6df5c727, 0x0374dad1, 0x7112b3f1, 0x735fc311,
+ 0x404331a9, 0x74d97781, 0x64495118, 0x323e04be, 0x5974b425, 0x4862e393, 0x62389c1d, 0x28a68b82,
+ 0x0f95da37, 0x7a50bbc6, 0x09b0091c, 0x22cdb7b4, 0x4faaed26, 0x66417ccd, 0x189e4bfa, 0x1ce4e8dd,
+ 0x5274c742, 0x3bdcf4dc, 0x2d94e907, 0x32eac016, 0x26d33ca3, 0x60415a8a, 0x31f57880, 0x68c8aa52,
+ 0x23eb16da, 0x6204f4a1, 0x373927c1, 0x0d24eb7c, 0x06dd7379, 0x2b3be507, 0x0f9c55b1, 0x2c7925eb,
+ 0x36d67c9a, 0x42f831d9, 0x5e3961cb, 0x65d637a8, 0x24bb3820, 0x4d08e33d, 0x2188754f, 0x147e409e,
+ 0x6a9620a0, 0x62e26657, 0x7bd8ce81, 0x11da0abb, 0x5f9e7b50, 0x23e444b6, 0x25920c78, 0x5fc894f0,
+ 0x5e338cbb, 0x404237fd, 0x1d60f80f, 0x320a1743, 0x76013d2b, 0x070294ee, 0x695e243b, 0x56b177fd,
+ 0x752492e1, 0x6decd52f, 0x125f5219, 0x139d2e78, 0x1898d11e, 0x2f7ee785, 0x4db405d8, 0x1a028a35,
+ 0x63f6f323, 0x1f6d0078, 0x307cfd67, 0x3f32a78a, 0x6980796c, 0x462b3d83, 0x34b639f2, 0x53fce379,
+ 0x74ba50f4, 0x1abc2c4b, 0x5eeaeb8d, 0x335a7a0d, 0x3973dd20, 0x0462d66b, 0x159813ff, 0x1e4643fd,
+ 0x06bc5c62, 0x3115e3fc, 0x09101613, 0x47af2515, 0x4f11ec54, 0x78b99911, 0x3db8dd44, 0x1ec10b9b,
+ 0x5b5506ca, 0x773ce092, 0x567be81a, 0x5475b975, 0x7a2cde1a, 0x494536f5, 0x34737bb4, 0x76d9750b,
+ 0x2a1f6232, 0x2e49644d, 0x7dddcbe7, 0x500cebdb, 0x619dab9e, 0x48c626fe, 0x1cda3193, 0x52dabe9d};
+
+PWSTR RtlpRegistryPaths[ RTL_REGISTRY_MAXIMUM ] = {
+ NULL,
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services",
+ L"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
+ L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
+ L"\\Registry\\Machine\\Hardware\\DeviceMap",
+ L"\\Registry\\User\\.Default"
+};
+
+WCHAR szBias[] = L"Bias";
+WCHAR szStandardName[] = L"StandardName";
+WCHAR szStandardBias[] = L"StandardBias";
+WCHAR szStandardStart[] = L"StandardStart";
+WCHAR szDaylightName[] = L"DaylightName";
+WCHAR szDaylightBias[] = L"DaylightBias";
+WCHAR szDaylightStart[] = L"DaylightStart";
+
+#if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma data_seg()
+#endif
+
diff --git a/private/ntos/rtl/rtlexec.c b/private/ntos/rtl/rtlexec.c
new file mode 100644
index 000000000..84719b090
--- /dev/null
+++ b/private/ntos/rtl/rtlexec.c
@@ -0,0 +1,1378 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rtlexec.c
+
+Abstract:
+
+ User Mode routines for creating user mode processes and threads.
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Environment:
+
+ User Mode or Kernel Mode
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include <nturtl.h>
+#include <string.h>
+#include "init.h"
+#include "ntos.h"
+#define ROUND_UP( x, y ) ((ULONG)(x) + ((y)-1) & ~((y)-1))
+
+extern ULONG NtGlobalFlag;
+
+
+VOID
+RtlpCopyProcString(
+ IN OUT PWSTR *pDst,
+ OUT PUNICODE_STRING DestString,
+ IN PUNICODE_STRING SourceString,
+ IN ULONG DstAlloc OPTIONAL
+ );
+
+NTSTATUS
+RtlpOpenImageFile(
+ IN PUNICODE_STRING ImagePathName,
+ IN ULONG Attributes,
+ OUT PHANDLE FileHandle,
+ IN BOOLEAN ReportErrors
+ );
+
+NTSTATUS
+RtlpFreeStack(
+ IN HANDLE Process,
+ IN PINITIAL_TEB InitialTeb
+ );
+
+NTSTATUS
+RtlpCreateStack(
+ IN HANDLE Process,
+ IN ULONG MaximumStackSize OPTIONAL,
+ IN ULONG CommittedStackSize OPTIONAL,
+ IN ULONG ZeroBits OPTIONAL,
+ OUT PINITIAL_TEB InitialTeb
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(INIT,RtlpCopyProcString )
+#pragma alloc_text(INIT,RtlCreateProcessParameters )
+#pragma alloc_text(INIT,RtlDestroyProcessParameters )
+#pragma alloc_text(INIT,RtlNormalizeProcessParams )
+#pragma alloc_text(INIT,RtlDeNormalizeProcessParams )
+#pragma alloc_text(INIT,RtlpOpenImageFile )
+#pragma alloc_text(INIT,RtlpCreateStack )
+#pragma alloc_text(INIT,RtlpFreeStack )
+#pragma alloc_text(INIT,RtlCreateUserProcess )
+#pragma alloc_text(INIT,RtlCreateUserThread )
+#endif
+
+
+VOID
+RtlpCopyProcString(
+ IN OUT PWSTR *pDst,
+ OUT PUNICODE_STRING DestString,
+ IN PUNICODE_STRING SourceString,
+ IN ULONG DstAlloc OPTIONAL
+ )
+{
+ if (!ARGUMENT_PRESENT( DstAlloc )) {
+ DstAlloc = SourceString->MaximumLength;
+ }
+
+ if (SourceString->Buffer != NULL && SourceString->Length != 0) {
+ RtlMoveMemory( *pDst,
+ SourceString->Buffer,
+ SourceString->Length
+ );
+ }
+
+ DestString->Buffer = *pDst;
+ DestString->Length = SourceString->Length;
+ DestString->MaximumLength = (USHORT)DstAlloc;
+
+ *pDst = (PWSTR)((PCHAR)(*pDst) + ROUND_UP( DstAlloc, sizeof( ULONG ) ) );
+ return;
+}
+
+NTSTATUS
+RtlCreateProcessParameters(
+ OUT PRTL_USER_PROCESS_PARAMETERS *ProcessParameters,
+ IN PUNICODE_STRING ImagePathName,
+ IN PUNICODE_STRING DllPath OPTIONAL,
+ IN PUNICODE_STRING CurrentDirectory OPTIONAL,
+ IN PUNICODE_STRING CommandLine OPTIONAL,
+ IN PVOID Environment OPTIONAL,
+ IN PUNICODE_STRING WindowTitle OPTIONAL,
+ IN PUNICODE_STRING DesktopInfo OPTIONAL,
+ IN PUNICODE_STRING ShellInfo OPTIONAL,
+ IN PUNICODE_STRING RuntimeData OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function formats NT style RTL_USER_PROCESS_PARAMETERS
+ record. The record is self-contained in a single block of memory
+ allocated by this function. The allocation method is opaque and
+ thus the record must be freed by calling the
+ RtlDestroyProcessParameters function.
+
+ The process parameters record is created in a de-normalized form,
+ thus making it suitable for passing to the RtlCreateUserProcess
+ function. It is expected that the caller will fill in additional
+ fields in the process parameters record after this function returns,
+ but prior to calling RtlCreateUserProcess.
+
+Arguments:
+
+ ProcessParameters - Pointer to a variable that will receive the address
+ of the process parameter structure created by this routinue. The
+ memory for the structure is allocated in an opaque manner and must
+ be freed by calling RtlDestroyProcessParameters.
+
+ ImagePathName - Required parameter that is the fully qualified NT
+ path name of the image file that will be used to create the process
+ that will received these parameters.
+
+ DllPath - An optional parameter that is an NT String variable pointing
+ to the search path the NT Loader is to use in the target process
+ when searching for Dll modules. If not specified, then the Dll
+ search path is filled in from the current process's Dll search
+ path.
+
+ CurrentDirectory - An optional parameter that is an NT String variable
+ pointing to the default directory string for the target process.
+ If not specified, then the current directory string is filled in
+ from the current process's current directory string.
+
+ CommandLine - An optional parameter that is an NT String variable that
+ will be passed to the target process as its command line. If not
+ specified, then the command line passed to the target process will
+ be a null string.
+
+ Environment - An optional parameter that is an opaque pointer to an
+ environment variable block of the type created by
+ RtlCreateEnvironment routine. If not specified, then the target
+ process will receive a copy of the calling process's environment
+ variable block.
+
+ WindowTitle - An optional parameter that is an NT String variable that
+ points to the title string the target process is to use for its
+ main window. If not specified, then a null string will be passed
+ to the target process as its default window title.
+
+ DesktopInfo - An optional parameter that is an NT String variable that
+ contains uninterpreted data that is passed as is to the target
+ process. If not specified, the target process will receive a
+ pointer to an empty string.
+
+ ShellInfo - An optional parameter that is an NT String variable that
+ contains uninterpreted data that is passed as is to the target
+ process. If not specified, the target process will receive a
+ pointer to an empty string.
+
+ RuntimeData - An optional parameter that is an NT String variable that
+ contains uninterpreted data that is passed as is to the target
+ process. If not specified, the target process will receive a
+ pointer to an empty string.
+
+Return Value:
+
+ STATUS_SUCCESS - The process parameters is De-Normalized and
+ contains entries for each of the specified argument and variable
+ strings.
+
+ STATUS_BUFFER_TOO_SMALL - The specified process parameters buffer is
+ too small to contain the argument and environment strings. The value
+ of ProcessParameters->Length is modified to contain the buffer
+ size needed to contain the argument and variable strings.
+
+--*/
+
+{
+ PRTL_USER_PROCESS_PARAMETERS p;
+ NTSTATUS Status;
+ UNICODE_STRING NullString = {0, 1, L""};
+ ULONG ByteCount, MaxByteCount;
+ PWSTR pDst;
+ PPEB Peb = NtCurrentPeb();
+ HANDLE CurDirHandle;
+
+ //
+ // Acquire the Peb Lock for the duration while we copy information out
+ // of it.
+ //
+
+ RtlAcquirePebLock();
+ Status = STATUS_SUCCESS;
+ p = NULL;
+ CurDirHandle = NULL;
+ try {
+ //
+ // For optional pointer parameters, default them to point to their
+ // corresponding field in the current process's process parameter
+ // structure or to a null string.
+ //
+
+ if (!ARGUMENT_PRESENT( DllPath )) {
+ DllPath = &Peb->ProcessParameters->DllPath;
+ }
+
+ if (!ARGUMENT_PRESENT( CurrentDirectory )) {
+
+ if ( Peb->ProcessParameters->CurrentDirectory.Handle ) {
+ CurDirHandle = (HANDLE)((ULONG)Peb->ProcessParameters->CurrentDirectory.Handle & ~OBJ_HANDLE_TAGBITS);
+ CurDirHandle = (HANDLE)((ULONG)CurDirHandle | RTL_USER_PROC_CURDIR_INHERIT);
+ }
+ CurrentDirectory = &Peb->ProcessParameters->CurrentDirectory.DosPath;
+ }
+ else {
+ if ( Peb->ProcessParameters->CurrentDirectory.Handle ) {
+ CurDirHandle = (HANDLE)((ULONG)Peb->ProcessParameters->CurrentDirectory.Handle & ~OBJ_HANDLE_TAGBITS);
+ CurDirHandle = (HANDLE)((ULONG)CurDirHandle | RTL_USER_PROC_CURDIR_CLOSE);
+ }
+ }
+
+ if (!ARGUMENT_PRESENT( CommandLine )) {
+ CommandLine = ImagePathName;
+ }
+
+ if (!ARGUMENT_PRESENT( Environment )) {
+ Environment = Peb->ProcessParameters->Environment;
+ }
+
+ if (!ARGUMENT_PRESENT( WindowTitle )) {
+ WindowTitle = &NullString;
+ }
+
+ if (!ARGUMENT_PRESENT( DesktopInfo )) {
+ DesktopInfo = &NullString;
+ }
+
+ if (!ARGUMENT_PRESENT( ShellInfo )) {
+ ShellInfo = &NullString;
+ }
+
+ if (!ARGUMENT_PRESENT( RuntimeData )) {
+ RuntimeData = &NullString;
+ }
+
+ //
+ // Determine size need to contain the process parameter record
+ // structure and all of the strings it will point to. Each string
+ // will be aligned on a ULONG byte boundary.
+ //
+
+ ByteCount = sizeof( **ProcessParameters );
+ ByteCount += ROUND_UP( ImagePathName->Length + sizeof(UNICODE_NULL), sizeof( ULONG ) );
+ ByteCount += ROUND_UP( DllPath->MaximumLength, sizeof( ULONG ) );
+ ByteCount += ROUND_UP( DOS_MAX_PATH_LENGTH*2, sizeof( ULONG ) );
+ ByteCount += ROUND_UP( CommandLine->Length + sizeof(UNICODE_NULL), sizeof( ULONG ) );
+ ByteCount += ROUND_UP( WindowTitle->MaximumLength, sizeof( ULONG ) );
+ ByteCount += ROUND_UP( DesktopInfo->MaximumLength, sizeof( ULONG ) );
+ ByteCount += ROUND_UP( ShellInfo->MaximumLength, sizeof( ULONG ) );
+ ByteCount += ROUND_UP( RuntimeData->MaximumLength, sizeof( ULONG ) );
+
+ //
+ // Allocate memory for the process parameter record.
+ //
+
+ MaxByteCount = ByteCount;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&p,
+ 0,
+ &MaxByteCount,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ p->MaximumLength = MaxByteCount;
+ p->Length = ByteCount;
+ p->Flags = RTL_USER_PROC_PARAMS_NORMALIZED;
+ p->Environment = Environment;
+ p->CurrentDirectory.Handle = CurDirHandle;
+
+ //
+ // Inherits ^C inhibit information
+ //
+
+ p->ConsoleFlags = Peb->ProcessParameters->ConsoleFlags;
+
+ pDst = (PWSTR)(p + 1);
+ RtlpCopyProcString( &pDst,
+ &p->CurrentDirectory.DosPath,
+ CurrentDirectory,
+ DOS_MAX_PATH_LENGTH*2
+ );
+
+ RtlpCopyProcString( &pDst, &p->DllPath, DllPath, 0 );
+ RtlpCopyProcString( &pDst, &p->ImagePathName, ImagePathName, ImagePathName->Length + sizeof(UNICODE_NULL) );
+ if (CommandLine->Length == CommandLine->MaximumLength) {
+ RtlpCopyProcString( &pDst, &p->CommandLine, CommandLine, 0 );
+ }
+ else {
+ RtlpCopyProcString( &pDst, &p->CommandLine, CommandLine, CommandLine->Length + sizeof(UNICODE_NULL) );
+ }
+ RtlpCopyProcString( &pDst, &p->WindowTitle, WindowTitle, 0 );
+ RtlpCopyProcString( &pDst, &p->DesktopInfo, DesktopInfo, 0 );
+ RtlpCopyProcString( &pDst, &p->ShellInfo, ShellInfo, 0 );
+ if (RuntimeData->Length != 0) {
+ RtlpCopyProcString( &pDst, &p->RuntimeData, RuntimeData, 0 );
+ }
+ else {
+ p->RuntimeData.Buffer = NULL;
+ p->RuntimeData.Length = 0;
+ p->RuntimeData.MaximumLength = 0;
+ }
+ *ProcessParameters = RtlDeNormalizeProcessParams( p );
+ p = NULL;
+ }
+ finally {
+ if (AbnormalTermination()) {
+ Status = STATUS_ACCESS_VIOLATION;
+ }
+
+ if (p != NULL) {
+ RtlDestroyProcessParameters( p );
+ }
+
+ RtlReleasePebLock();
+ }
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+RtlDestroyProcessParameters(
+ IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters
+ )
+{
+ NTSTATUS Status;
+ ULONG RegionSize;
+
+ RegionSize = 0;
+ Status = ZwFreeVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&ProcessParameters,
+ &RegionSize,
+ MEM_RELEASE
+ );
+
+ return( Status );
+}
+
+
+#define RtlpNormalizeProcessParam( Base, p ) \
+ if ((p) != NULL) { \
+ (p) = (PWSTR)((PCHAR)(p) + (ULONG)(Base)); \
+ } \
+
+#define RtlpDeNormalizeProcessParam( Base, p ) \
+ if ((p) != NULL) { \
+ (p) = (PWSTR)((PCHAR)(p) - (ULONG)(Base)); \
+ } \
+
+
+PRTL_USER_PROCESS_PARAMETERS
+RtlNormalizeProcessParams(
+ IN OUT PRTL_USER_PROCESS_PARAMETERS ProcessParameters
+ )
+{
+ if (!ARGUMENT_PRESENT( ProcessParameters )) {
+ return( NULL );
+ }
+
+ if (ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED) {
+ return( ProcessParameters );
+ }
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->CurrentDirectory.DosPath.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->DllPath.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->ImagePathName.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->CommandLine.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->WindowTitle.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->DesktopInfo.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->ShellInfo.Buffer
+ );
+
+ RtlpNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->RuntimeData.Buffer
+ );
+ ProcessParameters->Flags |= RTL_USER_PROC_PARAMS_NORMALIZED;
+
+ return( ProcessParameters );
+}
+
+PRTL_USER_PROCESS_PARAMETERS
+RtlDeNormalizeProcessParams(
+ IN OUT PRTL_USER_PROCESS_PARAMETERS ProcessParameters
+ )
+{
+ if (!ARGUMENT_PRESENT( ProcessParameters )) {
+ return( NULL );
+ }
+
+ if (!(ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
+ return( ProcessParameters );
+ }
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->CurrentDirectory.DosPath.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->DllPath.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->ImagePathName.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->CommandLine.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->WindowTitle.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->DesktopInfo.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->ShellInfo.Buffer
+ );
+
+ RtlpDeNormalizeProcessParam( ProcessParameters,
+ ProcessParameters->RuntimeData.Buffer
+ );
+
+ ProcessParameters->Flags &= ~RTL_USER_PROC_PARAMS_NORMALIZED;
+ return( ProcessParameters );
+}
+
+NTSTATUS
+RtlpOpenImageFile(
+ IN PUNICODE_STRING ImagePathName,
+ IN ULONG Attributes,
+ OUT PHANDLE FileHandle,
+ IN BOOLEAN ReportErrors
+ )
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ HANDLE File;
+ IO_STATUS_BLOCK IoStatus;
+
+ *FileHandle = NULL;
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ ImagePathName,
+ Attributes,
+ NULL,
+ NULL
+ );
+ Status = ZwOpenFile( &File,
+ SYNCHRONIZE | FILE_EXECUTE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ | FILE_SHARE_DELETE,
+ FILE_NON_DIRECTORY_FILE
+ );
+
+ if (!NT_SUCCESS( Status )) {
+#if DBG
+ if (ReportErrors) {
+ DbgPrint( "NTRTL: RtlpOpenImageFile - NtCreateFile( %wZ ) failed. Status == %X\n",
+ ImagePathName,
+ Status
+ );
+ }
+#endif // DBG
+ return( Status );
+ }
+
+ *FileHandle = File;
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlpCreateStack(
+ IN HANDLE Process,
+ IN ULONG MaximumStackSize OPTIONAL,
+ IN ULONG CommittedStackSize OPTIONAL,
+ IN ULONG ZeroBits OPTIONAL,
+ OUT PINITIAL_TEB InitialTeb
+ )
+{
+ NTSTATUS Status;
+ PCH Stack;
+ SYSTEM_BASIC_INFORMATION SysInfo;
+ BOOLEAN GuardPage;
+ ULONG RegionSize;
+ ULONG OldProtect;
+
+ Status = ZwQuerySystemInformation( SystemBasicInformation,
+ (PVOID)&SysInfo,
+ sizeof( SysInfo ),
+ NULL
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // if stack is in the current process, then default to
+ // the parameters from the image
+ //
+
+ if ( Process == NtCurrentProcess() ) {
+ PPEB Peb;
+ PIMAGE_NT_HEADERS NtHeaders;
+
+
+ Peb = NtCurrentPeb();
+ NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress);
+
+
+ if (!MaximumStackSize) {
+ MaximumStackSize = NtHeaders->OptionalHeader.SizeOfStackReserve;
+ }
+
+ if (!CommittedStackSize) {
+ CommittedStackSize = NtHeaders->OptionalHeader.SizeOfStackCommit;
+ }
+
+ }
+ else {
+
+ if (!CommittedStackSize) {
+ CommittedStackSize = SysInfo.PageSize;
+ }
+
+ if (!MaximumStackSize) {
+ MaximumStackSize = SysInfo.AllocationGranularity;
+ }
+
+ }
+
+
+ if ( CommittedStackSize >= MaximumStackSize ) {
+ MaximumStackSize = ROUND_UP(CommittedStackSize, (1024*1024));
+ }
+
+
+ CommittedStackSize = ROUND_UP( CommittedStackSize, SysInfo.PageSize );
+ MaximumStackSize = ROUND_UP( MaximumStackSize,
+ SysInfo.AllocationGranularity
+ );
+
+ Stack = NULL,
+ Status = ZwAllocateVirtualMemory( Process,
+ (PVOID *)&Stack,
+ ZeroBits,
+ &MaximumStackSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Reservation Status == %X\n",
+ Process,
+ Status
+ );
+#endif // DBG
+ return( Status );
+ }
+
+ InitialTeb->OldInitialTeb.OldStackBase = NULL;
+ InitialTeb->OldInitialTeb.OldStackLimit = NULL;
+ InitialTeb->StackAllocationBase = Stack;
+ InitialTeb->StackBase = Stack + MaximumStackSize;
+
+ Stack += MaximumStackSize - CommittedStackSize;
+ if (MaximumStackSize > CommittedStackSize) {
+ Stack -= SysInfo.PageSize;
+ CommittedStackSize += SysInfo.PageSize;
+ GuardPage = TRUE;
+ }
+ else {
+ GuardPage = FALSE;
+ }
+ Status = ZwAllocateVirtualMemory( Process,
+ (PVOID *)&Stack,
+ 0,
+ &CommittedStackSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ InitialTeb->StackLimit = Stack;
+
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Stack Commit Status == %X\n",
+ Process,
+ Status
+ );
+#endif // DBG
+ return( Status );
+ }
+
+ //
+ // if we have space, create a guard page.
+ //
+
+ if (GuardPage) {
+ RegionSize = SysInfo.PageSize;
+ Status = ZwProtectVirtualMemory( Process,
+ (PVOID *)&Stack,
+ &RegionSize,
+ PAGE_GUARD | PAGE_READWRITE,
+ &OldProtect);
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlpCreateStack( %lx ) failed. Guard Page Creation Status == %X\n",
+ Process,
+ Status
+ );
+#endif // DBG
+ return( Status );
+ }
+ InitialTeb->StackLimit = (PVOID)((PUCHAR)InitialTeb->StackLimit - RegionSize);
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlpFreeStack(
+ IN HANDLE Process,
+ IN PINITIAL_TEB InitialTeb
+ )
+{
+ NTSTATUS Status;
+ ULONG Zero;
+
+ Zero = 0;
+ Status = ZwFreeVirtualMemory( Process,
+ &InitialTeb->StackAllocationBase,
+ &Zero,
+ MEM_RELEASE
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlpFreeStack( %lx ) failed. Stack DeCommit Status == %X\n",
+ Process,
+ Status
+ );
+#endif // DBG
+ return( Status );
+ }
+
+ RtlZeroMemory( InitialTeb, sizeof( *InitialTeb ) );
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlCreateUserProcess(
+ IN PUNICODE_STRING NtImagePathName,
+ IN ULONG Attributes,
+ IN PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
+ IN PSECURITY_DESCRIPTOR ProcessSecurityDescriptor OPTIONAL,
+ IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL,
+ IN HANDLE ParentProcess OPTIONAL,
+ IN BOOLEAN InheritHandles,
+ IN HANDLE DebugPort OPTIONAL,
+ IN HANDLE ExceptionPort OPTIONAL,
+ OUT PRTL_USER_PROCESS_INFORMATION ProcessInformation
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a user mode process with a single thread with
+ a suspend count of one. The address space of the new process is
+ initialized with the contents of specified image file. The caller
+ can specify the Access Control List for the new process and thread.
+ The caller can also specify the parent process to inherit process
+ priority and processor affinity from. The default is to inherit
+ these from the current process. Finally the caller can specify
+ whether the new process is to inherit any of the object handles
+ from the specified parent process or not.
+
+ Information about the new process and thread is returned via
+ the ProcessInformation parameter.
+
+Arguments:
+
+ NtImagePathName - A required pointer that points to the NT Path string
+ that identifies the image file that is to be loaded into the
+ child process.
+
+ ProcessParameters - A required pointer that points to parameters that
+ are to passed to the child process.
+
+ ProcessSecurityDescriptor - An optional pointer to the Security Descriptor
+ give to the new process.
+
+ ThreadSecurityDescriptor - An optional pointer to the Security Descriptor
+ give to the new thread.
+
+ ParentProcess - An optional process handle that will used to inherit
+ certain properties from.
+
+ InheritHandles - A boolean value. TRUE specifies that object handles
+ associated with the specified parent process are to be inherited
+ by the new process, provided they have the OBJ_INHERIT attribute.
+ FALSE specifies that the new process is to inherit no handles.
+
+ DebugPort - An optional handle to the debug port associated with this
+ process.
+
+ ExceptionPort - An optional handle to the exception port associated with this
+ process.
+
+ ProcessInformation - A pointer to a variable that receives information
+ about the new process and thread.
+
+Return Value:
+
+ TBS.
+
+--*/
+
+{
+ NTSTATUS Status;
+ HANDLE Section, File;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ PRTL_USER_PROCESS_PARAMETERS Parameters;
+ ULONG ParameterLength;
+ PVOID Environment;
+ PWCHAR s;
+ ULONG EnvironmentLength;
+ ULONG RegionSize;
+ PROCESS_BASIC_INFORMATION ProcessInfo;
+ PPEB Peb;
+ UNICODE_STRING Unicode;
+
+ //
+ // Zero output parameter and probe the addresses at the same time
+ //
+
+ RtlZeroMemory( ProcessInformation, sizeof( *ProcessInformation ) );
+ ProcessInformation->Length = sizeof( *ProcessInformation );
+
+ //
+ // Open the specified image file.
+ //
+
+ Status = RtlpOpenImageFile( NtImagePathName,
+ Attributes & (OBJ_INHERIT | OBJ_CASE_INSENSITIVE),
+ &File,
+ TRUE
+ );
+ if (!NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+
+ //
+ // Create a memory section backed by the opened image file
+ //
+
+ Status = ZwCreateSection( &Section,
+ SECTION_ALL_ACCESS,
+ NULL,
+ NULL,
+ PAGE_EXECUTE,
+ SEC_IMAGE,
+ File
+ );
+ ZwClose( File );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtCreateSection Status == %X \n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ return( Status );
+ }
+
+
+ //
+ // Create the user mode process, defaulting the parent process to the
+ // current process if one is not specified. The new process will not
+ // have a name nor will the handle be inherited by other processes.
+ //
+
+ if (!ARGUMENT_PRESENT( ParentProcess )) {
+ ParentProcess = NtCurrentProcess();
+ }
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL,
+ ProcessSecurityDescriptor );
+ if ( RtlGetNtGlobalFlags() & FLG_ENABLE_CSRDEBUG ) {
+ if ( wcsstr(NtImagePathName->Buffer,L"csrss") ||
+ wcsstr(NtImagePathName->Buffer,L"CSRSS")
+ ) {
+ RtlInitUnicodeString(&Unicode,L"\\WindowsSS");
+ InitializeObjectAttributes( &ObjectAttributes, &Unicode, 0, NULL,
+ ProcessSecurityDescriptor );
+ }
+ }
+
+ if ( !InheritHandles ) {
+ ProcessParameters->CurrentDirectory.Handle = NULL;
+ }
+ Status = ZwCreateProcess( &ProcessInformation->Process,
+ PROCESS_ALL_ACCESS,
+ &ObjectAttributes,
+ ParentProcess,
+ InheritHandles,
+ Section,
+ DebugPort,
+ ExceptionPort
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtCreateProcess Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( Section );
+ return( Status );
+ }
+
+
+ //
+ // Retreive the interesting information from the image header
+ //
+
+ Status = ZwQuerySection( Section,
+ SectionImageInformation,
+ &ProcessInformation->ImageInformation,
+ sizeof( ProcessInformation->ImageInformation ),
+ NULL
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtQuerySection Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ Status = ZwQueryInformationProcess( ProcessInformation->Process,
+ ProcessBasicInformation,
+ &ProcessInfo,
+ sizeof( ProcessInfo ),
+ NULL
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtQueryProcessInformation Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ Peb = ProcessInfo.PebBaseAddress;
+
+ //
+ // Duplicate Native handles into new process if any specified.
+ // Note that the duplicated handles will overlay the input values.
+ //
+
+ try {
+ Status = STATUS_SUCCESS;
+
+ if ( ProcessParameters->StandardInput ) {
+
+ Status = ZwDuplicateObject(
+ ParentProcess,
+ ProcessParameters->StandardInput,
+ ProcessInformation->Process,
+ &ProcessParameters->StandardInput,
+ 0L,
+ 0L,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+ }
+
+ if ( ProcessParameters->StandardOutput ) {
+
+ Status = ZwDuplicateObject(
+ ParentProcess,
+ ProcessParameters->StandardOutput,
+ ProcessInformation->Process,
+ &ProcessParameters->StandardOutput,
+ 0L,
+ 0L,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+ }
+
+ if ( ProcessParameters->StandardError ) {
+
+ Status = ZwDuplicateObject(
+ ParentProcess,
+ ProcessParameters->StandardError,
+ ProcessInformation->Process,
+ &ProcessParameters->StandardError,
+ 0L,
+ 0L,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+ }
+
+ } finally {
+ if ( !NT_SUCCESS(Status) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. Duplicate Standard I/O Handle Status == %lx\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ }
+ //
+ // Possibly reserve some address space in the new process
+ //
+
+ if (ProcessInformation->ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_NATIVE ) {
+ if ( ProcessParameters->Flags & RTL_USER_PROC_RESERVE_1MB ) {
+
+ Environment = (PVOID)4;
+ RegionSize = (1024*1024)-(256);
+
+ Status = ZwAllocateVirtualMemory( ProcessInformation->Process,
+ (PVOID *)&Environment,
+ 0,
+ &RegionSize,
+ MEM_RESERVE,
+ PAGE_READWRITE
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtAllocateReserveMemory Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+ }
+ }
+
+ //
+ // Allocate virtual memory in the new process and use NtWriteVirtualMemory
+ // to write a copy of the process environment block into the address
+ // space of the new process. Save the address of the allocated block in
+ // the process parameter block so the new process can access it.
+ //
+
+ if (s = (PWCHAR)ProcessParameters->Environment) {
+ while (*s++) {
+ while (*s++) {
+ }
+ }
+ EnvironmentLength = (s - (PWCHAR)ProcessParameters->Environment) * sizeof(WCHAR);
+
+ Environment = NULL;
+ RegionSize = EnvironmentLength;
+ Status = ZwAllocateVirtualMemory( ProcessInformation->Process,
+ (PVOID *)&Environment,
+ 0,
+ &RegionSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtAllocateVirtualMemory Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ Status = ZwWriteVirtualMemory( ProcessInformation->Process,
+ Environment,
+ ProcessParameters->Environment,
+ EnvironmentLength,
+ NULL
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtWriteVirtualMemory Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ ProcessParameters->Environment = Environment;
+ }
+
+ //
+ // Allocate virtual memory in the new process and use NtWriteVirtualMemory
+ // to write a copy of the process parameters block into the address
+ // space of the new process. Set the initial parameter to the new thread
+ // to be the address of the block in the new process's address space.
+ //
+
+ Parameters = NULL;
+ ParameterLength = ProcessParameters->MaximumLength;
+ Status = ZwAllocateVirtualMemory( ProcessInformation->Process,
+ (PVOID *)&Parameters,
+ 0,
+ &ParameterLength,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtAllocateVirtualMemory Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ Status = ZwWriteVirtualMemory( ProcessInformation->Process,
+ Parameters,
+ ProcessParameters,
+ ProcessParameters->Length,
+ NULL
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtWriteVirtualMemory Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+ }
+
+ Status = ZwWriteVirtualMemory( ProcessInformation->Process,
+ &Peb->ProcessParameters,
+ &Parameters,
+ sizeof( Parameters ),
+ NULL
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. NtWriteVirtualMemory Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ //
+ // Create a suspended thread in the new process. Specify the size and
+ // position of the stack, along with the start address, initial parameter
+ // and an SECURITY_DESCRIPTOR. The new thread will not have a name and its handle will
+ // not be inherited by other processes.
+ //
+
+ Status = RtlCreateUserThread(
+ ProcessInformation->Process,
+ ThreadSecurityDescriptor,
+ TRUE,
+ ProcessInformation->ImageInformation.ZeroBits,
+ ProcessInformation->ImageInformation.MaximumStackSize,
+ ProcessInformation->ImageInformation.CommittedStackSize,
+ (PUSER_THREAD_START_ROUTINE)
+ ProcessInformation->ImageInformation.TransferAddress,
+ (PVOID)Peb,
+ &ProcessInformation->Thread,
+ &ProcessInformation->ClientId
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserProcess( %Z ) failed. RtlCreateUserThread Status == %X\n",
+ NtImagePathName,
+ Status
+ );
+#endif // DBG
+ ZwClose( ProcessInformation->Process );
+ ZwClose( Section );
+ return( Status );
+ }
+
+ //
+ // Now close the section and file handles. The objects they represent
+ // will not actually go away until the process is destroyed.
+ //
+
+ ZwClose( Section );
+
+ //
+ // Return success status
+ //
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+RtlCreateUserThread(
+ IN HANDLE Process,
+ IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor OPTIONAL,
+ IN BOOLEAN CreateSuspended,
+ IN ULONG ZeroBits OPTIONAL,
+ IN ULONG MaximumStackSize OPTIONAL,
+ IN ULONG CommittedStackSize OPTIONAL,
+ IN PUSER_THREAD_START_ROUTINE StartAddress,
+ IN PVOID Parameter OPTIONAL,
+ OUT PHANDLE Thread OPTIONAL,
+ OUT PCLIENT_ID ClientId OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This function creates a user mode thread in a user process. The caller
+ specifies the attributes of the new thread. A handle to the thread, along
+ with its Client Id are returned to the caller.
+
+Arguments:
+
+ Process - Handle to the target process in which to create the new thread.
+
+ ThreadSecurityDescriptor - An optional pointer to the Security Descriptor
+ give to the new thread.
+
+ CreateSuspended - A boolean parameter that specifies whether or not the new
+ thread is to be created suspended or not. If TRUE, the new thread
+ will be created with an initial suspend count of 1. If FALSE then
+ the new thread will be ready to run when this call returns.
+
+ ZeroBits - This parameter is passed to the virtual memory manager
+ when the stack is allocated. Stacks are always allocated with the
+ MEM_TOP_DOWN allocation attribute.
+
+ MaximumStackSize - This is the maximum size of the stack. This size
+ will be rounded up to the next highest page boundary. If zero is
+ specified, then the default size will be 64K bytes.
+
+ CommittedStackSize - This is the initial committed size of the stack. This
+ size is rounded up to the next highest page boundary and then an
+ additional page is added for the guard page. The resulting size
+ will then be commited and the guard page protection initialized
+ for the last committed page in the stack.
+
+ StartAddress - The initial starting address of the thread.
+
+ Parameter - An optional pointer to a 32-bit pointer parameter that is
+ passed as a single argument to the procedure at the start address
+ location.
+
+ Thread - An optional pointer that, if specified, points to a variable that
+ will receive the handle of the new thread.
+
+ ClientId - An optional pointer that, if specified, points to a variable
+ that will receive the Client Id of the new thread.
+
+Return Value:
+
+ TBS
+
+--*/
+
+{
+ NTSTATUS Status;
+ CONTEXT ThreadContext;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ INITIAL_TEB InitialTeb;
+ HANDLE ThreadHandle;
+ CLIENT_ID ThreadClientId;
+
+ //
+ // Allocate a stack for this thread in the address space of the target
+ // process.
+ //
+
+ Status = RtlpCreateStack( Process,
+ MaximumStackSize,
+ CommittedStackSize,
+ ZeroBits,
+ &InitialTeb
+ );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // Create an initial context for the new thread.
+ //
+
+
+ RtlInitializeContext( Process,
+ &ThreadContext,
+ Parameter,
+ (PVOID)StartAddress,
+ InitialTeb.StackBase
+ );
+
+ //
+ // Now create a thread in the target process. The new thread will
+ // not have a name and its handle will not be inherited by other
+ // processes.
+ //
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0, NULL,
+ ThreadSecurityDescriptor );
+ Status = ZwCreateThread( &ThreadHandle,
+ THREAD_ALL_ACCESS,
+ &ObjectAttributes,
+ Process,
+ &ThreadClientId,
+ &ThreadContext,
+ &InitialTeb,
+ CreateSuspended
+ );
+ if (!NT_SUCCESS( Status )) {
+#if DBG
+ DbgPrint( "NTRTL: RtlCreateUserThread Failed. NtCreateThread Status == %X\n",
+ Status );
+#endif // DBG
+ RtlpFreeStack( Process, &InitialTeb );
+ }
+ else {
+ if (ARGUMENT_PRESENT( Thread )) {
+ *Thread = ThreadHandle;
+ }
+
+ if (ARGUMENT_PRESENT( ClientId )) {
+ *ClientId = ThreadClientId;
+ }
+
+ }
+
+ //
+ // Return status
+ //
+
+ return( Status );
+}
+
+
+VOID
+RtlFreeUserThreadStack(
+ HANDLE hProcess,
+ HANDLE hThread
+ )
+{
+ NTSTATUS Status;
+ PTEB Teb;
+ THREAD_BASIC_INFORMATION ThreadInfo;
+ PVOID StackDeallocationBase;
+ ULONG Size;
+
+ Status = NtQueryInformationThread( hThread,
+ ThreadBasicInformation,
+ &ThreadInfo,
+ sizeof( ThreadInfo ),
+ NULL
+ );
+ Teb = ThreadInfo.TebBaseAddress;
+ if (!NT_SUCCESS( Status ) || !Teb) {
+ return;
+ }
+
+ Status = NtReadVirtualMemory( hProcess,
+ &Teb->DeallocationStack,
+ &StackDeallocationBase,
+ sizeof( StackDeallocationBase ),
+ &Size
+ );
+ if (!NT_SUCCESS( Status ) || !StackDeallocationBase) {
+ return;
+ }
+
+ Size = 0;
+ NtFreeVirtualMemory( hProcess, &StackDeallocationBase, &Size, MEM_RELEASE );
+ return;
+}
diff --git a/private/ntos/rtl/rxact.c b/private/ntos/rtl/rxact.c
new file mode 100644
index 000000000..6ed46f506
--- /dev/null
+++ b/private/ntos/rtl/rxact.c
@@ -0,0 +1,1880 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ rxact.c
+
+Abstract:
+
+ This Module implements a simple transaction mechanism for registry
+ database operations which helps eliminate the possibility of partial
+ updates being made. The cases covered are specifically partial updates
+ caused by system crashes or program aborts during multiple-write updates.
+
+
+ WARNING: This package does not yet deal with full-disk problems
+ automatically. If a full disk is encountered during
+ transaction commit, then manual interaction may be required
+ to free enough space to complete the commit. There is
+ no means provided for backing out the commit.
+
+Author:
+
+ Jim Kelly (JimK) 15-May-1991
+ Robert Reichel (RobertRe) 15-July-1992
+
+Environment:
+
+ Pure Runtime Library Routine
+
+Revision History:
+
+
+
+--*/
+
+
+/*
+////////////////////////////////////////////////////////////////////////////
+
+High Level Description:
+
+ The simple transaction mechanism expects the following to be true:
+
+ (1) A single server is responsible for operations on an entire
+ sub-tree of the registry database. For example, the security
+ account manager server (SAM) is responsible for everything
+ below \REGISTRY\LOCAL_MACHINE\SECURITY\SAM.
+
+ (2) Transactions on the sub-tree are serialized by the server
+ responsible for the sub-tree. That is, the server will not
+ start a second user request until all previous user requests
+ have been completed.
+
+ The simple transaction mechanism helps eliminate the problem of partial
+ updates caused by system crash or program abort during multiple-write
+ updates to the registry database. This is achieved by:
+
+ (1) Keeping all actions in-memory until instructed to commit. The
+ presence of in-memory data structures implicitly indicates
+ that a transaction is in progress.
+
+ The initial state is no transaction in progress.
+
+ (2) Providing a service which allows a server to initiate a transaction.
+ This allocates in-memory data structures, thereby changing the
+ state to transaction in progress.
+
+ (3) Keeping a log of all keys in the sub-tree that are to be
+ updated in a single transaction. Each record in this log
+ contains the following information:
+
+ (a) The name of the sub-key effected
+
+ (b) The operation to be performed on the sub-key
+ either DELETE or SET_VALUE. Note that these
+ operations are idempotent and may be applied
+ again in the event that the server aborts during
+ an initial commit.
+
+ (c) The new value of the sub-key (if applicable)
+
+ (d) (optionally) The attribute name of the subkey
+ to be operated on.
+
+ (note that SET_VALUE is used to create new sub-keys
+ as well as updated existing ones).
+
+ The entire list of sub-keys to be modified must be entered
+ into this log before ANY of the sub-keys is actually modified.
+
+ (4) Providing a commit service that applies all changes indicated
+ in the change log. This is done by first writing the contents
+ of the in-memory structures to a single key value ("Log") in
+ the registry and flushing the data to disk. The presence of
+ the "Log" value and data imply that a commit is in progress.
+
+ All necessary changes are applied, the "Log" value and its
+ data are deleted, and in-memory data structres are freed,
+ thereby changing the state to no-transaction.
+
+
+ The package also includes a service which must be called upon server
+ startup. This service checks to make sure the state of the sub-tree
+ is NO_TRANSACTION. If it is not, then one of the actions below is
+ performed based upon the current state of the sub-tree:
+
+ COMMITTING - This means the server was previously aborted while
+ a transaction was being committed (applied to the registry).
+ In this case, the commit is performed again from the beginning
+ of the change log. After the commit is completed, the state
+ of the sub-tree is set to NO_TRANSACTION.
+
+////////////////////////////////////////////////////////////////////////////
+*/
+
+
+
+/*
+////////////////////////////////////////////////////////////////////////////
+
+Detailed Description:
+
+ Registry State
+ --------------
+
+ The registry state of a subtree is kept in a sub-key of that tree
+ named:
+
+ "RXACT"
+
+ The value field of that registry key includes a revision field.
+
+
+ RXact Context
+ -------------
+
+ A call to RtlInitializeRXact will return a pointer to an
+ RTL_RXACT_CONTEXT structure. This structure contains:
+
+ (1) the passed RootRegistryKey (eg, key to "Sam"),
+
+ (2) a handle to the top of the RXact subtree (eg, key to
+ "Sam\RXACT"),
+
+ (3) a flag indicating if handles stored in the log are
+ valid,
+
+ (4) a pointer to the current RXactLog.
+
+ The subsystem calling RtlInitializeRXact must keep this returned
+ pointer and pass it back to RXact in all subsequent calls.
+
+
+ Operation Log
+ -------------
+
+ The operation log of a registry sub-tree transaction is kept as sequence
+ of "operation log entries".
+
+ An in-memory log is a block of heap memory allocted by RtlStartRXact.
+ It has a header which contains:
+
+ (1) The count of operations in the log.
+
+ (2) The maximum size of the log.
+
+ (3) The amount of the log currently in use.
+
+ The log data itself follows the header directly.
+
+
+ Operation Log Entries
+ ---------------------
+
+ An operation log entry is described by the following structure:
+
+ typedef struct _RXACT_LOG_ENTRY {
+ ULONG LogEntrySize;
+ RTL_RXACT_OPERATION Operation;
+ UNICODE_STRING SubKeyName; // Self-relativized (Buffer is really offset)
+ UNICODE_STRING AttributeName; // Self-relativized (Buffer is really offset)
+ HANDLE KeyHandle; // optional, not valid if read from disk.
+ ULONG NewKeyValueType;
+ ULONG NewKeyValueLength;
+ PVOID NewKeyValue; // Contains offset to data from start of log
+ } RXACT_LOG_ENTRY, *PRXACT_LOG_ENTRY;
+
+ The log entry contains all of the information passed in during a call
+ to RtlAddActionToRXact or RtlAddAttributeActionToRXact.
+
+ The UNICODE_STRING structures contain an offset to the string data
+ rather than a pointer. These offsets are relative to the start of
+ the log data, and are adjusted in place as each log entry is commited.
+
+ The KeyHandle is valid if it is not equal to INVALID_HANDLE_VALUE and
+ if the HandlesValid flag in the RXactContext structure is TRUE. This
+ is so that we do not attempt to use the handles if the log has been
+ read from disk after a reboot.
+
+
+////////////////////////////////////////////////////////////////////////////
+*/
+
+
+#include "ntrtlp.h"
+
+//
+// Cannot include <windows.h> from kernel code
+//
+#define INVALID_HANDLE_VALUE (HANDLE)-1
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Macros & Definitions //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Revision level of a registry transaction .
+//
+
+#define RTLP_RXACT_REVISION1 (1l)
+#define RTLP_RXACT_CURRENT_REVISION RTLP_RXACT_REVISION1
+
+
+#define RTLP_RXACT_KEY_NAME L"RXACT"
+
+#define RTLP_RXACT_LOG_NAME L"Log"
+
+#define RTLP_INITIAL_LOG_SIZE 0x4000
+
+//
+// Given a value return its longword aligned equivalent value
+//
+
+#define DwordAlign(Value) ( \
+ (ULONG)((((ULONG)(Value)) + 3) & 0xfffffffc) \
+ )
+
+//
+// The value field of the RXACT registry key is one of the following data
+// structures.
+//
+
+//
+// The state of a registry sub-tree is one of the following:
+//
+// RtlpRXactStateNoTransaction - There is not a transaction in progress.
+//
+// RtlpRXactStateCommitting - The actions of a transaction are being
+// applied to the registry database.
+//
+
+typedef enum _RTLP_RXACT_STATE {
+ RtlpRXactStateNoTransaction = 2,
+ RtlpRXactStateCommitting
+} RTLP_RXACT_STATE, *PRTLP_RXACT_STATE;
+
+
+typedef struct _RTLP_RXACT {
+ ULONG Revision;
+ RTLP_RXACT_STATE State; // no longer used
+ ULONG OperationCount; // no longer used
+} RTLP_RXACT, *PRTLP_RXACT;
+
+
+typedef struct _RXACT_LOG_ENTRY {
+ ULONG LogEntrySize;
+ RTL_RXACT_OPERATION Operation;
+ UNICODE_STRING SubKeyName; // Self-relativized (Buffer is really offset)
+ UNICODE_STRING AttributeName; // Self-relativized (Buffer is really offset)
+ HANDLE KeyHandle; // optional, not valid if read from disk.
+ ULONG NewKeyValueType;
+ ULONG NewKeyValueLength;
+ PVOID NewKeyValue; // Contains offset to data from start of log
+} RXACT_LOG_ENTRY, *PRXACT_LOG_ENTRY;
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// //
+// Prototypes for local procedures //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+
+
+
+NTSTATUS
+RXactpCommit(
+ IN PRTL_RXACT_CONTEXT RXactContext
+ );
+
+NTSTATUS
+RXactpOpenTargetKey(
+ IN HANDLE RootRegistryKey,
+ IN RTL_RXACT_OPERATION Operation,
+ IN PUNICODE_STRING SubKeyName,
+ OUT PHANDLE TargetKey
+ );
+
+
+
+VOID
+RXactInitializeContext(
+ IN PRTL_RXACT_CONTEXT RXactContext,
+ IN HANDLE RootRegistryKey,
+ IN HANDLE RXactKey
+ );
+
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RXactpCommit)
+#pragma alloc_text(PAGE,RXactpOpenTargetKey)
+#pragma alloc_text(PAGE,RXactInitializeContext)
+#pragma alloc_text(PAGE,RtlInitializeRXact)
+#pragma alloc_text(PAGE,RtlStartRXact)
+#pragma alloc_text(PAGE,RtlAbortRXact)
+#pragma alloc_text(PAGE,RtlAddAttributeActionToRXact)
+#pragma alloc_text(PAGE,RtlAddActionToRXact)
+#pragma alloc_text(PAGE,RtlApplyRXact)
+#pragma alloc_text(PAGE,RtlApplyRXactNoFlush)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Exported Procedures (defined in ntrtl.h) //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+NTSTATUS
+RtlInitializeRXact(
+ IN HANDLE RootRegistryKey,
+ IN BOOLEAN CommitIfNecessary,
+ OUT PRTL_RXACT_CONTEXT *RXactContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine should be called by a server exactly once when it starts.
+ This routine will check to see that the registry transaction information
+ exists for the specified registry sub-tree, and will create it if it
+ doesn't exist.
+
+Arguments:
+
+ RootRegistryKey - A handle to the registry key within whose sub-tree
+ a transaction is to be initialized.
+
+ CommitIfNecessary - A BOOLEAN value indicating whether or not any
+ previously aborted commit discovered should be commited at this
+ time. A value of TRUE indicates the commit should be applied
+ if encountered. A value of FALSE indicates a previously
+ aborted COMMIT should not be committed at this time.
+
+ RXactContext - Returns a pointer to an RTL_RXACT_CONTEXT structure
+ allocated out of the local heap. The caller must keep this
+ pointer and pass it back in for all future RXact transactions
+ for the passed RootRegistryKey.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the transaction state already exists for the
+ registry sub-tree and is already in the NO_TRANSACTION state.
+
+ STATUS_UNKNOWN_REVISION - Indicates that a transaction state already
+ exists for the specified sub-tree, but is a revision level that is
+ unknown by this service.
+
+ STATUS_RXACT_STATE_CREATED - This informational level status indicates
+ that a specified registry sub-tree transaction state did not yet
+ exist and had to be created.
+
+ STATUS_RXACT_COMMIT_NECESSARY - This warning level status indicates that the
+ transaction state already exists for the registry sub-tree, but that
+ a transaction commit was previously aborted. The commit has NOT been
+ completed. Another call to this service with a CommitIfNecessary value
+ of TRUE may be used to commit the transaction.
+
+
+ STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
+ of the registry sub-tree is incompatible with the requested operation.
+ For example, a request to start a new transaction while one is already
+ in progress, or a request to apply a transaction when one is not
+ currently in progress.
+
+--*/
+
+{
+
+ HANDLE RXactKey;
+ LARGE_INTEGER LastWriteTime;
+ NTSTATUS Status, TmpStatus;
+ OBJECT_ATTRIBUTES RXactAttributes;
+ PKEY_VALUE_FULL_INFORMATION FullInformation;
+ RTLP_RXACT RXactKeyValue;
+ UCHAR BasicInformation[128]; // Should be more than long enough
+ ULONG Disposition;
+ ULONG KeyValueLength;
+ ULONG KeyValueType;
+ ULONG ResultLength;
+ UNICODE_STRING RXactKeyName;
+ UNICODE_STRING ValueName;
+ UNICODE_STRING NullName;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Initialize some stuff
+ //
+
+ KeyValueLength = (ULONG)sizeof( RTLP_RXACT );
+ KeyValueType = 0; // Not used by RXact
+
+ RtlInitUnicodeString( &NullName, NULL );
+
+ //
+ // Create or open the RXACT key.
+ //
+
+ RtlInitUnicodeString( &RXactKeyName, RTLP_RXACT_KEY_NAME);
+
+ InitializeObjectAttributes(
+ &RXactAttributes,
+ &RXactKeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
+ RootRegistryKey,
+ NULL);
+
+// Status = RtlpNtCreateKey(
+// &RXactKey,
+// (KEY_READ | KEY_WRITE | DELETE),
+// &RXactAttributes,
+// 0,
+// NULL,
+// &Disposition
+// );
+
+ Status = NtCreateKey( &RXactKey,
+ (KEY_READ | KEY_WRITE | DELETE),
+ &RXactAttributes,
+ 0, //TitleIndex
+ NULL, //Class OPTIONAL,
+ REG_OPTION_NON_VOLATILE, //CreateOptions,
+ &Disposition
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return(Status);
+ }
+
+ //
+ // Allocate the RXactContext block
+ //
+
+ *RXactContext = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof( RTL_RXACT_CONTEXT ));
+
+ if ( *RXactContext == NULL ) {
+
+ //
+ // Something prevented value assignment...
+ // Get rid of the RXact key and return the error
+ //
+
+ TmpStatus = NtDeleteKey( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+ TmpStatus = NtClose( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+
+ return( STATUS_NO_MEMORY );
+ }
+
+ //
+ // Initialize the newly created RXactContext structure.
+ //
+
+ RXactInitializeContext( *RXactContext, RootRegistryKey, RXactKey );
+
+ //
+ // If we created (as opposed to opened an existing) rxact key,
+ // then we need to initialize it.
+ //
+
+ if ( Disposition == REG_CREATED_NEW_KEY ) {
+
+ RXactKeyValue.Revision = RTLP_RXACT_REVISION1;
+
+ Status = NtSetValueKey( RXactKey,
+ &NullName, // ValueName
+ 0, // TitleIndex
+ KeyValueType,
+ &RXactKeyValue,
+ KeyValueLength
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Something prevented value assignment...
+ // Get rid of the RXact key and return the error
+ //
+
+ TmpStatus = NtDeleteKey( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+ TmpStatus = NtClose( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+
+ RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
+
+ return( Status );
+ }
+
+ return( STATUS_RXACT_STATE_CREATED );
+ }
+
+
+
+ //
+ // We have opened an existing RXACT key.
+ // See if it is a revision level we know about.
+ //
+
+ Status = RtlpNtQueryValueKey(
+ RXactKey, // KeyHandle
+ &KeyValueType, // KeyValueType
+ &RXactKeyValue, // KeyValue
+ &KeyValueLength, // KeyValueLength
+ &LastWriteTime // LastWriteTime
+ );
+
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Something prevented value query...
+ //
+
+ TmpStatus = NtClose( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+ RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
+ return( Status );
+ }
+
+
+ if ( KeyValueLength != (ULONG)sizeof(RTLP_RXACT) ) {
+ TmpStatus = NtClose( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+ RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
+ return( STATUS_UNKNOWN_REVISION );
+ }
+
+ if (RXactKeyValue.Revision != RTLP_RXACT_REVISION1) {
+ TmpStatus = NtClose( RXactKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); //Safe to ignore, notify security group
+ RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
+ return( STATUS_UNKNOWN_REVISION );
+ }
+
+
+
+ //
+ // Right revision...
+ // See if there is a transaction or commit in progress. If not,
+ // return success
+ //
+
+ //
+ // If a log file exists, then we are committing.
+ //
+
+ RtlInitUnicodeString( &ValueName, RTLP_RXACT_LOG_NAME );
+
+ Status = NtQueryValueKey(
+ RXactKey,
+ &ValueName,
+ KeyValueBasicInformation,
+ &BasicInformation,
+ 128,
+ &ResultLength
+ );
+
+ if ( NT_SUCCESS( Status )) {
+
+ //
+ // We found a value called 'Log'. This means that a commit
+ // was in progress.
+ //
+
+ if ( CommitIfNecessary ) {
+
+ //
+ // Query the full value of the log, then call a low level routine
+ // to actually perform the commit.
+ //
+
+ Status = NtQueryValueKey(
+ RXactKey,
+ &ValueName,
+ KeyValueFullInformation,
+ NULL,
+ 0,
+ &ResultLength
+ );
+
+ if ( Status != STATUS_BUFFER_TOO_SMALL ) {
+ return( Status );
+ }
+
+ FullInformation = RtlAllocateHeap( RtlProcessHeap(), 0, ResultLength );
+
+ if ( FullInformation == NULL ) {
+ return( STATUS_NO_MEMORY );
+ }
+
+
+ Status = NtQueryValueKey(
+ RXactKey,
+ &ValueName,
+ KeyValueFullInformation,
+ FullInformation,
+ ResultLength,
+ &ResultLength
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ RtlFreeHeap( RtlProcessHeap(), 0, FullInformation );
+ RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
+ return( Status );
+ }
+
+ //
+ // The log information is buried in the returned FullInformation
+ // buffer. Dig it out and make the RXactLog in the RXactContext
+ // structure point to it. Then commit.
+ //
+
+ (*RXactContext)->RXactLog = (PRTL_RXACT_LOG)((PCHAR)FullInformation + FullInformation->DataOffset);
+
+ //
+ // Don't use any handles we may find in the log file
+ //
+
+ (*RXactContext)->HandlesValid = FALSE;
+
+ Status = RXactpCommit( *RXactContext );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ RtlFreeHeap( RtlProcessHeap(), 0, FullInformation );
+ RtlFreeHeap( RtlProcessHeap(), 0, *RXactContext );
+ return( Status );
+ }
+
+
+ //
+ // The commit was successful. Clean up.
+ // Delete the log file value and data
+ //
+
+ Status = NtDeleteValueKey( RXactKey, &ValueName );
+
+ //
+ // This should never fail
+ //
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ //
+ // Get rid of the in memory data structures. Abort
+ // will free the RXactLog, so put what we want
+ // freed in there and it will go away.
+ //
+
+ (*RXactContext)->RXactLog = (PRTL_RXACT_LOG)FullInformation;
+
+ Status = RtlAbortRXact( *RXactContext );
+
+ //
+ // This should never fail
+ //
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ } else {
+
+ return( STATUS_RXACT_COMMIT_NECESSARY );
+ }
+
+ } else {
+
+ //
+ // No log, so nothing to do here.
+ //
+
+ return( STATUS_SUCCESS );
+ }
+
+}
+
+
+
+VOID
+RXactInitializeContext(
+ IN PRTL_RXACT_CONTEXT RXactContext,
+ IN HANDLE RootRegistryKey,
+ IN HANDLE RXactKey
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes an in-memory RXactContext structure.
+
+Arguments:
+
+ RXactContext - Supplies a pointer to an RXact Context created
+ by RtlInitializeRXact.
+
+ RootRegistryKey - Supplies the RootRegistryKey for this component.
+
+ RXactKey - Supplies the {RootRegistryKey}\RXactKey for this component
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Initialize the RXactContext for this client
+ //
+
+ RXactContext->RootRegistryKey = RootRegistryKey;
+ RXactContext->HandlesValid = TRUE;
+ RXactContext->RXactLog = NULL;
+ RXactContext->RXactKey = RXactKey;
+
+ return;
+}
+
+
+
+NTSTATUS
+RtlStartRXact(
+ IN PRTL_RXACT_CONTEXT RXactContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to start a new transaction in a registry sub-tree.
+ Transactions must be serialized by the server so that only one transaction
+ is in progress at a time.
+
+Arguments:
+
+ RXactContext - Supplies a pointer to an RTL_RXACT_CONTEXT structure
+ that is not currently in use.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the transaction was started.
+
+ STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
+ of the registry sub-tree is incompatible with the requested operation.
+ For example, a request to start a new transaction while one is already
+ in progress, or a request to apply a transaction when one is not
+ currently in progress. This may also indicate that there is no
+ transaction state at all for the specified registry sub-tree.
+
+--*/
+{
+ PRTL_RXACT_LOG RXactLogHeader;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Allocate in-memory log file and initialize. This implicitly
+ // sets the state to 'transaction in progress'.
+ //
+
+ if ( RXactContext->RXactLog != NULL ) {
+
+ //
+ // There is already a transaction in progress for this
+ // context. Return an error.
+ //
+
+ return( STATUS_RXACT_INVALID_STATE );
+ }
+
+ RXactLogHeader = RtlAllocateHeap( RtlProcessHeap(), 0, RTLP_INITIAL_LOG_SIZE );
+
+ if ( RXactLogHeader == NULL ) {
+ return( STATUS_NO_MEMORY );
+ }
+
+ //
+ // Fill in the log header information at the top of the
+ // newly allocated buffer.
+ //
+
+
+ RXactLogHeader->OperationCount = 0;
+ RXactLogHeader->LogSize = RTLP_INITIAL_LOG_SIZE;
+ RXactLogHeader->LogSizeInUse = sizeof( RTL_RXACT_LOG );
+
+ RXactContext->RXactLog = RXactLogHeader;
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+NTSTATUS
+RtlAbortRXact(
+ IN PRTL_RXACT_CONTEXT RXactContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to abort a transaction in a registry sub-tree.
+
+Arguments:
+
+ RootRegistryKey - A handle to the registry key within whose sub-tree
+ the transaction is to be aborted.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the transaction was aborted.
+
+
+ STATUS_UNKNOWN_REVISION - Indicates that a transaction state
+ exists for the specified sub-tree, but has a revision level that is
+ unknown by this service.
+
+
+ STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
+ of the registry sub-tree is incompatible with the requested operation.
+ For example, a request to start a new transaction while one is already
+ in progress, or a request to apply a transaction when one is not
+ currently in progress. This may also indicate that there is no
+ transaction state at all for the specified registry sub-tree.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if ( RXactContext->RXactLog == NULL ) {
+
+ //
+ // There is no transaction in progress for this
+ // context. Return an error.
+ //
+
+ return( STATUS_RXACT_INVALID_STATE );
+ }
+
+ (VOID) RtlFreeHeap( RtlProcessHeap(), 0, RXactContext->RXactLog );
+
+ //
+ // Reinitialize the RXactContext structure with the same initial data.
+ //
+
+ RXactInitializeContext(
+ RXactContext,
+ RXactContext->RootRegistryKey,
+ RXactContext->RXactKey
+ );
+
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+
+NTSTATUS
+RtlAddAttributeActionToRXact(
+ IN PRTL_RXACT_CONTEXT RXactContext,
+ IN RTL_RXACT_OPERATION Operation,
+ IN PUNICODE_STRING SubKeyName,
+ IN HANDLE KeyHandle OPTIONAL,
+ IN PUNICODE_STRING AttributeName,
+ IN ULONG NewValueType,
+ IN PVOID NewValue,
+ IN ULONG NewValueLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to add a new action to the transaction operation log.
+ Upon commit, these operations are applied in the order they are added
+ to the log.
+
+ This routine differs from RtlAddActionToRXact in that it takes an Attribute
+ Name parameter, rather than using the default ("NULL") Attribute of the
+ specified key.
+
+
+Arguments:
+
+ RXactContext - Supplies a pointer to the RXactContext structure for this
+ subsystem's root registry key.
+
+ Operation - Indicates the type of operation to perform (e.g., delete
+ a sub-key or set the value of a sub-key). Sub-keys may be created
+ by setting a value of a previously non-existent sub-key. This will
+ cause all sub-keys between the root and the specified sub-key to
+ be created.
+
+ SubKeyName - Specifies the name of the target registry key. This name
+ is relative to the Root of the Registry transaction sub-tree
+ and must NOT start with a delimiter character ("\").
+
+ KeyHandle - Optionally supplies a handle to the target key. If
+ not specified, the name passed for SubKeyName will determine
+ the target key.
+
+ AttributeName - Supplies the name of the key attribute to be
+ modified.
+
+ NewKeyValueType - (Optional) Contains the KeyValueType to assign
+ to the target registry key. This parameter is ignored if the
+ Operation is not RtlRXactOperationSetValue.
+
+ NewKeyValue - (Optional) Points to a buffer containing the value
+ to assign to the specified target registry key. This parameter
+ is ignored if the Operation is not RtlRXactOperationSetValue.
+
+ NewKeyValueLength - Indicates the length (number of bytes) of the
+ NewKeyValue buffer. This parameter is ignored if the Operation
+ is not RtlRXactOperationSetValue.
+
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the request completed successfully..
+
+ STATUS_INVALID_PARAMETER - Indicates that an unknown Operation
+ was requested.
+
+ STATUS_NO_MEMORY - Insufficient memeory was available to complete
+ this operation.
+
+ STATUS_UNKNOWN_REVISION - Indicates that a transaction state
+ exists for the specified sub-tree, but has a revision level that is
+ unknown by this service.
+
+
+--*/
+
+{
+
+ PRTL_RXACT_LOG NewLog;
+ PRXACT_LOG_ENTRY Base;
+
+ ULONG End;
+ ULONG LogEntrySize;
+ ULONG NewLogSize;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Make sure we were passed a legitimate operation.
+ //
+
+ if ( (Operation != RtlRXactOperationDelete) &&
+ (Operation != RtlRXactOperationSetValue) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Compute the total size of the new data
+ //
+
+ LogEntrySize = sizeof( RXACT_LOG_ENTRY ) +
+ DwordAlign( SubKeyName->Length ) +
+ DwordAlign( AttributeName->Length ) +
+ DwordAlign( NewValueLength );
+
+ //
+ // Make sure there is enough space in the current
+ // log file for this data. If not, we must create
+ // a larger log, copy all the old data, and then
+ // append this to the end.
+ //
+
+ if ( RXactContext->RXactLog->LogSizeInUse + LogEntrySize >
+ RXactContext->RXactLog->LogSize ) {
+
+ //
+ // We must allocate a bigger log file.
+ //
+
+ NewLogSize = RXactContext->RXactLog->LogSize;
+
+ do {
+
+ NewLogSize = NewLogSize * 2;
+
+ } while ( NewLogSize <
+ ( RXactContext->RXactLog->LogSizeInUse + LogEntrySize ) );
+
+ NewLog = RtlAllocateHeap( RtlProcessHeap(), 0, NewLogSize );
+
+ if ( NewLog == NULL ) {
+ return( STATUS_NO_MEMORY );
+ }
+
+ //
+ // Copy over previous information
+ //
+
+ RtlMoveMemory( NewLog, RXactContext->RXactLog, RXactContext->RXactLog->LogSizeInUse );
+
+ //
+ // Free the old log file
+ //
+
+ RtlFreeHeap( RtlProcessHeap(), 0, RXactContext->RXactLog );
+
+ //
+ // Install the new log file and adjust its size in its header
+ //
+
+ RXactContext->RXactLog = NewLog;
+ RXactContext->RXactLog->LogSize = NewLogSize;
+ }
+
+ //
+ // The log file is big enough, append data to
+ // the end.
+ //
+
+ Base = (PRXACT_LOG_ENTRY)((PCHAR)(RXactContext->RXactLog) +
+ (RXactContext->RXactLog->LogSizeInUse));
+
+
+ //
+ // Append each parameter to the end of the log. Unicode string data
+ // will be appended to the end of the entry. The Buffer field in the
+ // Unicode string structure will contain the offset to the Buffer,
+ // relative to the beginning of the log file.
+ //
+
+ Base->LogEntrySize = LogEntrySize;
+ Base->Operation = Operation;
+ Base->SubKeyName = *SubKeyName;
+ Base->AttributeName = *AttributeName;
+ Base->NewKeyValueType = NewValueType;
+ Base->NewKeyValueLength = NewValueLength;
+ Base->KeyHandle = KeyHandle;
+
+ //
+ // Fill in the variable length data: SubKeyName, AttributeName,
+ // and NewKeyValue
+ //
+
+ //
+ // End is an offset relative to the beginning of the entire log
+ // structure. It is initialized to 'point' to the offset immediately
+ // following the structure we just filled in above.
+ //
+
+ End = (ULONG)((PCHAR)(RXactContext->RXactLog->LogSizeInUse) +
+ sizeof( *Base ));
+
+
+ //
+ // Append SubKeyName information to the log file
+ //
+
+ RtlMoveMemory (
+ (PCHAR)(RXactContext->RXactLog) + End,
+ SubKeyName->Buffer,
+ SubKeyName->Length
+ );
+
+ Base->SubKeyName.Buffer = (PWSTR)End;
+ End += DwordAlign( SubKeyName->Length );
+
+
+
+ //
+ // Append AttributeName information to the log file
+ //
+
+
+ RtlMoveMemory(
+ (PCHAR)(RXactContext->RXactLog) + End,
+ AttributeName->Buffer,
+ AttributeName->Length
+ );
+
+ Base->AttributeName.Buffer = (PWSTR)End;
+ End += DwordAlign( AttributeName->Length );
+
+
+
+ //
+ // Append NewKeyValue information (if present) to the log file
+ //
+
+ if ( Operation == RtlRXactOperationSetValue ) {
+
+ RtlMoveMemory(
+ (PCHAR)(RXactContext->RXactLog) + End,
+ NewValue,
+ NewValueLength
+ );
+
+ Base->NewKeyValue = (PVOID)End;
+ End += DwordAlign( NewValueLength );
+ }
+
+
+
+ RXactContext->RXactLog->LogSizeInUse = End;
+ RXactContext->RXactLog->OperationCount++;
+
+ //
+ // We're done
+ //
+
+ return(STATUS_SUCCESS);
+}
+
+
+NTSTATUS
+RtlAddActionToRXact(
+ IN PRTL_RXACT_CONTEXT RXactContext,
+ IN RTL_RXACT_OPERATION Operation,
+ IN PUNICODE_STRING SubKeyName,
+ IN ULONG NewKeyValueType,
+ IN PVOID NewKeyValue OPTIONAL,
+ IN ULONG NewKeyValueLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to add a new action to the transaction operation log.
+ Upon commit, these operations are applied in the order they are added
+ to the log.
+
+Arguments:
+
+ RXactContext - Supplies a pointer to the RXactContext structure for this
+ subsystem's root registry key.
+
+ Operation - Indicates the type of operation to perform (e.g., delete
+ a sub-key or set the value of a sub-key). Sub-keys may be created
+ by setting a value of a previously non-existent sub-key. This will
+ cause all sub-keys between the root and the specified sub-key to
+ be created.
+
+ SubKeyName - Specifies the name of the target registry key. This name
+ is relative to the Root of the Registry transaction sub-tree
+ and must NOT start with a delimiter character ("\").
+
+ NewKeyValueType - (Optional) Contains the KeyValueType to assign
+ to the target registry key. This parameter is ignored if the
+ Operation is not RtlRXactOperationSetValue.
+
+ NewKeyValue - (Optional) Points to a buffer containing the value
+ to assign to the specified target registry key. This parameter
+ is ignored if the Operation is not RtlRXactOperationSetValue.
+
+ NewKeyValueLength - Indicates the length (number of bytes) of the
+ NewKeyValue buffer. This parameter is ignored if the Operation
+ is not RtlRXactOperationSetValue.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the request completed successfully..
+
+
+ STATUS_UNKNOWN_REVISION - Indicates that a transaction state
+ exists for the specified sub-tree, but has a revision level that is
+ unknown by this service.
+
+ Others - Other status values that may be returned from registry key
+ services (such as STATUS_ACCESS_DENIED).
+
+--*/
+{
+ UNICODE_STRING AttributeName;
+ NTSTATUS Status;
+
+ RTL_PAGED_CODE();
+
+ RtlInitUnicodeString( &AttributeName, NULL );
+
+ Status = RtlAddAttributeActionToRXact(
+ RXactContext,
+ Operation,
+ SubKeyName,
+ INVALID_HANDLE_VALUE,
+ &AttributeName,
+ NewKeyValueType,
+ NewKeyValue,
+ NewKeyValueLength
+ );
+
+ return( Status );
+
+
+}
+
+
+
+NTSTATUS
+RtlApplyRXact(
+ IN PRTL_RXACT_CONTEXT RXactContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to apply the changes of a registry sub-tree
+ Transaction to that registry sub-tree. This routine is meant to be
+ called for the common case, where the hive is automatically
+ lazy-flushed. That means that this routine must write the change log
+ to disk, then flush the hive (to ensure that pieces of changes aren't
+ lazy-written to disk before this routine finishes an atomic operation),
+ the apply the changes, then delete the change log.
+
+ The actual changes will be lazy-written to disk, but the registry
+ guarantees that none or all will make it. If the machine goes down
+ while this routine is executing, the flushed change log guarantees
+ that the hive can be put into a consistent state.
+
+Arguments:
+
+ RXactContext - Supplies a pointer to the RXactContext structure for this
+ subsystem's root registry key.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the transaction was completed.
+
+ STATUS_UNKNOWN_REVISION - Indicates that a transaction state
+ exists for the specified sub-tree, but has a revision level that is
+ unknown by this service.
+
+
+ STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
+ of the registry sub-tree is incompatible with the requested operation.
+ For example, a request to start a new transaction while one is already
+ in progress, or a request to apply a transaction when one is not
+ currently in progress. This may also indicate that there is no
+ transaction state at all for the specified registry sub-tree.
+
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING LogName;
+ HANDLE RXactKey;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Commit the contents of the current log to disk
+ //
+
+ RXactKey = RXactContext->RXactKey;
+
+ RtlInitUnicodeString( &LogName, RTLP_RXACT_LOG_NAME );
+
+ Status = NtSetValueKey( RXactKey,
+ &LogName, // ValueName
+ 0, // TitleIndex
+ REG_BINARY,
+ RXactContext->RXactLog,
+ RXactContext->RXactLog->LogSizeInUse
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = NtFlushKey( RXactKey );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // If this fails, maintain the in-memory data,
+ // but get rid of what we just tried to write
+ // to disk.
+ //
+ // Ignore the error, since we're in a funky
+ // state right now.
+ //
+
+ (VOID) NtDeleteValueKey( RXactKey, &LogName );
+
+ return( Status );
+ }
+
+ //
+ // The log is safe, now execute what is in it
+ //
+
+ Status = RXactpCommit( RXactContext );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // As above, try to get rid of what's on
+ // disk, leave the in-memory stuff alone,
+ // so that the caller may try again.
+ //
+
+ (VOID) NtDeleteValueKey( RXactKey, &LogName );
+
+ return( Status );
+ }
+
+ //
+ // Delete the log file value and data
+ //
+
+ Status = NtDeleteValueKey( RXactKey, &LogName );
+
+ //
+ // This should never fail
+ //
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ //
+ // Get rid of the in memory data structures. Abort
+ // does exactly what we want to do.
+ //
+
+ Status = RtlAbortRXact( RXactContext );
+
+ //
+ // This should never fail
+ //
+
+ ASSERT( NT_SUCCESS( Status ));
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+
+NTSTATUS
+RtlApplyRXactNoFlush(
+ IN PRTL_RXACT_CONTEXT RXactContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to apply the changes of a registry sub-tree
+ Transaction to that registry sub-tree. This routine should only be
+ called for special hives that do not have automatic lazy-flushing.
+ The caller must decide when to flush the hive in order to guarantee
+ a consistent hive.
+
+Arguments:
+
+ RXactContext - Supplies a pointer to the RXactContext structure for this
+ subsystem's root registry key.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the transaction was completed.
+
+ STATUS_UNKNOWN_REVISION - Indicates that a transaction state
+ exists for the specified sub-tree, but has a revision level that is
+ unknown by this service.
+
+
+ STATUS_RXACT_INVALID_STATE - Indicates that the transaction state
+ of the registry sub-tree is incompatible with the requested operation.
+ For example, a request to start a new transaction while one is already
+ in progress, or a request to apply a transaction when one is not
+ currently in progress. This may also indicate that there is no
+ transaction state at all for the specified registry sub-tree.
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Execute the contents of the RXACT log.
+ //
+
+ Status = RXactpCommit( RXactContext );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Get rid of the in memory data structures. Abort
+ // does exactly what we want to do.
+ //
+
+ Status = RtlAbortRXact( RXactContext );
+
+ //
+ // This should never fail
+ //
+
+ ASSERT( NT_SUCCESS( Status ));
+ }
+
+ return( Status );
+
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Internal Procedures (defined in within this file) //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+
+
+NTSTATUS
+RXactpCommit(
+ IN PRTL_RXACT_CONTEXT RXactContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine commits the operations in the operation log.
+
+ When all changes have been applied, the transaction state
+ is changed to NO_TRANSACTION.
+
+Arguments:
+
+ RXactContext - Supplies a pointer to the RXactContext structure for this
+ subsystem's root registry key.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the transaction was completed.
+
+
+
+--*/
+{
+ BOOLEAN HandlesValid;
+
+ HANDLE TargetKey;
+ HANDLE RXactKey;
+ HANDLE RootRegistryKey;
+
+ PRTL_RXACT_LOG RXactLog;
+ PRXACT_LOG_ENTRY RXactLogEntry;
+ RTL_RXACT_OPERATION Operation;
+
+ ULONG OperationCount;
+ ULONG i;
+
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS TmpStatus = STATUS_SUCCESS;
+ BOOLEAN CloseTargetKey;
+
+ //
+ // Extract information from the RXactContext to simplify
+ // the code that follows
+ //
+
+ RootRegistryKey = RXactContext->RootRegistryKey;
+ RXactKey = RXactContext->RXactKey;
+ RXactLog = RXactContext->RXactLog;
+
+ OperationCount = RXactLog->OperationCount;
+
+ HandlesValid = RXactContext->HandlesValid;
+
+
+ //
+ // Keep a pointer to the beginning of the current log entry.
+ //
+
+ RXactLogEntry = (PRXACT_LOG_ENTRY)((PCHAR)RXactLog + sizeof( RTL_RXACT_LOG ));
+
+
+ //
+ // Go through and perform each operation log. Notice that some operation
+ // logs may already have been deleted by a previous commit attempt.
+ // So, don't get alarmed if we don't successfully open some operation
+ // log entry keys.
+ //
+
+ for ( i=0 ; i<OperationCount ; i++ ) {
+
+ //
+ // Turn the self-relative offsets in the structure
+ // back into real pointers.
+ //
+
+ RXactLogEntry->SubKeyName.Buffer = (PWSTR) ((PCHAR)RXactLogEntry->SubKeyName.Buffer +
+ (ULONG)RXactLog);
+
+ RXactLogEntry->AttributeName.Buffer = (PWSTR) ((PCHAR)RXactLogEntry->AttributeName.Buffer +
+ (ULONG)RXactLog);
+
+ RXactLogEntry->NewKeyValue = (PVOID)((PCHAR)RXactLogEntry->NewKeyValue + (ULONG)RXactLog);
+
+ Operation = RXactLogEntry->Operation;
+
+ //
+ // Perform this operation
+ //
+
+ switch (Operation) {
+ case RtlRXactOperationDelete:
+
+ //
+ // Open the target key and delete it.
+ // The name is relative to the RootRegistryKey.
+ //
+
+ if ( ((RXactLogEntry->KeyHandle == INVALID_HANDLE_VALUE) || !HandlesValid) ) {
+
+ Status = RXactpOpenTargetKey(
+ RootRegistryKey,
+ RtlRXactOperationDelete,
+ &RXactLogEntry->SubKeyName,
+ &TargetKey
+ );
+
+ if ( !NT_SUCCESS(Status)) {
+
+ //
+ // We must allow the object not to be found,
+ // because we may be replaying this log after
+ // it had been partially executed.
+ //
+
+ if ( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
+
+ return( Status );
+
+ } else {
+
+ break;
+ }
+ }
+
+ CloseTargetKey = TRUE;
+
+ } else {
+
+ TargetKey = RXactLogEntry->KeyHandle;
+ CloseTargetKey = FALSE;
+ }
+
+
+ //
+ // If this fails, then it is an error
+ // because the key should exist at
+ // this point.
+ //
+
+ Status = NtDeleteKey( TargetKey );
+
+
+ //
+ // Only close the target key if we opened it
+ //
+
+ if ( CloseTargetKey ) {
+
+ TmpStatus = NtClose( TargetKey );
+
+ //
+ // If we opened this handle, then we should
+ // be able to close it, whether it has been
+ // deleted or not.
+ //
+
+ ASSERT(NT_SUCCESS(TmpStatus)); // safe to ignore, but curious...
+ }
+
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ break;
+
+ case RtlRXactOperationSetValue:
+
+ //
+ // Open the target key.
+ // The name is relative to the RootRegistryKey.
+ //
+
+ if ( ((RXactLogEntry->KeyHandle == INVALID_HANDLE_VALUE) || !HandlesValid) ) {
+
+ Status = RXactpOpenTargetKey(
+ RootRegistryKey,
+ RtlRXactOperationSetValue,
+ &RXactLogEntry->SubKeyName,
+ &TargetKey
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return(Status);
+ }
+
+ CloseTargetKey = TRUE;
+
+ } else {
+
+ TargetKey = RXactLogEntry->KeyHandle;
+ CloseTargetKey = FALSE;
+ }
+
+ //
+ // Assign to the target key's new value
+ //
+
+ Status = NtSetValueKey( TargetKey,
+ &RXactLogEntry->AttributeName,
+ 0, // TitleIndex
+ RXactLogEntry->NewKeyValueType,
+ RXactLogEntry->NewKeyValue,
+ RXactLogEntry->NewKeyValueLength
+ );
+
+ //
+ // Only close the target key if we opened it
+ //
+
+ if ( CloseTargetKey ) {
+
+ TmpStatus = NtClose( TargetKey );
+ ASSERT(NT_SUCCESS(TmpStatus)); // safe to ignore, but curious...
+
+ }
+
+ if ( !NT_SUCCESS(Status) ) {
+ return(Status);
+ }
+
+ break;
+
+
+
+ default:
+
+ //
+ // Unknown operation type. This should never happen.
+ //
+
+ ASSERT( FALSE );
+
+ return(STATUS_INVALID_PARAMETER);
+
+ }
+
+ RXactLogEntry = (PRXACT_LOG_ENTRY)((PCHAR)RXactLogEntry + RXactLogEntry->LogEntrySize);
+
+ }
+
+ //
+ // Commit complete
+ //
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+
+
+NTSTATUS
+RXactpOpenTargetKey(
+ IN HANDLE RootRegistryKey,
+ IN RTL_RXACT_OPERATION Operation,
+ IN PUNICODE_STRING SubKeyName,
+ OUT PHANDLE TargetKey
+ )
+
+/*++
+
+Routine Description:
+
+ This routine opens the target registry key of an operation.
+
+Arguments:
+
+ RootRegistryKey - A handle to the registry key within whose sub-tree
+ a transaction is to be initialized.
+
+ Operation - Indicates what operation is to be performed on the target.
+ This will effect how the target is opened.
+
+ OperationNameKey - A handle to the operation log sub-key
+ containing the name of the target registry key.
+
+ TargetKey - Receives a handle to the target registry key.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the operation log entry was opened.
+
+ STATUS_NO_MEMORY - Ran out of heap.
+
+
+--*/
+{
+
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES TargetKeyAttributes;
+ ACCESS_MASK DesiredAccess;
+ ULONG Disposition;
+
+
+ if (Operation == RtlRXactOperationDelete) {
+
+ DesiredAccess = DELETE;
+
+ InitializeObjectAttributes(
+ &TargetKeyAttributes,
+ SubKeyName,
+ OBJ_CASE_INSENSITIVE,
+ RootRegistryKey,
+ NULL);
+
+// Status = RtlpNtOpenKey(
+// TargetKey,
+// DesiredAccess,
+// &TargetKeyAttributes,
+// 0);
+
+ Status = NtOpenKey( TargetKey,
+ DesiredAccess,
+ &TargetKeyAttributes
+ );
+
+
+ } else if (Operation == RtlRXactOperationSetValue) {
+
+ DesiredAccess = KEY_WRITE;
+
+ InitializeObjectAttributes(
+ &TargetKeyAttributes,
+ SubKeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
+ RootRegistryKey,
+ NULL);
+
+ Status = NtCreateKey(
+ TargetKey,
+ DesiredAccess,
+ &TargetKeyAttributes,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ &Disposition
+ );
+
+ } else {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+
+
+ return( Status );
+
+}
+
+
+
+//NTSTATUS
+//RXactpAssignTargetValue(
+// IN PVOID NewKeyValue,
+// IN ULONG NewKeyValueLength,
+// IN ULONG NewKeyValueType,
+// IN HANDLE TargetKey,
+// IN PUNICODE_STRING AttributeName
+// );
+
+
+//NTSTATUS
+//RXactpAssignTargetValue(
+// IN PVOID NewKeyValue,
+// IN ULONG NewKeyValueLength,
+// IN ULONG NewKeyValueType,
+// IN HANDLE TargetKey,
+// IN PUNICODE_STRING AttributeName
+// )
+//
+///*++
+//
+//Routine Description:
+//
+// This routine copies the value of an operation log entry to its
+// corresponding target key. The target key must already be open.
+//
+//Arguments:
+//
+// NewKeyValue - The new value for the key being modified.
+//
+// NewKeyValueLength - The size in bytes of the new value information.
+//
+// NewKeyValueType - The type of the data for the new key.
+//
+// TargetKey - A handle to the target registry key.
+//
+// AttributeName - Supplies the name of the key attribute being edited.
+//
+//Return Value:
+//
+// STATUS_SUCCESS - Indicates the value was successfully applied to
+// the target registry key.
+//
+// STATUS_NO_MEMORY - ran out of heap.
+//
+//
+//--*/
+//{
+// NTSTATUS Status;
+//
+// //
+// // Now apply the value to the target key
+// //
+// // Even if there is no key value, we need to do the assign so that
+// // the key value type is assigned.
+// //
+//
+// Status = NtSetValueKey( TargetKey,
+// AttributeName,
+// 0, // TitleIndex
+// NewKeyValueType,
+// NewKeyValue,
+// NewKeyValueLength
+// );
+//
+//
+// return( Status );
+//}
diff --git a/private/ntos/rtl/sertl.c b/private/ntos/rtl/sertl.c
new file mode 100644
index 000000000..bf44bbf6e
--- /dev/null
+++ b/private/ntos/rtl/sertl.c
@@ -0,0 +1,5010 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sertl.c
+
+Abstract:
+
+ This Module implements many security rtl routines defined in ntseapi.h
+
+Author:
+
+ Jim Kelly (JimK) 23-Mar-1990
+ Robert Reichel (RobertRe) 1-Mar-1991
+
+Environment:
+
+ Pure Runtime Library Routine
+
+Revision History:
+
+
+--*/
+
+
+#include "ntrtlp.h"
+#include <stdio.h>
+#include "seopaque.h"
+#include "sertlp.h"
+
+//
+// BUG, BUG does anybody use this routine - no prototype in ntrtl.h
+//
+
+ULONG
+RtlLengthUsedSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ );
+
+#undef RtlEqualLuid
+
+NTSYSAPI
+BOOLEAN
+NTAPI
+RtlEqualLuid (
+ PLUID Luid1,
+ PLUID Luid2
+ );
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlRunEncodeUnicodeString)
+#pragma alloc_text(PAGE,RtlRunDecodeUnicodeString)
+#pragma alloc_text(PAGE,RtlEraseUnicodeString)
+#pragma alloc_text(PAGE,RtlAdjustPrivilege)
+#pragma alloc_text(PAGE,RtlValidSid)
+#pragma alloc_text(PAGE,RtlEqualSid)
+#pragma alloc_text(PAGE,RtlEqualPrefixSid)
+#pragma alloc_text(PAGE,RtlLengthRequiredSid)
+#pragma alloc_text(PAGE,RtlAllocateAndInitializeSid)
+#pragma alloc_text(PAGE,RtlInitializeSid)
+#pragma alloc_text(PAGE,RtlFreeSid)
+#pragma alloc_text(PAGE,RtlIdentifierAuthoritySid)
+#pragma alloc_text(PAGE,RtlSubAuthoritySid)
+#pragma alloc_text(PAGE,RtlSubAuthorityCountSid)
+#pragma alloc_text(PAGE,RtlLengthSid)
+#pragma alloc_text(PAGE,RtlCopySid)
+#pragma alloc_text(PAGE,RtlCopySidAndAttributesArray)
+#pragma alloc_text(PAGE,RtlConvertSidToUnicodeString)
+#pragma alloc_text(PAGE,RtlEqualLuid)
+#pragma alloc_text(PAGE,RtlCopyLuid)
+#pragma alloc_text(PAGE,RtlCopyLuidAndAttributesArray)
+#pragma alloc_text(PAGE,RtlCreateSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlValidSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlLengthSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlLengthUsedSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlGetControlSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlSetDaclSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlGetDaclSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlSetSaclSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlGetSaclSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlSetOwnerSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlGetOwnerSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlSetGroupSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlGetGroupSecurityDescriptor)
+#pragma alloc_text(PAGE,RtlAreAllAccessesGranted)
+#pragma alloc_text(PAGE,RtlAreAnyAccessesGranted)
+#pragma alloc_text(PAGE,RtlMapGenericMask)
+#pragma alloc_text(PAGE,RtlpApplyAclToObject)
+#pragma alloc_text(PAGE,RtlpContainsCreatorGroupSid)
+#pragma alloc_text(PAGE,RtlpContainsCreatorOwnerSid)
+#pragma alloc_text(PAGE,RtlpGenerateInheritAcl)
+#pragma alloc_text(PAGE,RtlpGenerateInheritedAce)
+#pragma alloc_text(PAGE,RtlpInheritAcl)
+#pragma alloc_text(PAGE,RtlpLengthInheritAcl)
+#pragma alloc_text(PAGE,RtlpLengthInheritedAce)
+#pragma alloc_text(PAGE,RtlpValidOwnerSubjectContext)
+#endif
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Local Macros and Symbols //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+#define CREATOR_SID_SIZE 12
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Exported Procedures //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+VOID
+RtlRunEncodeUnicodeString(
+ PUCHAR Seed OPTIONAL,
+ PUNICODE_STRING String
+ )
+
+/*++
+
+Routine Description:
+
+ This function performs a trivial XOR run-encoding of a string.
+ The purpose of this run-encoding is to change the character values
+ to appear somewhat random and typically not printable. This is
+ useful for transforming passwords that you don't want to be easily
+ distinguishable by visually scanning a paging file or memory dump.
+
+
+Arguments:
+
+ Seed - Points to a seed value to use in the encoding. If the
+ pointed to value is zero, then this routine will assign
+ a value.
+
+ String - The string to encode. This string may be decode
+ by passing it and the seed value to RtlRunDecodeUnicodeString().
+
+
+Return Value:
+
+ None - Nothing can really go wrong unless the caller passes bogus
+ parameters. In this case, the caller can catch the access
+ violation.
+
+
+--*/
+{
+
+ LARGE_INTEGER Time;
+ PUCHAR LocalSeed;
+ NTSTATUS Status;
+ ULONG i;
+ PSTRING S;
+
+
+ RTL_PAGED_CODE();
+
+ //
+ // Typecast so we can work on bytes rather than WCHARs
+ //
+
+ S = (PSTRING)((PVOID)String);
+
+ //
+ // If a seed wasn't passed, use the 2nd byte of current time.
+ // This byte seems to be sufficiently random (by observation).
+ //
+
+ if ((*Seed) == 0) {
+ Status = NtQuerySystemTime ( &Time );
+ ASSERT(NT_SUCCESS(Status));
+
+ LocalSeed = (PUCHAR)((PVOID)&Time);
+
+ i = 1;
+
+ (*Seed) = LocalSeed[ i ];
+
+ //
+ // Occasionally, this byte could be zero. That would cause the
+ // string to become un-decodable, since 0 is the magic value that
+ // causes us to re-gen the seed. This loop makes sure that we
+ // never end up with a zero byte (unless time is zero, as well).
+ //
+
+ while ( ((*Seed) == 0) && ( i < sizeof( Time ) ) )
+ {
+ (*Seed) |= LocalSeed[ i++ ] ;
+ }
+
+ if ( (*Seed) == 0 )
+ {
+ (*Seed) = 1;
+ }
+ }
+
+ //
+ // Transform the initial byte.
+ // The funny constant just keeps the first byte from propagating
+ // into the second byte in the next step. Without a funny constant
+ // this would happen for many languages (which typically have every
+ // other byte zero.
+ //
+ //
+
+ if (S->Length >= 1) {
+ S->Buffer[0] ^= ((*Seed) | 0X43);
+ }
+
+
+ //
+ // Now transform the rest of the string
+ //
+
+ for (i=1; i<S->Length; i++) {
+
+ //
+ // There are export issues that cause us to want to
+ // keep this algorithm simple. Please don't change it
+ // without checking with JimK first. Thanks.
+ //
+
+ //
+ // In order to be compatible with zero terminated unicode strings,
+ // this algorithm is designed to not produce a wide character of
+ // zero as long a the seed is not zero.
+ //
+
+ //
+ // Simple running XOR with the previous byte and the
+ // seed value.
+ //
+
+ S->Buffer[i] ^= (S->Buffer[i-1]^(*Seed));
+
+ }
+
+
+ return;
+
+}
+
+
+VOID
+RtlRunDecodeUnicodeString(
+ UCHAR Seed,
+ PUNICODE_STRING String
+ )
+/*++
+
+Routine Description:
+
+ This function performs the inverse of the function performed
+ by RtlRunEncodeUnicodeString(). Please see RtlRunEncodeUnicodeString()
+ for details.
+
+
+Arguments:
+
+ Seed - The seed value to use in RtlRunEncodeUnicodeString().
+
+ String - The string to reveal.
+
+
+Return Value:
+
+ None - Nothing can really go wrong unless the caller passes bogus
+ parameters. In this case, the caller can catch the access
+ violation.
+
+
+--*/
+
+{
+
+ ULONG
+ i;
+
+ PSTRING
+ S;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Typecast so we can work on bytes rather than WCHARs
+ //
+
+ S = (PSTRING)((PVOID)String);
+
+
+ //
+ // Transform the end of the string
+ //
+
+ for (i=S->Length; i>1; i--) {
+
+ //
+ // a simple running XOR with the previous byte and the
+ // seed value.
+ //
+
+ S->Buffer[i-1] ^= (S->Buffer[i-2]^Seed);
+
+ }
+
+ //
+ // Finally, transform the initial byte
+ //
+
+ if (S->Length >= 1) {
+ S->Buffer[0] ^= (Seed | 0X43);
+ }
+
+
+ return;
+}
+
+
+
+VOID
+RtlEraseUnicodeString(
+ PUNICODE_STRING String
+ )
+/*++
+
+Routine Description:
+
+ This function scrubs the passed string by over-writing all
+ characters in the string. The entire string (i.e., MaximumLength)
+ is erased, not just the current length.
+
+
+Argumen ts:
+
+ String - The string to be erased.
+
+
+Return Value:
+
+ None - Nothing can really go wrong unless the caller passes bogus
+ parameters. In this case, the caller can catch the access
+ violation.
+
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ if ((String->Buffer == NULL) || (String->MaximumLength == 0)) {
+ return;
+ }
+
+ RtlZeroMemory( (PVOID)String->Buffer, (ULONG)String->MaximumLength );
+
+ String->Length = 0;
+
+ return;
+}
+
+
+
+NTSTATUS
+RtlAdjustPrivilege(
+ ULONG Privilege,
+ BOOLEAN Enable,
+ BOOLEAN Client,
+ PBOOLEAN WasEnabled
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure enables or disables a privilege process-wide.
+
+Arguments:
+
+ Privilege - The lower 32-bits of the privilege ID to be enabled or
+ disabled. The upper 32-bits is assumed to be zero.
+
+ Enable - A boolean indicating whether the privilege is to be enabled
+ or disabled. TRUE indicates the privilege is to be enabled.
+ FALSE indicates the privilege is to be disabled.
+
+ Client - A boolean indicating whether the privilege should be adjusted
+ in a client token or the process's own token. TRUE indicates
+ the client's token should be used (and an error returned if there
+ is no client token). FALSE indicates the process's token should
+ be used.
+
+ WasEnabled - points to a boolean to receive an indication of whether
+ the privilege was previously enabled or disabled. TRUE indicates
+ the privilege was previously enabled. FALSE indicates the privilege
+ was previoulsy disabled. This value is useful for returning the
+ privilege to its original state after using it.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The privilege has been sucessfully enabled or disabled.
+
+ STATUS_PRIVILEGE_NOT_HELD - The privilege is not held by the specified context.
+
+ Other status values as may be returned by:
+
+ NtOpenProcessToken()
+ NtAdjustPrivilegesToken()
+
+
+--*/
+
+{
+ NTSTATUS
+ Status,
+ TmpStatus;
+
+ HANDLE
+ Token;
+
+ LUID
+ LuidPrivilege;
+
+ PTOKEN_PRIVILEGES
+ NewPrivileges,
+ OldPrivileges;
+
+ ULONG
+ Length;
+
+ UCHAR
+ Buffer1[sizeof(TOKEN_PRIVILEGES)+
+ ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))],
+ Buffer2[sizeof(TOKEN_PRIVILEGES)+
+ ((1-ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES))];
+
+
+ RTL_PAGED_CODE();
+
+ NewPrivileges = (PTOKEN_PRIVILEGES)Buffer1;
+ OldPrivileges = (PTOKEN_PRIVILEGES)Buffer2;
+
+ //
+ // Open the appropriate token...
+ //
+
+ if (Client == TRUE) {
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+ FALSE,
+ &Token
+ );
+ } else {
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
+ &Token
+ );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+
+
+ //
+ // Initialize the privilege adjustment structure
+ //
+
+ LuidPrivilege = RtlConvertUlongToLuid(Privilege);
+
+
+ NewPrivileges->PrivilegeCount = 1;
+ NewPrivileges->Privileges[0].Luid = LuidPrivilege;
+ NewPrivileges->Privileges[0].Attributes = Enable ? SE_PRIVILEGE_ENABLED : 0;
+
+
+
+ //
+ // Adjust the privilege
+ //
+
+ Status = NtAdjustPrivilegesToken(
+ Token, // TokenHandle
+ FALSE, // DisableAllPrivileges
+ NewPrivileges, // NewPrivileges
+ sizeof(Buffer1), // BufferLength
+ OldPrivileges, // PreviousState (OPTIONAL)
+ &Length // ReturnLength
+ );
+
+
+ TmpStatus = NtClose(Token);
+ ASSERT(NT_SUCCESS(TmpStatus));
+
+
+ //
+ // Map the success code NOT_ALL_ASSIGNED to an appropriate error
+ // since we're only trying to adjust the one privilege.
+ //
+
+ if (Status == STATUS_NOT_ALL_ASSIGNED) {
+ Status = STATUS_PRIVILEGE_NOT_HELD;
+ }
+
+
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // If there are no privileges in the previous state, there were
+ // no changes made. The previous state of the privilege
+ // is whatever we tried to change it to.
+ //
+
+ if (OldPrivileges->PrivilegeCount == 0) {
+
+ (*WasEnabled) = Enable;
+
+ } else {
+
+ (*WasEnabled) =
+ (OldPrivileges->Privileges[0].Attributes & SE_PRIVILEGE_ENABLED)
+ ? TRUE : FALSE;
+ }
+ }
+
+ return(Status);
+}
+
+
+BOOLEAN
+RtlValidSid (
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure validates an SID's structure.
+
+Arguments:
+
+ Sid - Pointer to the SID structure to validate.
+
+Return Value:
+
+ BOOLEAN - TRUE if the structure of Sid is valid.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Make sure revision is SID_REVISION and sub authority count is not
+ // greater than maximum number of allowed sub-authorities.
+ //
+
+ try {
+
+ if ((((SID *)Sid)->Revision & 0x0f) == SID_REVISION) {
+ if (((SID *)Sid)->SubAuthorityCount <= SID_MAX_SUB_AUTHORITIES) {
+ return TRUE;
+ }
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return FALSE;
+ }
+
+ return FALSE;
+
+}
+
+
+
+BOOLEAN
+RtlEqualSid (
+ IN PSID Sid1,
+ IN PSID Sid2
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure tests two SID values for equality.
+
+Arguments:
+
+ Sid1, Sid2 - Supply pointers to the two SID values to compare.
+ The SID structures are assumed to be valid.
+
+Return Value:
+
+ BOOLEAN - TRUE if the value of Sid1 is equal to Sid2, and FALSE
+ otherwise.
+
+--*/
+
+{
+ ULONG SidLength;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Make sure they are the same revision
+ //
+
+ if ( ((SID *)Sid1)->Revision == ((SID *)Sid2)->Revision ) {
+
+ //
+ // Check the SubAuthorityCount first, because it's fast and
+ // can help us exit faster.
+ //
+
+ if ( *RtlSubAuthorityCountSid( Sid1 ) == *RtlSubAuthorityCountSid( Sid2 )) {
+
+ SidLength = SeLengthSid( Sid1 );
+ return( (BOOLEAN)RtlEqualMemory( Sid1, Sid2, SidLength) );
+ }
+ }
+
+ return( FALSE );
+
+}
+
+
+
+BOOLEAN
+RtlEqualPrefixSid (
+ IN PSID Sid1,
+ IN PSID Sid2
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure tests two SID prefix values for equality.
+
+ An SID prefix is the entire SID except for the last sub-authority
+ value.
+
+Arguments:
+
+ Sid1, Sid2 - Supply pointers to the two SID values to compare.
+ The SID structures are assumed to be valid.
+
+Return Value:
+
+ BOOLEAN - TRUE if the prefix value of Sid1 is equal to Sid2, and FALSE
+ otherwise.
+
+--*/
+
+
+{
+ LONG Index;
+
+ //
+ // Typecast to the opaque SID structures.
+ //
+
+ SID *ISid1 = Sid1;
+ SID *ISid2 = Sid2;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Make sure they are the same revision
+ //
+
+ if (ISid1->Revision == ISid2->Revision ) {
+
+ //
+ // Compare IdentifierAuthority values
+ //
+
+ if ( (ISid1->IdentifierAuthority.Value[0] ==
+ ISid2->IdentifierAuthority.Value[0]) &&
+ (ISid1->IdentifierAuthority.Value[1]==
+ ISid2->IdentifierAuthority.Value[1]) &&
+ (ISid1->IdentifierAuthority.Value[2] ==
+ ISid2->IdentifierAuthority.Value[2]) &&
+ (ISid1->IdentifierAuthority.Value[3] ==
+ ISid2->IdentifierAuthority.Value[3]) &&
+ (ISid1->IdentifierAuthority.Value[4] ==
+ ISid2->IdentifierAuthority.Value[4]) &&
+ (ISid1->IdentifierAuthority.Value[5] ==
+ ISid2->IdentifierAuthority.Value[5])
+ ) {
+
+ //
+ // Compare SubAuthorityCount values
+ //
+
+ if (ISid1->SubAuthorityCount == ISid2->SubAuthorityCount) {
+
+ if (ISid1->SubAuthorityCount == 0) {
+ return TRUE;
+ }
+
+ Index = 0;
+ while (Index < (ISid1->SubAuthorityCount - 1)) {
+ if ((ISid1->SubAuthority[Index]) != (ISid2->SubAuthority[Index])) {
+
+ //
+ // Found some SubAuthority values that weren't equal.
+ //
+
+ return FALSE;
+ }
+ Index += 1;
+ }
+
+ //
+ // All SubAuthority values are equal.
+ //
+
+ return TRUE;
+ }
+ }
+ }
+
+ //
+ // Either the Revision, SubAuthorityCount, or IdentifierAuthority values
+ // weren't equal.
+ //
+
+ return FALSE;
+}
+
+
+
+ULONG
+RtlLengthRequiredSid (
+ IN ULONG SubAuthorityCount
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the length, in bytes, required to store an SID
+ with the specified number of Sub-Authorities.
+
+Arguments:
+
+ SubAuthorityCount - The number of sub-authorities to be stored in the SID.
+
+Return Value:
+
+ ULONG - The length, in bytes, required to store the SID.
+
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return (8L + (4 * SubAuthorityCount));
+
+}
+
+
+NTSTATUS
+RtlAllocateAndInitializeSid(
+ IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
+ IN UCHAR SubAuthorityCount,
+ IN ULONG SubAuthority0,
+ IN ULONG SubAuthority1,
+ IN ULONG SubAuthority2,
+ IN ULONG SubAuthority3,
+ IN ULONG SubAuthority4,
+ IN ULONG SubAuthority5,
+ IN ULONG SubAuthority6,
+ IN ULONG SubAuthority7,
+ OUT PSID *Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This function allocates and initializes a sid with the specified
+ number of sub-authorities (up to 8). A sid allocated with this
+ routine must be freed using RtlFreeSid().
+
+ THIS ROUTINE IS CURRENTLY NOT CALLABLE FROM KERNEL MODE.
+
+Arguments:
+
+ IdentifierAuthority - Pointer to the Identifier Authority value to
+ set in the SID.
+
+ SubAuthorityCount - The number of sub-authorities to place in the SID.
+ This also identifies how many of the SubAuthorityN parameters
+ have meaningful values. This must contain a value from 0 through
+ 8.
+
+ SubAuthority0-7 - Provides the corresponding sub-authority value to
+ place in the SID. For example, a SubAuthorityCount value of 3
+ indicates that SubAuthority0, SubAuthority1, and SubAuthority0
+ have meaningful values and the rest are to be ignored.
+
+ Sid - Receives a pointer to the SID data structure to initialize.
+
+Return Value:
+
+ STATUS_SUCCESS - The SID has been allocated and initialized.
+
+ STATUS_NO_MEMORY - The attempt to allocate memory for the SID
+ failed.
+
+ STATUS_INVALID_SID - The number of sub-authorities specified did
+ not fall in the valid range for this api (0 through 8).
+
+
+--*/
+{
+ PISID ISid;
+
+ RTL_PAGED_CODE();
+
+ if ( SubAuthorityCount > 8 ) {
+ return( STATUS_INVALID_SID );
+ }
+
+ ISid = RtlAllocateHeap( RtlProcessHeap(), 0,
+ RtlLengthRequiredSid(SubAuthorityCount)
+ );
+ if (ISid == NULL) {
+ return(STATUS_NO_MEMORY);
+ }
+
+ ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
+ ISid->Revision = 1;
+ ISid->IdentifierAuthority = *IdentifierAuthority;
+
+ switch (SubAuthorityCount) {
+
+ case 8:
+ ISid->SubAuthority[7] = SubAuthority7;
+ case 7:
+ ISid->SubAuthority[6] = SubAuthority6;
+ case 6:
+ ISid->SubAuthority[5] = SubAuthority5;
+ case 5:
+ ISid->SubAuthority[4] = SubAuthority4;
+ case 4:
+ ISid->SubAuthority[3] = SubAuthority3;
+ case 3:
+ ISid->SubAuthority[2] = SubAuthority2;
+ case 2:
+ ISid->SubAuthority[1] = SubAuthority1;
+ case 1:
+ ISid->SubAuthority[0] = SubAuthority0;
+ case 0:
+ ;
+ }
+
+ (*Sid) = ISid;
+ return( STATUS_SUCCESS );
+
+}
+
+
+
+NTSTATUS
+RtlInitializeSid(
+ IN PSID Sid,
+ IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
+ IN UCHAR SubAuthorityCount
+ )
+/*++
+
+Routine Description:
+
+ This function initializes an SID data structure. It does not, however,
+ set the sub-authority values. This must be done separately.
+
+Arguments:
+
+ Sid - Pointer to the SID data structure to initialize.
+
+ IdentifierAuthority - Pointer to the Identifier Authority value to
+ set in the SID.
+
+ SubAuthorityCount - The number of sub-authorities that will be placed in
+ the SID (a separate action).
+
+Return Value:
+
+
+--*/
+{
+ PISID ISid;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Typecast to the opaque SID
+ //
+
+ ISid = (PISID)Sid;
+
+ if ( SubAuthorityCount > SID_MAX_SUB_AUTHORITIES ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ ISid->SubAuthorityCount = (UCHAR)SubAuthorityCount;
+ ISid->Revision = 1;
+ ISid->IdentifierAuthority = *IdentifierAuthority;
+
+ return( STATUS_SUCCESS );
+
+}
+
+
+PVOID
+RtlFreeSid(
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This function is used to free a SID previously allocated using
+ RtlAllocateAndInitializeSid().
+
+ THIS ROUTINE IS CURRENTLY NOT CALLABLE FROM KERNEL MODE.
+
+Arguments:
+
+ Sid - Pointer to the SID to free.
+
+Return Value:
+
+ None.
+
+
+--*/
+{
+ RTL_PAGED_CODE();
+
+ if (RtlFreeHeap( RtlProcessHeap(), 0, Sid ))
+ return NULL;
+ else
+ return Sid;
+}
+
+
+PSID_IDENTIFIER_AUTHORITY
+RtlIdentifierAuthoritySid(
+ IN PSID Sid
+ )
+/*++
+
+Routine Description:
+
+ This function returns the address of an SID's IdentifierAuthority field.
+
+Arguments:
+
+ Sid - Pointer to the SID data structure.
+
+Return Value:
+
+
+--*/
+{
+ PISID ISid;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Typecast to the opaque SID
+ //
+
+ ISid = (PISID)Sid;
+
+ return &(ISid->IdentifierAuthority);
+
+}
+
+PULONG
+RtlSubAuthoritySid(
+ IN PSID Sid,
+ IN ULONG SubAuthority
+ )
+/*++
+
+Routine Description:
+
+ This function returns the address of a sub-authority array element of
+ an SID.
+
+Arguments:
+
+ Sid - Pointer to the SID data structure.
+
+ SubAuthority - An index indicating which sub-authority is being specified.
+ This value is not compared against the number of sub-authorities in the
+ SID for validity.
+
+Return Value:
+
+
+--*/
+{
+ PISID ISid;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Typecast to the opaque SID
+ //
+
+ ISid = (PISID)Sid;
+
+ return &(ISid->SubAuthority[SubAuthority]);
+
+}
+
+PUCHAR
+RtlSubAuthorityCountSid(
+ IN PSID Sid
+ )
+/*++
+
+Routine Description:
+
+ This function returns the address of the sub-authority count field of
+ an SID.
+
+Arguments:
+
+ Sid - Pointer to the SID data structure.
+
+Return Value:
+
+
+--*/
+{
+ PISID ISid;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Typecast to the opaque SID
+ //
+
+ ISid = (PISID)Sid;
+
+ return &(ISid->SubAuthorityCount);
+
+}
+
+ULONG
+RtlLengthSid (
+ IN PSID Sid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the length, in bytes, of a structurally valid SID.
+
+Arguments:
+
+ Sid - Points to the SID whose length is to be returned. The
+ SID's structure is assumed to be valid.
+
+Return Value:
+
+ ULONG - The length, in bytes, of the SID.
+
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return SeLengthSid(Sid);
+}
+
+
+NTSTATUS
+RtlCopySid (
+ IN ULONG DestinationSidLength,
+ OUT PSID DestinationSid,
+ IN PSID SourceSid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the value of the source SID to the destination
+ SID.
+
+Arguments:
+
+ DestinationSidLength - Indicates the length, in bytes, of the
+ destination SID buffer.
+
+ DestinationSid - Pointer to a buffer to receive a copy of the
+ source Sid value.
+
+ SourceSid - Supplies the Sid value to be copied.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the SID was successfully copied.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the target buffer wasn't
+ large enough to receive a copy of the SID.
+
+
+--*/
+
+{
+ ULONG SidLength = SeLengthSid(SourceSid);
+
+ RTL_PAGED_CODE();
+
+ if (SidLength > DestinationSidLength) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Buffer is large enough
+ //
+
+ RtlMoveMemory( DestinationSid, SourceSid, SidLength );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlCopySidAndAttributesArray (
+ IN ULONG ArrayLength,
+ IN PSID_AND_ATTRIBUTES Source,
+ IN ULONG TargetSidBufferSize,
+ OUT PSID_AND_ATTRIBUTES TargetArrayElement,
+ OUT PSID TargetSid,
+ OUT PSID *NextTargetSid,
+ OUT PULONG RemainingTargetSidBufferSize
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the value of the source SID_AND_ATTRIBUTES array
+ to the target. The actual SID values are placed according to a separate
+ parameter. This allows multiple arrays to be merged using this service
+ to copy each.
+
+Arguments:
+
+ ArrayLength - Number of elements in the source array to copy.
+
+ Source - Pointer to the source array.
+
+ TargetSidBufferSize - Indicates the length, in bytes, of the buffer
+ to receive the actual SID values. If this value is less than
+ the actual amount needed, then STATUS_BUFFER_TOO_SMALL is returned.
+
+ TargetArrayElement - Indicates where the array elements are to be
+ copied to (but not the SID values themselves).
+
+ TargetSid - Indicates where the target SID values s are to be copied. This
+ is assumed to be ULONG aligned. Each SID value will be copied
+ into this buffer. Each SID will be ULONG aligned.
+
+ NextTargetSid - On completion, will be set to point to the ULONG
+ aligned address following the last SID copied.
+
+ RemainingTargetSidBufferSize - On completion, receives an indicatation
+ of how much of the SID buffer is still unused.
+
+
+Return Value:
+
+ STATUS_SUCCESS - The call completed successfully.
+
+ STATUS_BUFFER_TOO_SMALL - Indicates the buffer to receive the SID
+ values wasn't large enough.
+
+
+
+--*/
+
+{
+
+ ULONG Index = 0;
+ PSID NextSid = TargetSid;
+ ULONG NextSidLength;
+ ULONG AlignedSidLength;
+ ULONG RemainingLength = TargetSidBufferSize;
+
+ RTL_PAGED_CODE();
+
+ while (Index < ArrayLength) {
+
+ NextSidLength = SeLengthSid( Source[Index].Sid );
+ AlignedSidLength = (ULONG)LongAlign(NextSidLength);
+
+ if (NextSidLength > RemainingLength) {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ RemainingLength -= AlignedSidLength;
+
+ TargetArrayElement[Index].Sid = NextSid;
+ TargetArrayElement[Index].Attributes = Source[Index].Attributes;
+
+ RtlCopySid( NextSidLength, NextSid, Source[Index].Sid );
+
+ NextSid = (PSID)((ULONG)NextSid + AlignedSidLength);
+
+ Index += 1;
+
+ } //end_while
+
+ (*NextTargetSid) = NextSid;
+ (*RemainingTargetSidBufferSize) = RemainingLength;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlConvertSidToUnicodeString(
+ PUNICODE_STRING UnicodeString,
+ PSID Sid,
+ BOOLEAN AllocateDestinationString
+ )
+
+/*++
+
+Routine Description:
+
+
+ This function generates a printable unicode string representation
+ of a SID.
+
+ The resulting string will take one of two forms. If the
+ IdentifierAuthority value is not greater than 2^32, then
+ the SID will be in the form:
+
+
+ S-1-281736-12-72-9-110
+ ^ ^^ ^^ ^ ^^^
+ | | | | |
+ +-----+--+-+--+---- Decimal
+
+
+
+ Otherwise it will take the form:
+
+
+ S-1-0x173495281736-12-72-9-110
+ ^^^^^^^^^^^^^^ ^^ ^^ ^ ^^^
+ Hexidecimal | | | |
+ +--+-+--+---- Decimal
+
+
+
+
+
+
+Arguments:
+
+
+
+ UnicodeString - Returns a unicode string that is equivalent to
+ the SID. The maximum length field is only set if
+ AllocateDestinationString is TRUE.
+
+ Sid - Supplies the SID that is to be converted to unicode.
+
+ AllocateDestinationString - Supplies a flag that controls whether or
+ not this API allocates the buffer space for the destination
+ string. If it does, then the buffer must be deallocated using
+ RtlFreeUnicodeString (note that only storage for
+ DestinationString->Buffer is allocated by this API).
+
+Return Value:
+
+ SUCCESS - The conversion was successful
+
+ STATUS_INVALID_SID - The sid provided does not have a valid structure,
+ or has too many sub-authorities (more than SID_MAX_SUB_AUTHORITIES).
+
+ STATUS_NO_MEMORY - There was not sufficient memory to allocate the
+ target string. This is returned only if AllocateDestinationString
+ is specified as TRUE.
+
+ STATUS_BUFFER_OVERFLOW - This is returned only if
+ AllocateDestinationString is specified as FALSE.
+
+
+--*/
+
+{
+ NTSTATUS Status;
+ UCHAR Buffer[256];
+ UCHAR String[256];
+
+ UCHAR i;
+ ULONG Tmp;
+
+ PISID iSid = (PISID)Sid; // pointer to opaque structure
+
+ ANSI_STRING AnsiString;
+
+ RTL_PAGED_CODE();
+
+ if (RtlValidSid( Sid ) != TRUE) {
+ return(STATUS_INVALID_SID);
+ }
+
+
+ _snprintf(Buffer, sizeof(Buffer), "S-%u-", (USHORT)iSid->Revision );
+ strcpy(String, Buffer);
+
+ if ( (iSid->IdentifierAuthority.Value[0] != 0) ||
+ (iSid->IdentifierAuthority.Value[1] != 0) ){
+ _snprintf(Buffer, sizeof(Buffer), "0x%02hx%02hx%02hx%02hx%02hx%02hx",
+ (USHORT)iSid->IdentifierAuthority.Value[0],
+ (USHORT)iSid->IdentifierAuthority.Value[1],
+ (USHORT)iSid->IdentifierAuthority.Value[2],
+ (USHORT)iSid->IdentifierAuthority.Value[3],
+ (USHORT)iSid->IdentifierAuthority.Value[4],
+ (USHORT)iSid->IdentifierAuthority.Value[5] );
+ strcat(String, Buffer);
+
+ } else {
+
+ Tmp = (ULONG)iSid->IdentifierAuthority.Value[5] +
+ (ULONG)(iSid->IdentifierAuthority.Value[4] << 8) +
+ (ULONG)(iSid->IdentifierAuthority.Value[3] << 16) +
+ (ULONG)(iSid->IdentifierAuthority.Value[2] << 24);
+ _snprintf(Buffer, sizeof(Buffer), "%lu", Tmp);
+ strcat(String, Buffer);
+ }
+
+
+ for (i=0;i<iSid->SubAuthorityCount ;i++ ) {
+ _snprintf(Buffer, sizeof(Buffer), "-%lu", iSid->SubAuthority[i]);
+ strcat(String, Buffer);
+ }
+
+ //
+ // Convert the string to a Unicode String
+ //
+
+ RtlInitString(&AnsiString, (PSZ) String);
+
+ Status = RtlAnsiStringToUnicodeString( UnicodeString,
+ &AnsiString,
+ AllocateDestinationString
+ );
+
+ return(Status);
+}
+
+
+
+
+BOOLEAN
+RtlEqualLuid (
+ IN PLUID Luid1,
+ IN PLUID Luid2
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure test two LUID values for equality.
+
+ This routine is here for backwards compatibility only. New code
+ should use the macro.
+
+Arguments:
+
+ Luid1, Luid2 - Supply pointers to the two LUID values to compare.
+
+Return Value:
+
+ BOOLEAN - TRUE if the value of Luid1 is equal to Luid2, and FALSE
+ otherwise.
+
+
+--*/
+
+{
+ LUID UNALIGNED * TempLuid1;
+ LUID UNALIGNED * TempLuid2;
+
+ RTL_PAGED_CODE();
+
+ return((Luid1->HighPart == Luid2->HighPart) &&
+ (Luid1->LowPart == Luid2->LowPart));
+
+}
+
+
+VOID
+RtlCopyLuid (
+ OUT PLUID DestinationLuid,
+ IN PLUID SourceLuid
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the value of the source LUID to the
+ destination LUID.
+
+Arguments:
+
+ DestinationLuid - Receives a copy of the source Luid value.
+
+ SourceLuid - Supplies the Luid value to be copied. This LUID is
+ assumed to be structurally valid.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ (*DestinationLuid) = (*SourceLuid);
+ return;
+}
+
+VOID
+RtlCopyLuidAndAttributesArray (
+ IN ULONG ArrayLength,
+ IN PLUID_AND_ATTRIBUTES Source,
+ OUT PLUID_AND_ATTRIBUTES Target
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies the value of the source LUID_AND_ATTRIBUTES array
+ to the target.
+
+Arguments:
+
+ ArrayLength - Number of elements in the source array to copy.
+
+ Source - The source array.
+
+ Target - Indicates where the array elements are to be copied to.
+
+
+Return Value:
+
+ None.
+
+
+--*/
+
+{
+
+ ULONG Index = 0;
+
+ RTL_PAGED_CODE();
+
+ while (Index < ArrayLength) {
+
+ Target[Index] = Source[Index];
+
+ Index += 1;
+
+ } //end_while
+
+
+ return;
+
+}
+
+NTSTATUS
+RtlCreateSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN ULONG Revision
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure initializes a new "absolute format" security descriptor.
+ After the procedure call the security descriptor is initialized with no
+ system ACL, no discretionary ACL, no owner, no primary group and
+ all control flags set to false (null).
+
+Arguments:
+
+
+ SecurityDescriptor - Supplies the security descriptor to
+ initialize.
+
+ Revision - Provides the revision level to assign to the security
+ descriptor. This should be one (1) for this release.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision level provided
+ is not supported by this routine.
+
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Check the requested revision
+ //
+
+ if (Revision == SECURITY_DESCRIPTOR_REVISION) {
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ ISecurityDescriptor->Revision = SECURITY_DESCRIPTOR_REVISION;
+ ISecurityDescriptor->Sbz1 = 0;
+ *(PUSHORT)(&ISecurityDescriptor->Control) = 0;
+ ISecurityDescriptor->Owner = NULL;
+ ISecurityDescriptor->Group = NULL;
+ ISecurityDescriptor->Sacl = NULL;
+ ISecurityDescriptor->Dacl = NULL;
+
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_UNKNOWN_REVISION;
+}
+
+
+BOOLEAN
+RtlValidSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure validates a SecurityDescriptor's structure. This
+ involves validating the revision levels of each component of the
+ security descriptor.
+
+Arguments:
+
+ SecurityDescriptor - Pointer to the SECURITY_DESCRIPTOR structure
+ to validate.
+
+Return Value:
+
+ BOOLEAN - TRUE if the structure of SecurityDescriptor is valid.
+
+
+--*/
+
+{
+ PSID Owner;
+ PSID Group;
+ PACL Dacl;
+ PACL Sacl;
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ try {
+
+ //
+ // known revision ?
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return FALSE;
+ }
+
+
+ //
+ // Validate each element contained in the security descriptor
+ //
+
+ if (ISecurityDescriptor->Owner != NULL) {
+ Owner = RtlpOwnerAddrSecurityDescriptor( ISecurityDescriptor );
+ if (!RtlValidSid( Owner )) {
+ return FALSE;
+ }
+ }
+
+ if (ISecurityDescriptor->Group != NULL) {
+ Group = RtlpGroupAddrSecurityDescriptor( ISecurityDescriptor );
+ if (!RtlValidSid( Group )) {
+ return FALSE;
+ }
+ }
+
+ if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
+ (ISecurityDescriptor->Dacl != NULL) ) {
+ Dacl = RtlpDaclAddrSecurityDescriptor( ISecurityDescriptor );
+ if (!RtlValidAcl( Dacl )) {
+ return FALSE;
+ }
+ }
+
+ if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
+ (ISecurityDescriptor->Sacl != NULL) ) {
+ Sacl = RtlpSaclAddrSecurityDescriptor( ISecurityDescriptor );
+ if (!RtlValidAcl( Sacl )) {
+ return FALSE;
+ }
+ }
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ return FALSE;
+ }
+
+ //
+ // All components are valid
+ //
+
+ return TRUE;
+
+
+}
+
+
+ULONG
+RtlLengthSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the length, in bytes, necessary to capture a
+ structurally valid SECURITY_DESCRIPTOR. The length includes the length
+ of all associated data structures (like SIDs and ACLs). The length also
+ takes into account the alignment requirements of each component.
+
+ The minimum length of a security descriptor (one which has no associated
+ SIDs or ACLs) is SECURITY_DESCRIPTOR_MIN_LENGTH.
+
+
+Arguments:
+
+ SecurityDescriptor - Points to the SECURITY_DESCRIPTOR whose
+ length is to be returned. The SECURITY_DESCRIPTOR's
+ structure is assumed to be valid.
+
+Return Value:
+
+ ULONG - The length, in bytes, of the SECURITY_DESCRIPTOR.
+
+
+--*/
+
+{
+ ULONG sum;
+
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // The length is the sum of the following:
+ //
+ // SECURITY_DESCRIPTOR_MIN_LENGTH (or sizeof(SECURITY_DESCRIPTOR))
+ // length of Owner SID (if present)
+ // length of Group SID (if present)
+ // length of Discretionary ACL (if present and non-null)
+ // length of System ACL (if present and non-null)
+ //
+
+ sum = sizeof(SECURITY_DESCRIPTOR);
+
+ //
+ // Add in length of Owner SID
+ //
+
+ if (ISecurityDescriptor->Owner != NULL) {
+ sum += (ULONG)(LongAlign(SeLengthSid(RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor))));
+ }
+
+ //
+ // Add in length of Group SID
+ //
+
+ if (ISecurityDescriptor->Group != NULL) {
+ sum += (ULONG)(LongAlign(SeLengthSid(RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor))));
+ }
+
+ //
+ // Add in used length of Discretionary ACL
+ //
+
+ if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
+ (ISecurityDescriptor->Dacl != NULL) ) {
+
+ sum += (ULONG)(LongAlign(RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor)->AclSize) );
+ }
+
+ //
+ // Add in used length of System Acl
+ //
+
+ if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
+ (ISecurityDescriptor->Sacl != NULL) ) {
+ sum += (ULONG)(LongAlign(RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor)->AclSize) );
+ }
+
+ return sum;
+}
+
+
+ULONG
+RtlLengthUsedSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns the length, in bytes, in use in a structurally valid
+ SECURITY_DESCRIPTOR.
+
+ This is the number of bytes necessary to capture the security descriptor,
+ which may be less the the current actual length of the security descriptor
+ (RtlLengthSecurityDescriptor() is used to retrieve the actual length).
+
+ Notice that the used length and actual length may differ if either the SACL
+ or DACL include padding bytes.
+
+ The length includes the length of all associated data structures (like SIDs
+ and ACLs). The length also takes into account the alignment requirements
+ of each component.
+
+ The minimum length of a security descriptor (one which has no associated
+ SIDs or ACLs) is SECURITY_DESCRIPTOR_MIN_LENGTH.
+
+
+Arguments:
+
+ SecurityDescriptor - Points to the SECURITY_DESCRIPTOR whose used
+ length is to be returned. The SECURITY_DESCRIPTOR's
+ structure is assumed to be valid.
+
+Return Value:
+
+ ULONG - Number of bytes of the SECURITY_DESCRIPTOR that are in use.
+
+
+--*/
+
+{
+ ULONG sum;
+
+ ACL_SIZE_INFORMATION AclSize;
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = (SECURITY_DESCRIPTOR *)SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // The length is the sum of the following:
+ //
+ // SECURITY_DESCRIPTOR_MIN_LENGTH (or sizeof(SECURITY_DESCRIPTOR))
+ // length of Owner SID (if present)
+ // length of Group SID (if present)
+ // length of Discretionary ACL (if present and non-null)
+ // length of System ACL (if present and non-null)
+ //
+
+ sum = sizeof(SECURITY_DESCRIPTOR);
+
+ //
+ // Add in length of Owner SID
+ //
+
+ if (ISecurityDescriptor->Owner != NULL) {
+ sum += (ULONG)(LongAlign(SeLengthSid(RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor))));
+ }
+
+ //
+ // Add in length of Group SID
+ //
+
+ if (ISecurityDescriptor->Group != NULL) {
+ sum += (ULONG)(LongAlign(SeLengthSid(RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor))));
+ }
+
+ //
+ // Add in used length of Discretionary ACL
+ //
+
+ if ( (ISecurityDescriptor->Control & SE_DACL_PRESENT) &&
+ (ISecurityDescriptor->Dacl != NULL) ) {
+
+ RtlQueryInformationAcl( RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor),
+ (PVOID)&AclSize,
+ sizeof(AclSize),
+ AclSizeInformation );
+
+ sum += (ULONG)(LongAlign(AclSize.AclBytesInUse));
+ }
+
+ //
+ // Add in used length of System Acl
+ //
+
+ if ( (ISecurityDescriptor->Control & SE_SACL_PRESENT) &&
+ (ISecurityDescriptor->Sacl != NULL) ) {
+
+ RtlQueryInformationAcl( RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor),
+ (PVOID)&AclSize,
+ sizeof(AclSize),
+ AclSizeInformation );
+
+ sum += (ULONG)(LongAlign(AclSize.AclBytesInUse));
+ }
+
+ return sum;
+}
+
+
+
+NTSTATUS
+RtlSetAttributesSecurityDescriptor(
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN SECURITY_DESCRIPTOR_CONTROL Control,
+ IN OUT PULONG Revision
+ )
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Always return the revision value - even if this isn't a valid
+ // security descriptor
+ //
+
+ *Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;
+
+ if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
+ != SECURITY_DESCRIPTOR_REVISION ) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Only allow setting SE_SERVER_SECURITY and SE_DACL_UNTRUSTED
+ //
+
+ if ( Control & (SE_SERVER_SECURITY | SE_DACL_UNTRUSTED) != Control ) {
+ return STATUS_INVALID_PARAMETER ;
+ }
+
+ ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control |= Control;
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+RtlGetControlSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PSECURITY_DESCRIPTOR_CONTROL Control,
+ OUT PULONG Revision
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure retrieves the control information from a security descriptor.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor.
+
+ Control - Receives the control information.
+
+ Revision - Receives the revision of the security descriptor.
+ This value will always be returned, even if an error
+ is returned by this routine.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ //
+ // Always return the revision value - even if this isn't a valid
+ // security descriptor
+ //
+
+ *Revision = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision;
+
+
+ if ( ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Revision
+ != SECURITY_DESCRIPTOR_REVISION ) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+
+ *Control = ((SECURITY_DESCRIPTOR *)SecurityDescriptor)->Control;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlSetDaclSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN BOOLEAN DaclPresent,
+ IN PACL Dacl OPTIONAL,
+ IN BOOLEAN DaclDefaulted OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure sets the discretionary ACL information of an absolute
+ format security descriptor. If there is already a discretionary ACL
+ present in the security descriptor, it is superseded.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor to be which
+ the discretionary ACL is to be added.
+
+ DaclPresent - If FALSE, indicates the DaclPresent flag in the
+ security descriptor should be set to FALSE. In this case,
+ the remaining optional parameters are ignored. Otherwise,
+ the DaclPresent control flag in the security descriptor is
+ set to TRUE and the remaining optional parameters are not
+ ignored.
+
+ Dacl - Supplies the discretionary ACL for the security
+ descriptor. If this optional parameter is not passed, then a
+ null ACL is assigned to the security descriptor. A null
+ discretionary ACL unconditionally grants access. The ACL is
+ referenced by, not copied into, by the security descriptor.
+
+ DaclDefaulted - When set, indicates the discretionary ACL was
+ picked up from some default mechanism (rather than explicitly
+ specified by a user). This value is set in the DaclDefaulted
+ control flag in the security descriptor. If this optional
+ parameter is not passed, then the DaclDefaulted flag will be
+ cleared.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+ STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
+ is not an absolute format security descriptor.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Make sure the descriptor is absolute format
+ //
+
+ if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
+ return STATUS_INVALID_SECURITY_DESCR;
+ }
+
+ //
+ // Assign the DaclPresent flag value passed
+ //
+
+
+ if (DaclPresent) {
+
+ ISecurityDescriptor->Control |= SE_DACL_PRESENT;
+
+ //
+ // Assign the ACL address if passed, otherwise set to null.
+ //
+
+ ISecurityDescriptor->Dacl = NULL;
+ if (ARGUMENT_PRESENT(Dacl)) {
+ ISecurityDescriptor->Dacl = Dacl;
+ }
+
+
+
+
+ //
+ // Assign DaclDefaulted flag if passed, otherwise clear it.
+ //
+
+ ISecurityDescriptor->Control &= ~SE_DACL_DEFAULTED;
+ if (DaclDefaulted == TRUE) {
+ ISecurityDescriptor->Control |= SE_DACL_DEFAULTED;
+ }
+ } else {
+
+ ISecurityDescriptor->Control &= ~SE_DACL_PRESENT;
+
+ }
+
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlGetDaclSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PBOOLEAN DaclPresent,
+ OUT PACL *Dacl,
+ OUT PBOOLEAN DaclDefaulted
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure retrieves the discretionary ACL information of a
+ security descriptor.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor.
+
+ DaclPresent - If TRUE, indicates that the security descriptor
+ does contain a discretionary ACL. In this case, the
+ remaining OUT parameters will receive valid values.
+ Otherwise, the security descriptor does not contain a
+ discretionary ACL and the remaining OUT parameters will not
+ receive valid values.
+
+ Dacl - This value is returned only if the value returned for the
+ DaclPresent flag is TRUE. In this case, the Dacl parameter
+ receives the address of the security descriptor's
+ discretionary ACL. If this value is returned as null, then
+ the security descriptor has a null discretionary ACL.
+
+ DaclDefaulted - This value is returned only if the value returned
+ for the DaclPresent flag is TRUE. In this case, the
+ DaclDefaulted parameter receives the value of the security
+ descriptor's DaclDefaulted control flag.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+
+--*/
+
+{
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Assign the DaclPresent flag value
+ //
+
+ *DaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_PRESENT );
+
+ if (*DaclPresent) {
+
+ //
+ // Assign the ACL address.
+ //
+
+ *Dacl = RtlpDaclAddrSecurityDescriptor(ISecurityDescriptor);
+
+ //
+ // Assign DaclDefaulted flag.
+ //
+
+ *DaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_DACL_DEFAULTED );
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlSetSaclSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN BOOLEAN SaclPresent,
+ IN PACL Sacl OPTIONAL,
+ IN BOOLEAN SaclDefaulted OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure sets the system ACL information of an absolute security
+ descriptor. If there is already a system ACL present in the
+ security descriptor, it is superseded.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor to be which
+ the system ACL is to be added.
+
+ SaclPresent - If FALSE, indicates the SaclPresent flag in the
+ security descriptor should be set to FALSE. In this case,
+ the remaining optional parameters are ignored. Otherwise,
+ the SaclPresent control flag in the security descriptor is
+ set to TRUE and the remaining optional parameters are not
+ ignored.
+
+ Sacl - Supplies the system ACL for the security descriptor. If
+ this optional parameter is not passed, then a null ACL is
+ assigned to the security descriptor. The ACL is referenced
+ by, not copied into, by the security descriptor.
+
+ SaclDefaulted - When set, indicates the system ACL was picked up
+ from some default mechanism (rather than explicitly specified
+ by a user). This value is set in the SaclDefaulted control
+ flag in the security descriptor. If this optional parameter
+ is not passed, then the SaclDefaulted flag will be cleared.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+ STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
+ is not an absolute format security descriptor.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Make sure the descriptor is absolute format
+ //
+
+ if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
+ return STATUS_INVALID_SECURITY_DESCR;
+ }
+
+ //
+ // Assign the SaclPresent flag value passed
+ //
+
+
+ if (SaclPresent) {
+
+ ISecurityDescriptor->Control |= SE_SACL_PRESENT;
+
+ //
+ // Assign the ACL address if passed, otherwise set to null.
+ //
+
+ ISecurityDescriptor->Sacl = NULL;
+ if (ARGUMENT_PRESENT(Sacl)) {
+ ISecurityDescriptor->Sacl = Sacl;
+ }
+
+ //
+ // Assign SaclDefaulted flag if passed, otherwise clear it.
+ //
+
+ ISecurityDescriptor->Control &= ~ SE_SACL_DEFAULTED;
+ if (ARGUMENT_PRESENT(SaclDefaulted)) {
+ ISecurityDescriptor->Control |= SE_SACL_DEFAULTED;
+ }
+ } else {
+
+ ISecurityDescriptor->Control &= ~SE_SACL_PRESENT;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlGetSaclSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PBOOLEAN SaclPresent,
+ OUT PACL *Sacl,
+ OUT PBOOLEAN SaclDefaulted
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure retrieves the system ACL information of a security
+ descriptor.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor.
+
+ SaclPresent - If TRUE, indicates that the security descriptor
+ does contain a system ACL. In this case, the remaining OUT
+ parameters will receive valid values. Otherwise, the
+ security descriptor does not contain a system ACL and the
+ remaining OUT parameters will not receive valid values.
+
+ Sacl - This value is returned only if the value returned for the
+ SaclPresent flag is TRUE. In this case, the Sacl parameter
+ receives the address of the security descriptor's system ACL.
+ If this value is returned as null, then the security
+ descriptor has a null system ACL.
+
+ SaclDefaulted - This value is returned only if the value returned
+ for the SaclPresent flag is TRUE. In this case, the
+ SaclDefaulted parameter receives the value of the security
+ descriptor's SaclDefaulted control flag.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Assign the SaclPresent flag value
+ //
+
+ *SaclPresent = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_PRESENT );
+
+ if (*SaclPresent) {
+
+ //
+ // Assign the ACL address.
+ //
+
+ *Sacl = RtlpSaclAddrSecurityDescriptor(ISecurityDescriptor);
+
+ //
+ // Assign SaclDefaulted flag.
+ //
+
+ *SaclDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_SACL_DEFAULTED );
+
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlSetOwnerSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSID Owner OPTIONAL,
+ IN BOOLEAN OwnerDefaulted OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure sets the owner information of an absolute security
+ descriptor. If there is already an owner present in the security
+ descriptor, it is superseded.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor in which
+ the owner is to be set. If the security descriptor already
+ includes an owner, it will be superseded by the new owner.
+
+ Owner - Supplies the owner SID for the security descriptor. If
+ this optional parameter is not passed, then the owner is
+ cleared (indicating the security descriptor has no owner).
+ The SID is referenced by, not copied into, the security
+ descriptor.
+
+ OwnerDefaulted - When set, indicates the owner was picked up from
+ some default mechanism (rather than explicitly specified by a
+ user). This value is set in the OwnerDefaulted control flag
+ in the security descriptor. If this optional parameter is
+ not passed, then the SaclDefaulted flag will be cleared.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+ STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
+ is not an absolute format security descriptor.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Make sure the descriptor is absolute format
+ //
+
+ if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
+ return STATUS_INVALID_SECURITY_DESCR;
+ }
+
+ //
+ // Assign the Owner field if passed, otherwise clear it.
+ //
+
+ ISecurityDescriptor->Owner = NULL;
+ if (ARGUMENT_PRESENT(Owner)) {
+ ISecurityDescriptor->Owner = Owner;
+ }
+
+ //
+ // Assign the OwnerDefaulted flag if passed, otherwise clear it.
+ //
+
+ ISecurityDescriptor->Control &= ~SE_OWNER_DEFAULTED;
+ if (OwnerDefaulted == TRUE) {
+ ISecurityDescriptor->Control |= SE_OWNER_DEFAULTED;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlGetOwnerSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PSID *Owner,
+ OUT PBOOLEAN OwnerDefaulted
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure retrieves the owner information of a security
+ descriptor.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor.
+
+ Owner - Receives a pointer to the owner SID. If the security
+ descriptor does not currently contain an owner, then this
+ value will be returned as null. In this case, the remaining
+ OUT parameters are not given valid return values. Otherwise,
+ this parameter points to an SID and the remaining OUT
+ parameters are provided valid return values.
+
+ OwnerDefaulted - This value is returned only if the value
+ returned for the Owner parameter is not null. In this case,
+ the OwnerDefaulted parameter receives the value of the
+ security descriptor's OwnerDefaulted control flag.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Return the Owner field value.
+ //
+
+ *Owner = RtlpOwnerAddrSecurityDescriptor(ISecurityDescriptor);
+
+ //
+ // Return the OwnerDefaulted flag value.
+ //
+
+ *OwnerDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_OWNER_DEFAULTED );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlSetGroupSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ IN PSID Group OPTIONAL,
+ IN BOOLEAN GroupDefaulted OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure sets the primary group information of an absolute security
+ descriptor. If there is already an primary group present in the
+ security descriptor, it is superseded.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor in which
+ the primary group is to be set. If the security descriptor
+ already includes a primary group, it will be superseded by
+ the new group.
+
+ Group - Supplies the primary group SID for the security
+ descriptor. If this optional parameter is not passed, then
+ the primary group is cleared (indicating the security
+ descriptor has no primary group). The SID is referenced by,
+ not copied into, the security descriptor.
+
+ GroupDefaulted - When set, indicates the owner was picked up from
+ some default mechanism (rather than explicitly specified by a
+ user). This value is set in the OwnerDefaulted control flag
+ in the security descriptor. If this optional parameter is
+ not passed, then the SaclDefaulted flag will be cleared.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+ STATUS_INVALID_SECURITY_DESCR - Indicates the security descriptor
+ is not an absolute format security descriptor.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor = SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Make sure the descriptor is absolute format
+ //
+
+ if (ISecurityDescriptor->Control & SE_SELF_RELATIVE) {
+ return STATUS_INVALID_SECURITY_DESCR;
+ }
+
+ //
+ // Assign the Group field if passed, otherwise clear it.
+ //
+
+ ISecurityDescriptor->Group = NULL;
+ if (ARGUMENT_PRESENT(Group)) {
+ ISecurityDescriptor->Group = Group;
+ }
+
+ //
+ // Assign the GroupDefaulted flag if passed, otherwise clear it.
+ //
+
+ ISecurityDescriptor->Control &= ~SE_GROUP_DEFAULTED;
+ if (ARGUMENT_PRESENT(GroupDefaulted)) {
+ ISecurityDescriptor->Control |= SE_GROUP_DEFAULTED;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlGetGroupSecurityDescriptor (
+ IN PSECURITY_DESCRIPTOR SecurityDescriptor,
+ OUT PSID *Group,
+ OUT PBOOLEAN GroupDefaulted
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure retrieves the primary group information of a
+ security descriptor.
+
+Arguments:
+
+ SecurityDescriptor - Supplies the security descriptor.
+
+ Group - Receives a pointer to the primary group SID. If the
+ security descriptor does not currently contain a primary
+ group, then this value will be returned as null. In this
+ case, the remaining OUT parameters are not given valid return
+ values. Otherwise, this parameter points to an SID and the
+ remaining OUT parameters are provided valid return values.
+
+ GroupDefaulted - This value is returned only if the value
+ returned for the Group parameter is not null. In this case,
+ the GroupDefaulted parameter receives the value of the
+ security descriptor's GroupDefaulted control flag.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the call completed successfully.
+
+ STATUS_UNKNOWN_REVISION - Indicates the revision of the security
+ descriptor is not known to the routine. It may be a newer
+ revision than the routine knows about.
+
+
+--*/
+
+{
+
+ //
+ // Typecast to the opaque SECURITY_DESCRIPTOR structure.
+ //
+
+ SECURITY_DESCRIPTOR *ISecurityDescriptor =
+ (SECURITY_DESCRIPTOR *)SecurityDescriptor;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Check the revision
+ //
+
+ if (ISecurityDescriptor->Revision != SECURITY_DESCRIPTOR_REVISION) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+ //
+ // Return the Group field value.
+ //
+
+ *Group = RtlpGroupAddrSecurityDescriptor(ISecurityDescriptor);
+
+ //
+ // Return the GroupDefaulted flag value.
+ //
+
+ *GroupDefaulted = RtlpAreControlBitsSet( ISecurityDescriptor, SE_GROUP_DEFAULTED );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+BOOLEAN
+RtlAreAllAccessesGranted(
+ IN ACCESS_MASK GrantedAccess,
+ IN ACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to check a desired access mask against a
+ granted access mask. It is used by the Object Management
+ component when dereferencing a handle.
+
+Arguments:
+
+ GrantedAccess - Specifies the granted access mask.
+
+ DesiredAccess - Specifies the desired access mask.
+
+Return Value:
+
+ BOOLEAN - TRUE if the GrantedAccess mask has all the bits set
+ that the DesiredAccess mask has set. That is, TRUE is
+ returned if all of the desired accesses have been granted.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return ((BOOLEAN)((~(GrantedAccess) & (DesiredAccess)) == 0));
+}
+
+
+BOOLEAN
+RtlAreAnyAccessesGranted(
+ IN ACCESS_MASK GrantedAccess,
+ IN ACCESS_MASK DesiredAccess
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to test whether any of a set of desired
+ accesses are granted by a granted access mask. It is used by
+ components other than the the Object Management component for
+ checking access mask subsets.
+
+Arguments:
+
+ GrantedAccess - Specifies the granted access mask.
+
+ DesiredAccess - Specifies the desired access mask.
+
+Return Value:
+
+ BOOLEAN - TRUE if the GrantedAccess mask contains any of the bits
+ specified in the DesiredAccess mask. That is, if any of the
+ desired accesses have been granted, TRUE is returned.
+
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+ return ((BOOLEAN)(((GrantedAccess) & (DesiredAccess)) != 0));
+}
+
+
+VOID
+RtlMapGenericMask(
+ IN OUT PACCESS_MASK AccessMask,
+ IN PGENERIC_MAPPING GenericMapping
+ )
+
+/*++
+
+Routine Description:
+
+ This routine maps all generic accesses in the provided access mask
+ to specific and standard accesses according to the provided
+ GenericMapping.
+
+Arguments:
+
+ AccessMask - Points to the access mask to be mapped.
+
+ GenericMapping - The mapping of generic to specific and standard
+ access types.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RTL_PAGED_CODE();
+
+// //
+// // Make sure the pointer is properly aligned
+// //
+//
+// ASSERT( ((ULONG)AccessMask >> 2) << 2 == (ULONG)AccessMask );
+
+ if (*AccessMask & GENERIC_READ) {
+
+ *AccessMask |= GenericMapping->GenericRead;
+ }
+
+ if (*AccessMask & GENERIC_WRITE) {
+
+ *AccessMask |= GenericMapping->GenericWrite;
+ }
+
+ if (*AccessMask & GENERIC_EXECUTE) {
+
+ *AccessMask |= GenericMapping->GenericExecute;
+ }
+
+ if (*AccessMask & GENERIC_ALL) {
+
+ *AccessMask |= GenericMapping->GenericAll;
+ }
+
+ //
+ // Now clear the generic flags
+ //
+
+ *AccessMask &= ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
+
+ return;
+}
+
+NTSTATUS
+RtlImpersonateSelf(
+ IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel
+ )
+
+/*++
+
+Routine Description:
+
+ This routine may be used to obtain an Impersonation token representing
+ your own process's context. This may be useful for enabling a privilege
+ for a single thread rather than for the entire process; or changing
+ the default DACL for a single thread.
+
+ The token is assigned to the callers thread.
+
+
+
+Arguments:
+
+ ImpersonationLevel - The level to make the impersonation token.
+
+
+
+Return Value:
+
+ STATUS_SUCCESS - The thread is now impersonating the calling process.
+
+ Other - Status values returned by:
+
+ NtOpenProcessToken()
+ NtDuplicateToken()
+ NtSetInformationThread()
+
+--*/
+
+{
+ NTSTATUS
+ Status,
+ IgnoreStatus;
+
+ HANDLE
+ Token1,
+ Token2;
+
+ OBJECT_ATTRIBUTES
+ ObjectAttributes;
+
+ SECURITY_QUALITY_OF_SERVICE
+ Qos;
+
+
+ RTL_PAGED_CODE();
+
+ InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
+
+ Qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ Qos.ImpersonationLevel = ImpersonationLevel;
+ Qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ Qos.EffectiveOnly = FALSE;
+ ObjectAttributes.SecurityQualityOfService = &Qos;
+
+ Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_DUPLICATE, &Token1 );
+
+ if (NT_SUCCESS(Status)) {
+ Status = NtDuplicateToken(
+ Token1,
+ TOKEN_IMPERSONATE,
+ &ObjectAttributes,
+ FALSE, //EffectiveOnly
+ TokenImpersonation,
+ &Token2
+ );
+ if (NT_SUCCESS(Status)) {
+ Status = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ &Token2,
+ sizeof(HANDLE)
+ );
+
+ IgnoreStatus = NtClose( Token2 );
+ }
+
+
+ IgnoreStatus = NtClose( Token1 );
+ }
+
+
+ return(Status);
+
+}
+
+#ifndef WIN16
+
+
+
+BOOLEAN
+RtlpValidOwnerSubjectContext(
+ IN HANDLE Token,
+ IN PSID Owner,
+ IN BOOLEAN ServerObject,
+ OUT PNTSTATUS ReturnStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine checks to see whether the provided SID is one the subject
+ is authorized to assign as the owner of objects.
+
+Arguments:
+
+ Token - Points to the subject's effective token
+
+ Owner - Points to the SID to be checked.
+
+ ServerObject - Boolean indicating whether or not this is a server
+ object, meaning it is protected by a primary-client combination.
+
+ ReturnStatus - Status to be passed back to the caller on failure.
+
+Return Value:
+
+ FALSE on failure.
+
+--*/
+
+{
+
+ ULONG Index;
+ BOOLEAN Found;
+ ULONG ReturnLength;
+ PTOKEN_GROUPS GroupIds = NULL;
+ PTOKEN_USER UserId = NULL;
+ BOOLEAN rc = FALSE;
+ PVOID HeapHandle;
+ HANDLE TokenToUse;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Get the handle to the current process heap
+ //
+
+ if ( Owner == NULL ) {
+ return(FALSE);
+ }
+
+ //
+ // If it's not a server object, check the owner against the contents of the
+ // client token. If it is a server object, the owner must be valid in the
+ // primary token.
+ //
+
+ if (!ServerObject) {
+
+ TokenToUse = Token;
+
+ } else {
+
+ *ReturnStatus = NtOpenProcessToken(
+ NtCurrentProcess(),
+ TOKEN_QUERY,
+ &TokenToUse
+ );
+
+ if (!NT_SUCCESS( *ReturnStatus )) {
+ return( FALSE );
+ }
+ }
+
+ HeapHandle = RtlProcessHeap();
+
+ //
+ // Get the User from the Token
+ //
+
+ *ReturnStatus = NtQueryInformationToken(
+ TokenToUse,
+ TokenUser,
+ UserId,
+ 0,
+ &ReturnLength
+ );
+
+ if (!NT_SUCCESS( *ReturnStatus ) && (STATUS_BUFFER_TOO_SMALL != *ReturnStatus)) {
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+ return( FALSE );
+
+ }
+
+ UserId = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
+
+ if (UserId == NULL) {
+
+ *ReturnStatus = STATUS_NO_MEMORY;
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+
+ return( FALSE );
+ }
+
+ *ReturnStatus = NtQueryInformationToken(
+ TokenToUse,
+ TokenUser,
+ UserId,
+ ReturnLength,
+ &ReturnLength
+ );
+
+ if (!NT_SUCCESS( *ReturnStatus )) {
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+ return( FALSE );
+ }
+
+ if ( RtlEqualSid( Owner, UserId->User.Sid ) ) {
+
+ RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+ return( TRUE );
+ }
+
+ RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
+
+ //
+ // Get the groups from the Token
+ //
+
+ *ReturnStatus = NtQueryInformationToken(
+ TokenToUse,
+ TokenGroups,
+ GroupIds,
+ 0,
+ &ReturnLength
+ );
+
+ if (!NT_SUCCESS( *ReturnStatus ) && (STATUS_BUFFER_TOO_SMALL != *ReturnStatus)) {
+
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+ return( FALSE );
+ }
+
+ GroupIds = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
+
+ if (GroupIds == NULL) {
+
+ *ReturnStatus = STATUS_NO_MEMORY;
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+ return( FALSE );
+ }
+
+ *ReturnStatus = NtQueryInformationToken(
+ TokenToUse,
+ TokenGroups,
+ GroupIds,
+ ReturnLength,
+ &ReturnLength
+ );
+
+ if (ServerObject) {
+ NtClose( TokenToUse );
+ }
+
+ if (!NT_SUCCESS( *ReturnStatus )) {
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+ return( FALSE );
+ }
+
+ //
+ // Walk through the list of group IDs looking for a match to
+ // the specified SID. If one is found, make sure it may be
+ // assigned as an owner.
+ //
+ // This code is similar to that performed to set the default
+ // owner of a token (NtSetInformationToken).
+ //
+
+ Index = 0;
+ while (Index < GroupIds->GroupCount) {
+
+ Found = RtlEqualSid(
+ Owner,
+ GroupIds->Groups[Index].Sid
+ );
+
+ if ( Found ) {
+
+ if ( RtlpIdAssignableAsOwner(GroupIds->Groups[Index])) {
+
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+ return( TRUE );
+
+ } else {
+
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+ return( FALSE );
+
+ } //endif assignable
+
+ } //endif Found
+
+ Index++;
+
+ } //endwhile
+
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+
+ return ( FALSE );
+}
+
+#if 0
+
+BOOLEAN
+RtlpValidOwnerSubjectContext(
+ IN HANDLE Token,
+ IN PSID Owner,
+ BOOLEAN Dummy,
+ OUT PNTSTATUS ReturnStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine checks to see whether the provided SID is one the subject
+ is authorized to assign as the owner of objects.
+
+Arguments:
+
+ Token - Points to the subject's effective token
+
+ Owner - Points to the SID to be checked.
+
+
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+
+ ULONG Index;
+ BOOLEAN Found;
+ ULONG ReturnLength;
+ PTOKEN_GROUPS GroupIds = NULL;
+ PTOKEN_USER UserId = NULL;
+ BOOLEAN rc = FALSE;
+ NTSTATUS Status;
+ PVOID HeapHandle;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Get the handle to the current process heap
+ //
+
+ if ( Owner == NULL ) {
+ return(FALSE);
+ }
+
+ HeapHandle = RtlProcessHeap();
+
+ *ReturnStatus = STATUS_SUCCESS;
+
+ //
+ // Get the User from the Token
+ //
+
+ Status = NtQueryInformationToken(
+ Token, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+ UserId = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
+
+ if (UserId == NULL) {
+
+ *ReturnStatus = STATUS_NO_MEMORY;
+ return( FALSE );
+ }
+
+
+ Status = NtQueryInformationToken(
+ Token, // Handle
+ TokenUser, // TokenInformationClass
+ UserId, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ *ReturnStatus = Status;
+ return( FALSE );
+ }
+
+ if ( RtlEqualSid( Owner, UserId->User.Sid ) ) {
+
+ RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
+ return( TRUE );
+ }
+
+ RtlFreeHeap( HeapHandle, 0, (PVOID)UserId );
+
+ //
+ // Get the groups from the Token
+ //
+
+ Status = NtQueryInformationToken(
+ Token, // Handle
+ TokenGroups, // TokenInformationClass
+ GroupIds, // TokenInformation
+ 0, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+ ASSERT(Status == STATUS_BUFFER_TOO_SMALL);
+
+
+ GroupIds = RtlAllocateHeap( HeapHandle, 0, ReturnLength );
+
+ if (GroupIds == NULL) {
+
+ *ReturnStatus = STATUS_NO_MEMORY;
+ return( FALSE );
+
+ }
+
+ Status = NtQueryInformationToken(
+ Token, // Handle
+ TokenGroups, // TokenInformationClass
+ GroupIds, // TokenInformation
+ ReturnLength, // TokenInformationLength
+ &ReturnLength // ReturnLength
+ );
+
+
+ if (!NT_SUCCESS( Status )) {
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+ *ReturnStatus = Status;
+ return( FALSE );
+ }
+
+ //
+ // Walk through the list of group IDs looking for a match to
+ // the specified SID. If one is found, make sure it may be
+ // assigned as an owner.
+ //
+ // This code is similar to that performed to set the default
+ // owner of a token (NtSetInformationToken).
+ //
+
+ Index = 0;
+ while (Index < GroupIds->GroupCount) {
+
+ Found = RtlEqualSid(
+ Owner,
+ GroupIds->Groups[Index].Sid
+ );
+
+ if ( Found ) {
+
+ if ( RtlpIdAssignableAsOwner(GroupIds->Groups[Index])) {
+
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+ return( TRUE );
+
+ } else {
+
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+ return( FALSE );
+
+ } //endif assignable
+
+
+ } //endif Found
+
+
+ Index++;
+
+ } //endwhile
+
+ RtlFreeHeap( HeapHandle, 0, GroupIds );
+
+ return ( FALSE );
+
+}
+
+#endif // WIN16
+
+
+#endif
+
+#if 0
+
+
+BOOLEAN
+RtlpContainsCreatorOwnerSid(
+ PKNOWN_ACE Ace
+ )
+/*++
+
+Routine Description:
+
+ Tests to see if the specified ACE contains the CreatorOwnerSid.
+
+Arguments:
+
+ Ace - Pointer to the ACE whose SID is be compared to the Creator Sid.
+ This ACE is assumed to be valid and a known ACE type.
+
+Return Value:
+
+ TRUE - The creator sid is in the ACE.
+
+ FALSE - The creator sid is not in the ACE.
+
+
+--*/
+{
+
+ BOOLEAN IsEqual;
+
+
+ ULONG CreatorSid[CREATOR_SID_SIZE];
+
+
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ RTL_PAGED_CODE();
+
+ //
+ // This is gross and ugly, but it's better than allocating
+ // virtual memory to hold the CreatorSid, because that can
+ // fail, and propogating the error back is a tremendous pain
+ //
+
+ ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
+
+ *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
+
+ IsEqual = RtlEqualSid(&Ace->SidStart, (PSID)CreatorSid );
+
+ return( IsEqual );
+
+}
+
+
+BOOLEAN
+RtlpContainsCreatorGroupSid(
+ PKNOWN_ACE Ace
+ )
+/*++
+
+Routine Description:
+
+ Tests to see if the specified ACE contains the CreatorGroupSid.
+
+Arguments:
+
+ Ace - Pointer to the ACE whose SID is be compared to the Creator Sid.
+ This ACE is assumed to be valid and a known ACE type.
+
+Return Value:
+
+ TRUE - The creator sid is in the ACE.
+
+ FALSE - The creator sid is not in the ACE.
+
+
+--*/
+{
+
+ BOOLEAN IsEqual;
+
+
+ ULONG CreatorSid[CREATOR_SID_SIZE];
+
+
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ RTL_PAGED_CODE();
+
+ //
+ // This is gross and ugly, but it's better than allocating
+ // virtual memory to hold the CreatorSid, because that can
+ // fail, and propogating the error back is a tremendous pain
+ //
+
+ ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
+
+ *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
+
+ IsEqual = RtlEqualSid(&Ace->SidStart, (PSID)CreatorSid );
+
+ return( IsEqual );
+}
+
+
+BOOLEAN
+RtlpContainsCreatorOwnerServerSid(
+ PKNOWN_COMPOUND_ACE Ace
+ )
+/*++
+
+Routine Description:
+
+ Tests to see if the specified ACE contains the CreatorClientSid.
+
+Arguments:
+
+ Ace - Pointer to the compound ACE whose Client SID is be compared to the
+ Creator Client Sid. This ACE is assumed to be valid and a known
+ compound ACE type.
+
+Return Value:
+
+ TRUE - The creator sid is in the ACE.
+
+ FALSE - The creator sid is not in the ACE.
+
+
+--*/
+{
+ BOOLEAN IsEqual;
+ ULONG CreatorSid[CREATOR_SID_SIZE];
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ RTL_PAGED_CODE();
+
+ //
+ // This is gross and ugly, but it's better than allocating
+ // virtual memory to hold the ClientSid, because that can
+ // fail, and propogating the error back is a tremendous pain
+ //
+
+ ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
+
+ //
+ // Allocate and initialize the universal SID
+ //
+
+ RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
+ *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;
+
+ IsEqual = RtlEqualSid(RtlCompoundAceClientSid( Ace ), (PSID)CreatorSid );
+
+ return( IsEqual );
+
+}
+
+
+BOOLEAN
+RtlpContainsCreatorGroupServerSid(
+ PKNOWN_COMPOUND_ACE Ace
+ )
+/*++
+
+Routine Description:
+
+ Tests to see if the specified ACE contains the CreatorServerSid.
+
+Arguments:
+
+ Ace - Pointer to the compound ACE whose Server SID is be compared to the
+ Creator Server Sid. This ACE is assumed to be valid and a known
+ compound ACE type.
+
+Return Value:
+
+ TRUE - The creator Server sid is in the ACE.
+
+ FALSE - The creator Server sid is not in the ACE.
+
+
+--*/
+{
+ BOOLEAN IsEqual;
+ ULONG CreatorSid[CREATOR_SID_SIZE];
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ RTL_PAGED_CODE();
+
+ //
+ // This is gross and ugly, but it's better than allocating
+ // virtual memory to hold the CreatorSid, because that can
+ // fail, and propogating the error back is a tremendous pain
+ //
+
+ ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
+
+ //
+ // Allocate and initialize the universal SIDs
+ //
+
+ RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
+ *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;
+
+ IsEqual = RtlEqualSid(RtlCompoundAceServerSid(Ace), (PSID)CreatorSid );
+
+ return( IsEqual );
+}
+
+#endif
+
+
+VOID
+RtlpApplyAclToObject (
+ IN PACL Acl,
+ IN PGENERIC_MAPPING GenericMapping
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that maps Access Masks of an ACL so that
+ they are applicable to the object type the ACL is being applied to.
+
+ Only known DSA ACEs are mapped. Unknown ACE types are ignored.
+
+ Only access types in the GenericAll mapping for the target object
+ type will be non-zero upon return.
+
+Arguments:
+
+ Acl - Supplies the acl being applied.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the executive (in seassign.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+ ULONG i;
+
+ PACE_HEADER Ace;
+
+ RTL_PAGED_CODE();
+
+ //
+ // First check if the acl is null
+ //
+
+ if (Acl == NULL) {
+
+ return;
+
+ }
+
+
+ //
+ // Now walk the ACL, mapping each ACE as we go.
+ //
+
+ for (i = 0, Ace = FirstAce(Acl);
+ i < Acl->AceCount;
+ i += 1, Ace = NextAce(Ace)) {
+
+ if (IsMSAceType( Ace )) {
+
+ RtlApplyAceToObject( Ace, GenericMapping );
+ }
+
+ }
+
+ return;
+}
+
+#ifndef WIN16
+
+
+NTSTATUS
+RtlpInheritAcl (
+ IN PACL Acl,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID OwnerSid,
+ IN PSID GroupSid,
+ IN PSID ServerOwnerSid OPTIONAL,
+ IN PSID ServerGroupSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PACL *NewAcl
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that produces an inherited acl from
+ a parent acl according to the rules of inheritance
+
+Arguments:
+
+ Acl - Supplies the acl being inherited.
+
+ IsDirectoryObject - Specifies if the new acl is for a directory.
+
+ OwnerSid - Specifies the owner Sid to use.
+
+ GroupSid - Specifies the group SID to use.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+ NewAcl - Receives a pointer to the new (inherited) acl.
+
+Return Value:
+
+ STATUS_SUCCESS - An inheritable ACL was successfully generated.
+
+ STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
+ This is a warning completion status.
+
+ STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
+ This can becaused by a number of things. One of the more probable
+ causes is the replacement of a CreatorId with an SID that didn't fit
+ into the ACE or ACL.
+
+ STATUS_UNKNOWN_REVISION - Indicates the source ACL is a revision that
+ is unknown to this routine.
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the executive (in seassign.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+ NTSTATUS Status;
+ ULONG NewAclLength;
+ PVOID HeapHandle;
+
+ RTL_PAGED_CODE();
+
+ //
+ // Get the handle to the current process heap
+ //
+
+ HeapHandle = RtlProcessHeap();
+
+ //
+ // First check if the acl is null
+ //
+
+ if (Acl == NULL) {
+
+ return STATUS_NO_INHERITANCE;
+
+ }
+
+ if (Acl->AclRevision != ACL_REVISION2 && Acl->AclRevision != ACL_REVISION3) {
+ return STATUS_UNKNOWN_REVISION;
+ }
+
+
+ //
+ // Generating an inheritable ACL is a two-pass operation.
+ // First you must see if there is anything to inherit, and if so,
+ // allocate enough room to hold it. then you must actually copy
+ // the generated ACEs.
+ //
+
+ Status = RtlpLengthInheritAcl(
+ Acl,
+ IsDirectoryObject,
+ OwnerSid,
+ GroupSid,
+ ServerOwnerSid,
+ ServerGroupSid,
+ GenericMapping,
+ &NewAclLength
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+ if (NewAclLength == 0) {
+ return STATUS_NO_INHERITANCE;
+ }
+
+ (*NewAcl) = RtlAllocateHeap( HeapHandle, 0, NewAclLength );
+ if ((*NewAcl) == NULL ) {
+ return( STATUS_NO_MEMORY );
+ }
+
+
+
+ RtlCreateAcl( (*NewAcl), NewAclLength, Acl->AclRevision ); // Used to be hardwired to ACL_REVISION2
+ Status = RtlpGenerateInheritAcl(
+ Acl,
+ IsDirectoryObject,
+ OwnerSid,
+ GroupSid,
+ ServerOwnerSid,
+ ServerGroupSid,
+ GenericMapping,
+ (*NewAcl)
+ );
+
+ if (!NT_SUCCESS(Status)) {
+ RtlFreeHeap( HeapHandle, 0, *NewAcl );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+RtlpLengthInheritAcl(
+ IN PACL Acl,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID ClientOwnerSid,
+ IN PSID ClientGroupSid,
+ IN PSID ServerOwnerSid OPTIONAL,
+ IN PSID ServerGroupSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PULONG NewAclLength
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that calculates the length needed to
+ produce an inheritable ACL.
+
+Arguments:
+
+ Acl - Supplies the acl being inherited.
+
+ IsDirectoryObject - Specifies if the new acl is for a directory.
+
+ OwnerSid - Specifies the owner Sid to use.
+
+ GroupSid - Specifies the group SID to use.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+ NewAclLength - Receives the length of the inherited ACL.
+
+Return Value:
+
+ STATUS_SUCCESS - An inheritable ACL buffer successfully allocated.
+
+ STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
+ This is a warning completion status.
+
+ STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
+ This can becaused by a number of things. One of the more probable
+ causes is the replacement of a CreatorId with an SID that didn't fit
+ into the ACE or ACL.
+
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the executive (in seassign.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+ NTSTATUS Status;
+ ULONG i;
+
+ ULONG NewAclSize, NewAceSize;
+
+ PACE_HEADER OldAce;
+
+
+ RTL_PAGED_CODE();
+
+ //
+ // Calculate the length needed to store any inherited ACEs
+ // (this doesn't include the ACL header itself).
+ //
+
+ for (i = 0, OldAce = FirstAce(Acl), NewAclSize = 0;
+ i < Acl->AceCount;
+ i += 1, OldAce = NextAce(OldAce)) {
+
+ //
+ // RtlpLengthInheritedAce will return the number of bytes needed
+ // to inherit a single ACE.
+ //
+
+ Status = RtlpLengthInheritedAce(
+ OldAce,
+ IsDirectoryObject,
+ ClientOwnerSid,
+ ClientGroupSid,
+ ServerOwnerSid,
+ ServerGroupSid,
+ GenericMapping,
+ &NewAceSize
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ NewAclSize += NewAceSize;
+
+ }
+
+ //
+ // Check to make sure there is something inheritable
+ //
+
+ if (NewAclSize == 0) {
+ return STATUS_NO_INHERITANCE;
+ }
+
+ //
+ // And make sure we don't exceed the length limitations of an ACL (WORD)
+ //
+
+ NewAclSize += sizeof(ACL);
+
+ if (NewAclSize > 0xFFFF) {
+ return(STATUS_BAD_INHERITANCE_ACL);
+ }
+
+ (*NewAclLength) = NewAclSize;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlpGenerateInheritAcl(
+ IN PACL Acl,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID ClientOwnerSid,
+ IN PSID ClientGroupSid,
+ IN PSID ServerOwnerSid OPTIONAL,
+ IN PSID ServerGroupSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PACL NewAcl
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that produces an inheritable ACL.
+ It is expected that RtlpLengthInheritAcl() has already been
+ called to validate the inheritance and to indicate the length
+ of buffer needed to perform the inheritance.
+
+Arguments:
+
+ Acl - Supplies the acl being inherited.
+
+ IsDirectoryObject - Specifies if the new acl is for a directory.
+
+ OwnerSid - Specifies the owner Sid to use.
+
+ GroupSid - Specifies the group SID to use.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+ NewAcl - Provides a pointer to the buffer to receive the new
+ (inherited) acl. This ACL must already be initialized.
+
+
+Return Value:
+
+ STATUS_SUCCESS - An inheritable ACL has been generated.
+
+ STATUS_NO_INHERITANCE - An inheritable ACL was not successfully generated.
+ This is a warning completion status.
+
+ STATUS_BAD_INHERITANCE_ACL - Indicates the acl built was not a valid ACL.
+ This can becaused by a number of things. One of the more probable
+ causes is the replacement of a CreatorId with an SID that didn't fit
+ into the ACE or ACL.
+
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the executive (in seassign.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+ NTSTATUS Status;
+ ULONG i;
+
+ PACE_HEADER OldAce;
+
+
+ RTL_PAGED_CODE();
+
+ //
+ // Walk through the original ACL generating any necessary
+ // inheritable ACEs.
+ //
+
+ for (i = 0, OldAce = FirstAce(Acl);
+ i < Acl->AceCount;
+ i += 1, OldAce = NextAce(OldAce)) {
+
+ //
+ // RtlpGenerateInheritedAce() will generate the ACE(s) necessary
+ // to inherit a single ACE. This may be 0, 1, or more ACEs.
+ //
+
+ Status = RtlpGenerateInheritedAce(
+ OldAce,
+ IsDirectoryObject,
+ ClientOwnerSid,
+ ClientGroupSid,
+ ServerOwnerSid,
+ ServerGroupSid,
+ GenericMapping,
+ NewAcl
+ );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ }
+
+
+ return STATUS_SUCCESS;
+
+}
+
+#endif // WIN16
+
+NTSTATUS
+RtlpLengthInheritedAce (
+ IN PACE_HEADER Ace,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID ClientOwnerSid,
+ IN PSID ClientGroupSid,
+ IN PSID ServerOwnerSid OPTIONAL,
+ IN PSID ServerGroupSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ IN PULONG NewAceLength
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that calculates the number of bytes needed
+ to allow for the inheritance of the specified ACE. If the ACE is not
+ inheritable, or its AccessMask ends up with no accesses, then the
+ size may be returned as zero.
+
+Arguments:
+
+ Ace - Supplies the ace being checked
+
+ IsDirectoryObject - Specifies if the new ace is for a directory
+
+ ClientOwnerSid - Pointer to Sid to be assigned as the new owner.
+
+ ClientGroupSid - Points to SID to be assigned as the new primary group.
+
+ ServerOwnerSid - Provides the SID of a server to substitute into
+ compound ACEs (if any that require editing are encountered).
+ If this parameter is not provided, the SID passed for ClientOwnerSid
+ will be used.
+
+ ServerGroupSid - Provides the SID of a client to substitute into
+ compound ACEs (if any that require editing are encountered).
+ If this parameter is not provided, the SID passed for ClientGroupSid
+ will be used.
+
+ GenericMapping - Specifies the generic mapping to use.
+
+ NewAceLength - Receives the length (number of bytes) needed to allow for
+ the inheritance of the specified ACE. This might be zero.
+
+Return Value:
+
+ STATUS_SUCCESS - The length needed has been calculated.
+
+ STATUS_BAD_INHERITANCE_ACL - Indicates inheritance of the ace would
+ result in an invalid ACL structure. For example, an SID substitution
+ for a known ACE type which has a CreatorOwner SID might exceed the
+ length limits of an ACE.
+
+ STATUS_INVALID_PARAMETER - An optional parameter was required, but not
+ provided.
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the executive (in seassign.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // //
+ // !!!!!!!!! This is tricky !!!!!!!!!! //
+ // //
+ // The inheritence flags AND the sid of the ACE determine whether //
+ // we need 0, 1, or 2 ACEs. //
+ // //
+ // BE CAREFUL WHEN CHANGING THIS CODE. READ THE DSA ACL ARCHITECTURE //
+ // SECTION COVERING INHERITENCE BEFORE ASSUMING YOU KNOW WHAT THE HELL //
+ // YOU ARE DOING!!!! //
+ // //
+ // The general gist of the algorithm is: //
+ // //
+ // if ( (container && ContainerInherit) || //
+ // (!container && ObjectInherit) ) { //
+ // GenerateEffectiveAce; //
+ // } //
+ // //
+ // //
+ // if (Container && Propagate) { //
+ // Propogate copy of ACE and set InheritOnly; //
+ // } //
+ // //
+ // //
+ // A slightly more accurate description of this algorithm is: //
+ // //
+ // IO === InheritOnly flag //
+ // CI === ContainerInherit flag //
+ // OI === ObjectInherit flag //
+ // NPI === NoPropagateInherit flag //
+ // //
+ // if ( (container && CI) || //
+ // (!container && OI) ) { //
+ // Copy Header of ACE; //
+ // Clear IO, NPI, CI, OI; //
+ // //
+ // if (KnownAceType) { //
+ // if (SID is a creator ID) { //
+ // Copy appropriate creator SID; //
+ // } else { //
+ // Copy SID of original; //
+ // } //
+ // //
+ // Copy AccessMask of original; //
+ // MapGenericAccesses; //
+ // if (AccessMask == 0) { //
+ // discard new ACE; //
+ // } //
+ // //
+ // } else { //
+ // Copy body of ACE; //
+ // } //
+ // //
+ // } //
+ // //
+ // if (!NPI) { //
+ // Copy ACE as is; //
+ // Set IO; //
+ // } //
+ // //
+ // //
+ // //
+ ///////////////////////////////////////////////////////////////////////////
+
+
+ ULONG LengthRequired = 0;
+ ACCESS_MASK LocalMask;
+
+ PSID LocalServerOwner;
+ PSID LocalServerGroup;
+ PSID Sid;
+
+ ULONG Rid;
+
+ ULONG CreatorSid[CREATOR_SID_SIZE];
+ ULONG GroupSid[CREATOR_SID_SIZE];
+ ULONG CreatorOwnerServerSid[CREATOR_SID_SIZE];
+ ULONG CreatorGroupServerSid[CREATOR_SID_SIZE];
+
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+
+ RTL_PAGED_CODE();
+
+ //
+ // This is gross and ugly, but it's better than allocating
+ // virtual memory to hold the ClientSid, because that can
+ // fail, and propogating the error back is a tremendous pain
+ //
+
+ ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
+
+ //
+ // Allocate and initialize the universal SIDs we're going to need
+ // to look for inheritable ACEs.
+ //
+
+ RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
+ RtlInitializeSid( (PSID)GroupSid, &CreatorSidAuthority, 1 );
+
+ RtlInitializeSid( (PSID)CreatorOwnerServerSid, &CreatorSidAuthority, 1 );
+ RtlInitializeSid( (PSID)CreatorGroupServerSid, &CreatorSidAuthority, 1 );
+
+ *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
+ *(RtlSubAuthoritySid( (PSID)GroupSid, 0 )) = SECURITY_CREATOR_GROUP_RID;
+
+ *(RtlSubAuthoritySid( (PSID)CreatorOwnerServerSid, 0 )) = SECURITY_CREATOR_OWNER_SERVER_RID;
+ *(RtlSubAuthoritySid( (PSID)CreatorGroupServerSid, 0 )) = SECURITY_CREATOR_GROUP_SERVER_RID;
+
+ //
+ // Everywhere the pseudo-code above says "copy", the code in this
+ // routine simply calculates the length of. RtlpGenerateInheritedAce()
+ // will actually be used to do the "copy".
+ //
+
+ LocalServerOwner = ARGUMENT_PRESENT(ServerOwnerSid) ? ServerOwnerSid : ClientOwnerSid;
+
+ LocalServerGroup = ARGUMENT_PRESENT(ServerGroupSid) ? ServerGroupSid : ClientGroupSid;
+
+ //
+ // check to see if we will have a protection ACE (one mapped to
+ // the target object type).
+ //
+
+ if ( (IsDirectoryObject && ContainerInherit(Ace)) ||
+ (!IsDirectoryObject && ObjectInherit(Ace)) ) {
+
+ LengthRequired = (ULONG)Ace->AceSize;
+
+ if (IsKnownAceType( Ace ) ) {
+
+ //
+ // May need to adjust size due to SID substitution
+ //
+
+ PKNOWN_ACE KnownAce = (PKNOWN_ACE)Ace;
+
+ Sid = &KnownAce->SidStart;
+
+ if (RtlEqualPrefixSid ( Sid, CreatorSid )) {
+
+ Rid = *RtlSubAuthoritySid( Sid, 0 );
+
+ switch (Rid) {
+ case SECURITY_CREATOR_OWNER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
+ break;
+ }
+ case SECURITY_CREATOR_OWNER_SERVER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_SERVER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
+ break;
+ }
+ default :
+ {
+ //
+ // We don't know what this SID is, do nothing and the original will be copied.
+ //
+
+ break;
+ }
+ }
+ }
+
+ //
+ // If after mapping the access mask, the access mask
+ // is empty, then drop the ACE.
+ //
+
+ LocalMask = ((PKNOWN_ACE)(Ace))->Mask;
+ RtlMapGenericMask( &LocalMask, GenericMapping);
+
+ //
+ // Mask off any bits that aren't meaningful
+ //
+
+ LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
+
+ if (LocalMask == 0) {
+ LengthRequired = 0;
+ }
+
+ } else {
+
+ if (IsCompoundAceType(Ace)) {
+
+ PKNOWN_COMPOUND_ACE KnownAce = (PKNOWN_COMPOUND_ACE)Ace;
+ PSID AceServerSid;
+ PSID AceClientSid;
+
+ AceServerSid = RtlCompoundAceServerSid( KnownAce );
+ AceClientSid = RtlCompoundAceClientSid( KnownAce );
+
+ if (RtlEqualPrefixSid ( AceClientSid, CreatorSid )) {
+
+ Rid = *RtlSubAuthoritySid( AceClientSid, 0 );
+
+ switch (Rid) {
+ case SECURITY_CREATOR_OWNER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
+ break;
+ }
+ case SECURITY_CREATOR_OWNER_SERVER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_SERVER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
+ break;
+ }
+ default :
+ {
+ //
+ // We don't know what this SID is, do nothing and the original will be copied.
+ //
+
+ break;
+ }
+ }
+ }
+
+ if (RtlEqualPrefixSid ( AceServerSid, CreatorSid )) {
+
+ Rid = *RtlSubAuthoritySid( AceServerSid, 0 );
+
+ switch (Rid) {
+ case SECURITY_CREATOR_OWNER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientOwnerSid);
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ClientGroupSid);
+ break;
+ }
+ case SECURITY_CREATOR_OWNER_SERVER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerOwnerSid);
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_SERVER_RID:
+ {
+ LengthRequired = LengthRequired - CREATOR_SID_SIZE + SeLengthSid(ServerGroupSid);
+ break;
+ }
+ default :
+ {
+ //
+ // We don't know what this SID is, do nothing and the original will be copied.
+ //
+
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // If after mapping the access mask, the access mask
+ // is empty, then drop the ACE.
+ //
+
+ LocalMask = ((PKNOWN_COMPOUND_ACE)(Ace))->Mask;
+ RtlMapGenericMask( &LocalMask, GenericMapping);
+
+ //
+ // Mask off any bits that aren't meaningful
+ //
+
+ LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
+
+ if (LocalMask == 0) {
+ LengthRequired = 0;
+ }
+ }
+
+ //
+ // We have the length of the new ACE, but we've calculated
+ // it with a ULONG. It must fit into a USHORT. See if it
+ // does.
+ //
+
+ ASSERT(sizeof(Ace->AceSize) == 2);
+ if (LengthRequired > 0xFFFF) {
+ return STATUS_BAD_INHERITANCE_ACL;
+ }
+ }
+
+ //
+ // If we are inheriting onto a container, then we may need to
+ // propagate the inheritance as well.
+ //
+
+ if (IsDirectoryObject && Propagate(Ace)) {
+
+ LengthRequired += (ULONG)Ace->AceSize;
+ }
+
+ //
+ // Now return to our caller
+ //
+
+ (*NewAceLength) = LengthRequired;
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+RtlpGenerateInheritedAce (
+ IN PACE_HEADER OldAce,
+ IN BOOLEAN IsDirectoryObject,
+ IN PSID ClientOwnerSid,
+ IN PSID ClientGroupSid,
+ IN PSID ServerOwnerSid OPTIONAL,
+ IN PSID ServerGroupSid OPTIONAL,
+ IN PGENERIC_MAPPING GenericMapping,
+ OUT PACL NewAcl
+ )
+
+/*++
+
+Routine Description:
+
+ This is a private routine that checks if the input ace is inheritable
+ and produces 0, 1, or 2 inherited aces in the given buffer.
+
+ See RtlpLengthInheritedAce() for detailed information on ACE inheritance.
+
+ THE CODE IN THIS ROUTINE MUST MATCH THE CODE IN RtlpLengthInheritanceAce()!!!
+
+Arguments:
+
+ OldAce - Supplies the ace being inherited
+
+ IsDirectoryObject - Specifies if the new ACE is for a directory
+
+ ClientOwnerSid - Specifies the owner Sid to use
+
+ ClientGroupSid - Specifies the new Group Sid to use
+
+ ServerSid - Optionally specifies the Server Sid to use in compound ACEs.
+
+ ClientSid - Optionally specifies the Client Sid to use in compound ACEs.
+
+ GenericMapping - Specifies the generic mapping to use
+
+ NewAcl - Provides a pointer to the ACL into which the ACE is to be
+ inherited.
+
+Return Value:
+
+ STATUS_SUCCESS - The ACE was inherited successfully.
+
+ STATUS_BAD_INHERITANCE_ACL - Indicates something went wrong preventing
+ the ACE from being inherited. This generally represents a bugcheck
+ situation when returned from this call.
+
+
+--*/
+
+{
+//////////////////////////////////////////////////////////////////////////////
+// //
+// The logic in the ACL inheritance code must mirror the code for //
+// inheritance in the executive (in seassign.c). Do not make changes //
+// here without also making changes in that module. //
+// //
+//////////////////////////////////////////////////////////////////////////////
+
+
+
+ ACCESS_MASK LocalMask;
+ PVOID AcePosition;
+ PUCHAR Target;
+ BOOLEAN CreatorOwner, CreatorGroup;
+ BOOLEAN CreatorClient, CreatorServer;
+ BOOLEAN ProtectionAceCopied;
+
+ PSID LocalServerOwner;
+ PSID LocalServerGroup;
+
+ ULONG CreatorSid[CREATOR_SID_SIZE];
+
+ SID_IDENTIFIER_AUTHORITY CreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY;
+
+ ULONG Rid;
+ PSID SidToCopy;
+ PSID ClientSidToCopy;
+ PSID ServerSidToCopy;
+ PSID AceClientSid;
+ PSID AceServerSid;
+
+ RTL_PAGED_CODE();
+
+ //
+ // This is gross and ugly, but it's better than allocating
+ // virtual memory to hold the ClientSid, because that can
+ // fail, and propogating the error back is a tremendous pain
+ //
+
+ ASSERT(RtlLengthRequiredSid( 1 ) == CREATOR_SID_SIZE);
+
+ //
+ // Allocate and initialize the universal SIDs we're going to need
+ // to look for inheritable ACEs.
+ //
+
+ RtlInitializeSid( (PSID)CreatorSid, &CreatorSidAuthority, 1 );
+ *(RtlSubAuthoritySid( (PSID)CreatorSid, 0 )) = SECURITY_CREATOR_OWNER_RID;
+
+ if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
+ return STATUS_BAD_INHERITANCE_ACL;
+ }
+
+ if (!ARGUMENT_PRESENT(ServerOwnerSid)) {
+ LocalServerOwner = ClientOwnerSid;
+ } else {
+ LocalServerOwner = ServerOwnerSid;
+ }
+
+ if (!ARGUMENT_PRESENT(ServerGroupSid)) {
+ LocalServerGroup = ClientGroupSid;
+ } else {
+ LocalServerGroup = ServerGroupSid;
+ }
+
+ //
+ // check to see if we will have a protection ACE (one mapped to
+ // the target object type).
+ //
+
+ if ( (IsDirectoryObject && ContainerInherit(OldAce)) ||
+ (!IsDirectoryObject && ObjectInherit(OldAce)) ) {
+
+ ProtectionAceCopied = TRUE;
+
+ if (IsKnownAceType( OldAce ) ) {
+
+ //
+ // If after mapping the access mask, the access mask
+ // is empty, then drop the ACE.
+ //
+
+ LocalMask = ((PKNOWN_ACE)(OldAce))->Mask;
+ RtlMapGenericMask( &LocalMask, GenericMapping);
+
+ //
+ // Mask off any bits that aren't meaningful
+ //
+
+ LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
+
+ if (LocalMask == 0) {
+
+ ProtectionAceCopied = FALSE;
+
+ } else {
+
+ PKNOWN_ACE KnownAce = (PKNOWN_ACE)OldAce;
+
+ Target = (PUCHAR)AcePosition;
+
+ //
+ // See if the SID in the ACE is one of the various CREATOR_* SIDs by
+ // comparing identifier authorities.
+ //
+
+ if (RtlEqualPrefixSid ( &KnownAce->SidStart, CreatorSid )) {
+
+ Rid = *RtlSubAuthoritySid( &KnownAce->SidStart, 0 );
+
+ switch (Rid) {
+ case SECURITY_CREATOR_OWNER_RID:
+ {
+ SidToCopy = ClientOwnerSid;
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_RID:
+ {
+ SidToCopy = ClientGroupSid;
+ break;
+ }
+ case SECURITY_CREATOR_OWNER_SERVER_RID:
+ {
+ SidToCopy = LocalServerOwner;
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_SERVER_RID:
+ {
+ SidToCopy = LocalServerGroup;
+ break;
+ }
+ default :
+ {
+ //
+ // We don't know what this SID is, just copy the original.
+ //
+
+ SidToCopy = &KnownAce->SidStart;
+ break;
+ }
+ }
+
+ //
+ // SID substitution required. Copy all except the SID.
+ //
+
+ RtlMoveMemory(
+ Target,
+ OldAce,
+ FIELD_OFFSET(KNOWN_ACE, SidStart)
+ );
+
+ Target = (PUCHAR)(Target + FIELD_OFFSET(KNOWN_ACE, SidStart));
+
+ //
+ // Now copy the correct SID
+ //
+
+ RtlMoveMemory(
+ Target,
+ SidToCopy,
+ SeLengthSid(SidToCopy)
+ );
+
+ //
+ // Set the size of the ACE accordingly
+ //
+
+ ((PKNOWN_ACE)AcePosition)->Header.AceSize =
+ (USHORT)FIELD_OFFSET(KNOWN_ACE, SidStart) +
+ (USHORT)SeLengthSid(SidToCopy);
+
+ } else {
+
+ //
+ // No ID substitution, copy ACE as is.
+ //
+
+ RtlMoveMemory(
+ Target,
+ OldAce,
+ ((PKNOWN_ACE)OldAce)->Header.AceSize
+ );
+ }
+
+ //
+ // Put the mapped access mask in the new ACE
+ //
+
+ ((PKNOWN_ACE)AcePosition)->Mask = LocalMask;
+ }
+
+ } else if (IsCompoundAceType(OldAce)) {
+
+ //
+ // If after mapping the access mask, the access mask
+ // is empty, then drop the ACE.
+ //
+
+ LocalMask = ((PKNOWN_COMPOUND_ACE)(OldAce))->Mask;
+ RtlMapGenericMask( &LocalMask, GenericMapping);
+
+ //
+ // Mask off any bits that aren't meaningful
+ //
+
+ LocalMask &= ( STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY );
+
+ if (LocalMask == 0) {
+
+ ProtectionAceCopied = FALSE;
+
+ } else {
+
+ Target = (PUCHAR)AcePosition;
+
+ //
+ // See if the SID in the ACE is one of the various CREATOR_* SIDs by
+ // comparing identifier authorities.
+ //
+
+ AceServerSid = RtlCompoundAceServerSid( OldAce );
+ AceClientSid = RtlCompoundAceClientSid( OldAce );
+
+ if (RtlEqualPrefixSid ( AceServerSid, CreatorSid )) {
+
+ Rid = *RtlSubAuthoritySid( AceServerSid, 0 );
+
+ switch (Rid) {
+ case SECURITY_CREATOR_OWNER_RID:
+ {
+ ServerSidToCopy = ClientOwnerSid;
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_RID:
+ {
+ ServerSidToCopy = ClientGroupSid;
+ break;
+ }
+ case SECURITY_CREATOR_OWNER_SERVER_RID:
+ {
+ ServerSidToCopy = LocalServerOwner;
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_SERVER_RID:
+ {
+ ServerSidToCopy = LocalServerGroup;
+ break;
+ }
+ default :
+ {
+ //
+ // We don't know what this SID is, just copy the original.
+ //
+
+ ServerSidToCopy = AceServerSid;
+ break;
+ }
+ }
+
+ } else {
+
+ ServerSidToCopy = AceServerSid;
+ }
+
+ if (RtlEqualPrefixSid ( AceClientSid, CreatorSid )) {
+
+ Rid = *RtlSubAuthoritySid( AceClientSid, 0 );
+
+ switch (Rid) {
+ case SECURITY_CREATOR_OWNER_RID:
+ {
+ ClientSidToCopy = ClientOwnerSid;
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_RID:
+ {
+ ClientSidToCopy = ClientGroupSid;
+ break;
+ }
+ case SECURITY_CREATOR_OWNER_SERVER_RID:
+ {
+ ClientSidToCopy = LocalServerOwner;
+ break;
+ }
+ case SECURITY_CREATOR_GROUP_SERVER_RID:
+ {
+ ClientSidToCopy = LocalServerGroup;
+ break;
+ }
+ default :
+ {
+ //
+ // We don't know what this SID is, just copy the original.
+ //
+
+ ClientSidToCopy = AceClientSid;
+ break;
+ }
+ }
+
+ } else {
+
+ ClientSidToCopy = AceClientSid;
+ }
+
+ //
+ // Copy ACE in pieces. Body of ACE first.
+ //
+
+
+ RtlMoveMemory(
+ Target,
+ OldAce,
+ FIELD_OFFSET(KNOWN_ACE, Mask)
+ );
+
+ Target = (PUCHAR)(Target + FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart));
+
+ //
+ // Now copy the correct Server SID
+ //
+
+ RtlMoveMemory(
+ Target,
+ ServerSidToCopy,
+ SeLengthSid(ServerSidToCopy)
+ );
+
+ Target = (PUCHAR)(Target + SeLengthSid(ServerSidToCopy));
+
+ //
+ // Now copy the correct Client SID
+ //
+
+ RtlMoveMemory(
+ Target,
+ ClientSidToCopy,
+ SeLengthSid(ClientSidToCopy)
+ );
+
+ //
+ // Set the size of the ACE accordingly
+ //
+
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceSize =
+ (USHORT)FIELD_OFFSET(KNOWN_COMPOUND_ACE, SidStart) +
+ (USHORT)SeLengthSid(ServerSidToCopy) +
+ (USHORT)SeLengthSid(ClientSidToCopy);
+
+ //
+ // Put the mapped access mask in the new ACE and set the type information
+ //
+
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->Mask = LocalMask;
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->Header.AceType = ACCESS_ALLOWED_COMPOUND_ACE_TYPE;
+ ((PKNOWN_COMPOUND_ACE)AcePosition)->CompoundAceType = COMPOUND_ACE_IMPERSONATION;
+ }
+
+ } else {
+
+ //
+ // Not a known ACE type, copy ACE as is
+ //
+
+ RtlMoveMemory(
+ AcePosition,
+ OldAce,
+ ((PKNOWN_COMPOUND_ACE)OldAce)->Header.AceSize
+ );
+ }
+
+ //
+ // If the ACE was actually kept, clear all the inherit flags
+ // and update the ACE count of the ACL.
+ //
+
+ if (ProtectionAceCopied) {
+ ((PACE_HEADER)AcePosition)->AceFlags &= ~VALID_INHERIT_FLAGS;
+ NewAcl->AceCount += 1;
+ }
+ }
+
+ //
+ // If we are inheriting onto a container, then we may need to
+ // propagate the inheritance as well.
+ //
+
+ if (IsDirectoryObject && Propagate(OldAce)) {
+
+ //
+ // copy it as is, but make sure the InheritOnly bit is set.
+ //
+
+ if (!RtlFirstFreeAce( NewAcl, &AcePosition ) ) {
+ return STATUS_BAD_INHERITANCE_ACL;
+ }
+
+ RtlMoveMemory(
+ AcePosition,
+ OldAce,
+ ((PKNOWN_ACE)OldAce)->Header.AceSize
+ );
+
+ ((PACE_HEADER)AcePosition)->AceFlags |= INHERIT_ONLY_ACE;
+ NewAcl->AceCount += 1;
+ }
+
+ //
+ // Now return to our caller
+ //
+
+ return STATUS_SUCCESS;
+}
+
+#if DBG
+NTSTATUS
+RtlDumpUserSid(
+ VOID
+ )
+{
+ NTSTATUS Status;
+ HANDLE TokenHandle;
+ CHAR Buffer[200];
+ ULONG ReturnLength;
+ PSID pSid;
+ UNICODE_STRING SidString;
+ PTOKEN_USER User;
+
+ //
+ // Attempt to open the impersonation token first
+ //
+
+ Status = NtOpenThreadToken(
+ NtCurrentThread(),
+ GENERIC_READ,
+ FALSE,
+ &TokenHandle
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ DbgPrint("Not impersonating, status = %X, trying process token\n",Status);
+
+ Status = NtOpenProcessToken(
+ NtCurrentProcess(),
+ GENERIC_READ,
+ &TokenHandle
+ );
+
+ if (!NT_SUCCESS( Status )) {
+ DbgPrint("Unable to open process token, status = %X\n",Status);
+ return( Status );
+ }
+ }
+
+ Status = NtQueryInformationToken (
+ TokenHandle,
+ TokenUser,
+ Buffer,
+ 200,
+ &ReturnLength
+ );
+
+ if (!NT_SUCCESS( Status )) {
+
+ DbgPrint("Unable to query user sid, status = %X \n",Status);
+ NtClose(TokenHandle);
+ return( Status );
+ }
+
+ User = (PTOKEN_USER)Buffer;
+
+ pSid = User->User.Sid;
+
+ Status = RtlConvertSidToUnicodeString( &SidString, pSid, TRUE );
+
+ if (!NT_SUCCESS( Status )) {
+ DbgPrint("Unable to format sid string, status = %X \n",Status);
+ NtClose(TokenHandle);
+ return( Status );
+ }
+
+ DbgPrint("Current Sid = %wZ \n",&SidString);
+
+ RtlFreeUnicodeString( &SidString );
+
+ return( STATUS_SUCCESS );
+}
+
+#endif
diff --git a/private/ntos/rtl/splay.c b/private/ntos/rtl/splay.c
new file mode 100644
index 000000000..0134bbfdd
--- /dev/null
+++ b/private/ntos/rtl/splay.c
@@ -0,0 +1,1074 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Splay.c
+
+Abstract:
+
+ This module implements the general splay utilities
+
+Author:
+
+ Gary Kimura [GaryKi] 23-May-1989
+
+Environment:
+
+ Pure utility routine
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+
+#include <ntrtl.h>
+
+#define SwapPointers(Ptr1, Ptr2) { \
+ PVOID _SWAP_POINTER_TEMP; \
+ _SWAP_POINTER_TEMP = (PVOID)(Ptr1); \
+ (Ptr1) = (Ptr2); \
+ (Ptr2) = _SWAP_POINTER_TEMP; \
+ }
+
+#define ParentsChildPointerAddress(Links) ( \
+ RtlIsLeftChild(Links) ? \
+ &(((Links)->Parent)->LeftChild) \
+ : \
+ &(((Links)->Parent)->RightChild) \
+ )
+
+VOID
+SwapSplayLinks (
+ IN PRTL_SPLAY_LINKS Link1,
+ IN PRTL_SPLAY_LINKS Link2
+ );
+
+
+PRTL_SPLAY_LINKS
+RtlSplay (
+ IN PRTL_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ The Splay function takes as input a pointer to a splay link in a tree
+ and splays the tree. Its function return value is a pointer to the
+ root of the splayed tree.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+Return Value:
+
+ PRTL_SPLAY_LINKS - returns a pointer to the root of the splayed tree.
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS L;
+ PRTL_SPLAY_LINKS P;
+ PRTL_SPLAY_LINKS G;
+
+ //
+ // while links is not the root we need to keep rotating it toward
+ // the root
+ //
+
+ L = Links;
+
+ while (!RtlIsRoot(L)) {
+
+ P = RtlParent(L);
+ G = RtlParent(P);
+
+ if (RtlIsLeftChild(L)) {
+
+ if (RtlIsRoot(P)) {
+
+ /*
+ we have the following case
+
+ P L
+ / \ / \
+ L c ==> a P
+ / \ / \
+ a b b c
+ */
+
+ //
+ // Connect P & b
+ //
+
+ P->LeftChild = L->RightChild;
+ if (P->LeftChild != NULL) {P->LeftChild->Parent = P;}
+
+ //
+ // Connect L & P
+ //
+
+ L->RightChild = P;
+ P->Parent = L;
+
+ //
+ // Make L the root
+ //
+
+ L->Parent = L;
+
+ } else if (RtlIsLeftChild(P)) {
+
+ /*
+ we have the following case
+
+ | |
+ G L
+ / \ / \
+ P d ==> a P
+ / \ / \
+ L c b G
+ / \ / \
+ a b c d
+ */
+
+ //
+ // Connect P & b
+ //
+
+ P->LeftChild = L->RightChild;
+ if (P->LeftChild != NULL) {P->LeftChild->Parent = P;}
+
+ //
+ // Connect G & c
+ //
+
+ G->LeftChild = P->RightChild;
+ if (G->LeftChild != NULL) {G->LeftChild->Parent = G;}
+
+ //
+ // Connect L & Great GrandParent
+ //
+
+ if (RtlIsRoot(G)) {
+ L->Parent = L;
+ } else {
+ L->Parent = G->Parent;
+ *(ParentsChildPointerAddress(G)) = L;
+ }
+
+ //
+ // Connect L & P
+ //
+
+ L->RightChild = P;
+ P->Parent = L;
+
+ //
+ // Connect P & G
+ //
+
+ P->RightChild = G;
+ G->Parent = P;
+
+ } else { // RtlIsRightChild(Parent)
+
+ /*
+ we have the following case
+
+ | |
+ G L
+ / \ / \
+ a P G P
+ / \ / \ / \
+ L d ==> a b c d
+ / \
+ b c
+ */
+
+ //
+ // Connect G & b
+ //
+
+ G->RightChild = L->LeftChild;
+ if (G->RightChild != NULL) {G->RightChild->Parent = G;}
+
+ //
+ // Connect P & c
+ //
+
+ P->LeftChild = L->RightChild;
+ if (P->LeftChild != NULL) {P->LeftChild->Parent = P;}
+
+ //
+ // Connect L & Great GrandParent
+ //
+
+ if (RtlIsRoot(G)) {
+ L->Parent = L;
+ } else {
+ L->Parent = G->Parent;
+ *(ParentsChildPointerAddress(G)) = L;
+ }
+
+ //
+ // Connect L & G
+ //
+
+ L->LeftChild = G;
+ G->Parent = L;
+
+ //
+ // Connect L & P
+ //
+
+ L->RightChild = P;
+ P->Parent = L;
+
+ }
+
+ } else { // RtlIsRightChild(L)
+
+ if (RtlIsRoot(P)) {
+
+ /*
+ we have the following case
+
+ P L
+ / \ / \
+ a L P c
+ / \ / \
+ b c ==> a b
+ */
+
+ //
+ // Connect P & b
+ //
+
+ P->RightChild = L->LeftChild;
+ if (P->RightChild != NULL) {P->RightChild->Parent = P;}
+
+ //
+ // Connect P & L
+ //
+
+ L->LeftChild = P;
+ P->Parent = L;
+
+ //
+ // Make L the root
+ //
+
+ L->Parent = L;
+
+ } else if (RtlIsRightChild(P)) {
+
+ /*
+ we have the following case
+
+ | |
+ G L
+ / \ / \
+ a P P d
+ / \ / \
+ b L G c
+ / \ / \
+ c d ==> a b
+ */
+
+ //
+ // Connect G & b
+ //
+
+ G->RightChild = P->LeftChild;
+ if (G->RightChild != NULL) {G->RightChild->Parent = G;}
+
+ //
+ // Connect P & c
+ //
+
+ P->RightChild = L->LeftChild;
+ if (P->RightChild != NULL) {P->RightChild->Parent = P;}
+
+ //
+ // Connect L & Great GrandParent
+ //
+
+ if (RtlIsRoot(G)) {
+ L->Parent = L;
+ } else {
+ L->Parent = G->Parent;
+ *(ParentsChildPointerAddress(G)) = L;
+ }
+
+ //
+ // Connect L & P
+ //
+
+ L->LeftChild = P;
+ P->Parent = L;
+
+ //
+ // Connect P & G
+ //
+
+ P->LeftChild = G;
+ G->Parent = P;
+
+ } else { // RtlIsLeftChild(P)
+
+ /*
+ we have the following case
+
+ | |
+ G L
+ / \ / \
+ P d P G
+ / \ / \ / \
+ a L ==> a b c d
+ / \
+ b c
+ */
+
+ //
+ // Connect P & b
+ //
+
+ P->RightChild = L->LeftChild;
+ if (P->RightChild != NULL) {P->RightChild->Parent = P;}
+
+ //
+ // Connect G & c
+ //
+
+ G->LeftChild = L->RightChild;
+ if (G->LeftChild != NULL) {G->LeftChild->Parent = G;}
+
+ //
+ // Connect L & Great GrandParent
+ //
+
+ if (RtlIsRoot(G)) {
+ L->Parent = L;
+ } else {
+ L->Parent = G->Parent;
+ *(ParentsChildPointerAddress(G)) = L;
+ }
+
+ //
+ // Connect L & P
+ //
+
+ L->LeftChild = P;
+ P->Parent = L;
+
+ //
+ // Connect L & G
+ //
+
+ L->RightChild = G;
+ G->Parent = L;
+
+ }
+ }
+ }
+
+ return L;
+}
+
+
+PRTL_SPLAY_LINKS
+RtlDelete (
+ IN PRTL_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ The Delete function takes as input a pointer to a splay link in a tree
+ and deletes that node from the tree. Its function return value is a
+ pointer to the root of the tree. If the tree is now empty, the return
+ value is NULL.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+Return Value:
+
+ PRTL_SPLAY_LINKS - returns a pointer to the root of the tree.
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Predecessor;
+ PRTL_SPLAY_LINKS Parent;
+ PRTL_SPLAY_LINKS Child;
+
+ PRTL_SPLAY_LINKS *ParentChildPtr;
+
+ //
+ // First check to see if Links as two children. If it does then swap
+ // Links with its subtree predecessor. Now we are guaranteed that Links
+ // has at most one child.
+ //
+
+ if ((RtlLeftChild(Links) != NULL) && (RtlRightChild(Links) != NULL)) {
+
+ //
+ // get the predecessor, and swap their position in the tree
+ //
+
+ Predecessor = RtlSubtreePredecessor(Links);
+ SwapSplayLinks(Predecessor, Links);
+
+ }
+
+ //
+ // If Links has no children then delete links by checking if it is
+ // already the root or has a parent. If it is the root then the
+ // tree is now empty, otherwise it set the appropriate parent's child
+ // pointer (i.e., the one to links) to NULL, and splay the parent.
+ //
+
+ if ((RtlLeftChild(Links) == NULL) && (RtlRightChild(Links) == NULL)) {
+
+ //
+ // Links has no children, if it is the root then return NULL
+ //
+
+ if (RtlIsRoot(Links)) {
+
+ return NULL;
+ }
+
+ //
+ // Links as not children and is not the root, so to the parent's
+ // child pointer to NULL and splay the parent.
+ //
+
+ Parent = RtlParent(Links);
+
+ ParentChildPtr = ParentsChildPointerAddress(Links);
+ *ParentChildPtr = NULL;
+
+ return RtlSplay(Parent);
+
+ }
+
+ //
+ // otherwise Links has one child. If it is the root then make the child
+ // the new root, otherwise link together the child and parent, and splay
+ // the parent. But first remember who our child is.
+ //
+
+ if (RtlLeftChild(Links) != NULL) {
+ Child = RtlLeftChild(Links);
+ } else {
+ Child = RtlRightChild(Links);
+ }
+
+ //
+ // If links is the root then we make the child the root and return the
+ // child.
+ //
+
+ if (RtlIsRoot(Links)) {
+ Child->Parent = Child;
+ return Child;
+ }
+
+ //
+ // Links is not the root, so set link's parent child pointer to be
+ // the child and the set child's parent to be link's parent, and splay
+ // the parent.
+ //
+
+ ParentChildPtr = ParentsChildPointerAddress(Links);
+ *ParentChildPtr = Child;
+ Child->Parent = Links->Parent;
+
+ return RtlSplay(RtlParent(Child));
+
+}
+
+
+VOID
+RtlDeleteNoSplay (
+ IN PRTL_SPLAY_LINKS Links,
+ IN OUT PRTL_SPLAY_LINKS *Root
+ )
+
+/*++
+
+Routine Description:
+
+ The Delete function takes as input a pointer to a splay link in a tree,
+ a pointer to the callers pointer to the tree and deletes that node from
+ the tree. The caller's pointer is updated upon return. If the tree is
+ now empty, the value is NULL.
+
+ Unfortunately, the original RtlDelete() always splays and this is not
+ always a desireable side-effect.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+ Root - Pointer to the callers pointer to the root
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Predecessor;
+ PRTL_SPLAY_LINKS Parent;
+ PRTL_SPLAY_LINKS Child;
+
+ PRTL_SPLAY_LINKS *ParentChildPtr;
+
+ //
+ // First check to see if Links as two children. If it does then swap
+ // Links with its subtree predecessor. Now we are guaranteed that Links
+ // has at most one child.
+ //
+
+ if ((RtlLeftChild(Links) != NULL) && (RtlRightChild(Links) != NULL)) {
+
+ //
+ // get the predecessor, and swap their position in the tree
+ //
+
+ Predecessor = RtlSubtreePredecessor(Links);
+
+ if (RtlIsRoot(Links)) {
+
+ //
+ // If we're switching with the root of the tree, fix the
+ // caller's root pointer
+ //
+
+ *Root = Predecessor;
+ }
+
+ SwapSplayLinks(Predecessor, Links);
+
+ }
+
+ //
+ // If Links has no children then delete links by checking if it is
+ // already the root or has a parent. If it is the root then the
+ // tree is now empty, otherwise it set the appropriate parent's child
+ // pointer (i.e., the one to links) to NULL.
+ //
+
+ if ((RtlLeftChild(Links) == NULL) && (RtlRightChild(Links) == NULL)) {
+
+ //
+ // Links has no children, if it is the root then set root to NULL
+ //
+
+ if (RtlIsRoot(Links)) {
+
+ *Root = NULL;
+
+ return;
+ }
+
+ //
+ // Links as not children and is not the root, so to the parent's
+ // child pointer to NULL.
+ //
+
+ ParentChildPtr = ParentsChildPointerAddress(Links);
+ *ParentChildPtr = NULL;
+
+ return;
+ }
+
+ //
+ // otherwise Links has one child. If it is the root then make the child
+ // the new root, otherwise link together the child and parent. But first
+ // remember who our child is.
+ //
+
+ if (RtlLeftChild(Links) != NULL) {
+ Child = RtlLeftChild(Links);
+ } else {
+ Child = RtlRightChild(Links);
+ }
+
+ //
+ // If links is the root then we make the child the root and return the
+ // child.
+ //
+
+ if (RtlIsRoot(Links)) {
+ Child->Parent = Child;
+
+ *Root = Child;
+
+ return;
+ }
+
+ //
+ // Links is not the root, so set link's parent child pointer to be
+ // the child and the set child's parent to be link's parent.
+ //
+
+ ParentChildPtr = ParentsChildPointerAddress(Links);
+ *ParentChildPtr = Child;
+ Child->Parent = Links->Parent;
+
+ return;
+}
+
+
+PRTL_SPLAY_LINKS
+RtlSubtreeSuccessor (
+ IN PRTL_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ The SubtreeSuccessor function takes as input a pointer to a splay link
+ in a tree and returns a pointer to the successor of the input node of
+ the substree rooted at the input node. If there is not a successor, the
+ return value is NULL.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+Return Value:
+
+ PRTL_SPLAY_LINKS - returns a pointer to the successor in the subtree
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Ptr;
+
+ /*
+ check to see if there is a right subtree to the input link
+ if there is then the subtree successor is the left most node in
+ the right subtree. That is find and return P in the following diagram
+
+ Links
+ \
+ .
+ .
+ .
+ /
+ P
+ \
+ */
+
+ if ((Ptr = RtlRightChild(Links)) != NULL) {
+
+ while (RtlLeftChild(Ptr) != NULL) {
+ Ptr = RtlLeftChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ //
+ // otherwise we are do not have a subtree successor so we simply return
+ // NULL
+ //
+
+ return NULL;
+
+}
+
+
+PRTL_SPLAY_LINKS
+RtlSubtreePredecessor (
+ IN PRTL_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ The SubtreePredecessor function takes as input a pointer to a splay link
+ in a tree and returns a pointer to the predecessor of the input node of
+ the substree rooted at the input node. If there is not a predecessor,
+ the return value is NULL.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+Return Value:
+
+ PRTL_SPLAY_LINKS - returns a pointer to the predecessor in the subtree
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Ptr;
+
+ //
+ // check to see if there is a left subtree to the input link
+ // if there is then the subtree predecessor is the right most node in
+ // the left subtree. That is find and return P in the following diagram
+ //
+ // Links
+ // /
+ // .
+ // .
+ // .
+ // P
+ // /
+ //
+
+ if ((Ptr = RtlLeftChild(Links)) != NULL) {
+
+ while (RtlRightChild(Ptr) != NULL) {
+ Ptr = RtlRightChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ //
+ // otherwise we are do not have a subtree predecessor so we simply return
+ // NULL
+ //
+
+ return NULL;
+
+}
+
+
+PRTL_SPLAY_LINKS
+RtlRealSuccessor (
+ IN PRTL_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ The RealSuccessor function takes as input a pointer to a splay link
+ in a tree and returns a pointer to the successor of the input node within
+ the entire tree. If there is not a successor, the return value is NULL.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+Return Value:
+
+ PRTL_SPLAY_LINKS - returns a pointer to the successor in the entire tree
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Ptr;
+
+ /*
+ first check to see if there is a right subtree to the input link
+ if there is then the real successor is the left most node in
+ the right subtree. That is find and return P in the following diagram
+
+ Links
+ \
+ .
+ .
+ .
+ /
+ P
+ \
+ */
+
+ if ((Ptr = RtlRightChild(Links)) != NULL) {
+
+ while (RtlLeftChild(Ptr) != NULL) {
+ Ptr = RtlLeftChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ /*
+ we do not have a right child so check to see if have a parent and if
+ so find the first ancestor that we are a left decendent of. That
+ is find and return P in the following diagram
+
+ P
+ /
+ .
+ .
+ .
+ Links
+ */
+
+ Ptr = Links;
+ while (RtlIsRightChild(Ptr)) {
+ Ptr = RtlParent(Ptr);
+ }
+
+ if (RtlIsLeftChild(Ptr)) {
+ return RtlParent(Ptr);
+ }
+
+ //
+ // otherwise we are do not have a real successor so we simply return
+ // NULL
+ //
+
+ return NULL;
+
+}
+
+
+PRTL_SPLAY_LINKS
+RtlRealPredecessor (
+ IN PRTL_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ The RealPredecessor function takes as input a pointer to a splay link
+ in a tree and returns a pointer to the predecessor of the input node
+ within the entire tree. If there is not a predecessor, the return value
+ is NULL.
+
+Arguments:
+
+ Links - Supplies a pointer to a splay link in a tree.
+
+Return Value:
+
+ PRTL_SPLAY_LINKS - returns a pointer to the predecessor in the entire tree
+
+--*/
+
+{
+ PRTL_SPLAY_LINKS Ptr;
+
+ /*
+ first check to see if there is a left subtree to the input link
+ if there is then the real predecessor is the right most node in
+ the left subtree. That is find and return P in the following diagram
+
+ Links
+ /
+ .
+ .
+ .
+ P
+ /
+ */
+
+ if ((Ptr = RtlLeftChild(Links)) != NULL) {
+
+ while (RtlRightChild(Ptr) != NULL) {
+ Ptr = RtlRightChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ /*
+ we do not have a left child so check to see if have a parent and if
+ so find the first ancestor that we are a right decendent of. That
+ is find and return P in the following diagram
+
+ P
+ \
+ .
+ .
+ .
+ Links
+ */
+
+ Ptr = Links;
+ while (RtlIsLeftChild(Ptr)) {
+ Ptr = RtlParent(Ptr);
+ }
+
+ if (RtlIsRightChild(Ptr)) {
+ return RtlParent(Ptr);
+ }
+
+ //
+ // otherwise we are do not have a real predecessor so we simply return
+ // NULL
+ //
+
+ return NULL;
+
+}
+
+
+VOID
+SwapSplayLinks (
+ IN PRTL_SPLAY_LINKS Link1,
+ IN PRTL_SPLAY_LINKS Link2
+ )
+
+{
+ PRTL_SPLAY_LINKS *Parent1ChildPtr;
+ PRTL_SPLAY_LINKS *Parent2ChildPtr;
+
+ /*
+ We have the following situation
+
+
+ Parent1 Parent2
+ | |
+ | |
+ Link1 Link2
+ / \ / \
+ / \ / \
+ LC1 RC1 LC2 RC2
+
+ where one of the links can possibly be the root and one of the links
+ can possibly be a direct child of the other. Without loss of
+ generality we'll make link2 be the possible and root and link1 be
+ the possible child.
+ */
+
+ if ((RtlIsRoot(Link1)) || (RtlParent(Link2) == Link1)) {
+ SwapPointers(Link1, Link2);
+ }
+
+ //
+ // The four cases we need to handle are
+ //
+ // 1. Link1 is not a child of link2 and link2 is not the root
+ // 2. Link1 is not a child of link2 and link2 is the root
+ // 3. Link1 is a child of link2 and link2 is not the root
+ // 4. Link1 is a child of link2 and link2 is the root
+ //
+ //
+ // Each case will be handled separately
+ //
+
+ if (RtlParent(Link1) != Link2) {
+
+ if (!RtlIsRoot(Link2)) {
+
+ //
+ // Case 1 the initial steps are:
+ //
+ // 1. get both parent child pointers
+ // 2. swap the parent child pointers
+ // 3. swap the parent pointers
+ //
+
+ Parent1ChildPtr = ParentsChildPointerAddress(Link1);
+ Parent2ChildPtr = ParentsChildPointerAddress(Link2);
+
+ SwapPointers(*Parent1ChildPtr, *Parent2ChildPtr);
+
+ SwapPointers(Link1->Parent, Link2->Parent);
+
+ } else {
+
+ //
+ // Case 2 the initial steps are:
+ //
+ // 1. Set link1's parent child pointer to link2
+ // 2. Set parent pointer of link2 to link1's parent
+ // 3. Set parent pointer of link1 to be itself
+ //
+
+ Parent1ChildPtr = ParentsChildPointerAddress(Link1);
+ *Parent1ChildPtr = Link2;
+
+ Link2->Parent = Link1->Parent;
+
+ Link1->Parent = Link1;
+
+ }
+
+ //
+ // Case 1 and 2 common steps are:
+ //
+ // 1. swap the child pointers
+ //
+
+ SwapPointers(Link1->LeftChild, Link2->LeftChild);
+ SwapPointers(Link1->RightChild, Link2->RightChild);
+
+ } else { // RtlParent(Link1) == Link2
+
+ if (!RtlIsRoot(Link2)) {
+
+ //
+ // Case 3 the initial steps are:
+ //
+ // 1. Set Link2's parent child pointer to link1
+ // 2. Set Link1's parent pointer to parent of link2
+ //
+
+ Parent2ChildPtr = ParentsChildPointerAddress(Link2);
+ *Parent2ChildPtr = Link1;
+
+ Link1->Parent = Link2->Parent;
+
+ } else {
+
+ //
+ // Case 4 the initial steps are:
+ //
+ // 1. Set Link1's parent pointer to be link1
+ //
+
+ Link1->Parent = Link1;
+
+ }
+
+ //
+ // Case 3 and 4 common steps are:
+ //
+ // 1. Swap the child pointers
+ // 2. if link1 was a left child (i.e., RtlLeftChild(Link1) == Link1)
+ // then set left child of link1 to link2
+ // else set right child of link1 to link2
+ //
+
+ SwapPointers(Link1->LeftChild, Link2->LeftChild);
+ SwapPointers(Link1->RightChild, Link2->RightChild);
+
+ if (Link1->LeftChild == Link1) {
+ Link1->LeftChild = Link2;
+ } else {
+ Link1->RightChild = Link2;
+ }
+
+ }
+
+ //
+ // Case 1, 2, 3, 4 common (and final) steps are:
+ //
+ // 1. Fix the parent pointers of the left and right children
+ //
+
+ if (Link1->LeftChild != NULL) {Link1->LeftChild->Parent = Link1;}
+ if (Link1->RightChild != NULL) {Link1->RightChild->Parent = Link1;}
+ if (Link2->LeftChild != NULL) {Link2->LeftChild->Parent = Link2;}
+ if (Link2->RightChild != NULL) {Link2->RightChild->Parent = Link2;}
+
+}
diff --git a/private/ntos/rtl/stdtimep.h b/private/ntos/rtl/stdtimep.h
new file mode 100644
index 000000000..875faac3f
--- /dev/null
+++ b/private/ntos/rtl/stdtimep.h
@@ -0,0 +1,450 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ stdtimep.h
+
+Abstract:
+
+ This module contains definitions and function prototypes which are local to
+ stdime.c and fmttime.c.
+
+Author:
+
+ Rob McKaughan (t-robmc) 17-Jul-1991
+
+Revision History:
+
+--*/
+
+#ifndef _STD_TIME_P_
+#define _STD_TIME_P_
+
+//
+// These are the magic numbers needed to do our extended division. The
+// only numbers we ever need to divide by are
+//
+// 10,000 = convert 100ns tics to millisecond tics
+//
+// 10,000,000 = convert 100ns tics to one second tics
+//
+// 86,400,000 = convert Millisecond tics to one day tics
+//
+
+extern LARGE_INTEGER Magic10000;
+#define SHIFT10000 13
+
+extern LARGE_INTEGER Magic10000000;
+#define SHIFT10000000 23
+
+extern LARGE_INTEGER Magic86400000;
+#define SHIFT86400000 26
+
+//
+// To make the code more readable we'll also define some macros to
+// do the actual division for use
+//
+
+#define Convert100nsToMilliseconds(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000, SHIFT10000 ) \
+ )
+
+#define ConvertMillisecondsTo100ns(MILLISECONDS) ( \
+ RtlExtendedIntegerMultiply( (MILLISECONDS), 10000 ) \
+ )
+
+#define Convert100nsToSeconds(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000000, SHIFT10000000 ) \
+ )
+
+#define ConvertSecondsTo100ns(SECONDS) ( \
+ RtlExtendedIntegerMultiply( (SECONDS), 10000000L ) \
+ )
+
+#define ConvertMillisecondsToDays(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic86400000, SHIFT86400000 ) \
+ )
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Macros for Time Differentials and Time Revisions //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// The following define the minimum and maximum possible values for the Time
+// Differential Factor as defined by ISO 4031-1978.
+//
+
+#define MAX_STDTIME_TDF (780)
+#define MIN_STDTIME_TDF (-720)
+
+//
+// The revision of this design (will be inserted in the revision field of any
+// STANDARD_TIMEs created by this revision).
+//
+
+#define STDTIME_REVISION (4)
+
+
+//
+// The number of bits we need to shift to get to and from a revision in a
+// StdTime.TdfAndRevision field.
+//
+
+#define STDTIME_REVISION_SHIFT 12
+
+
+//
+// USHORT
+// ShiftStandardTimeRevision(
+// IN USHORT Rev
+// )
+// Description:
+// This routine shifts the given revision number to its proper place for
+// storing in a STANDARD_TIME.TdfAndRevision field.
+//
+
+#define ShiftStandardTimeRevision(Rev) \
+ ((USHORT) ((Rev) << STDTIME_REVISION_SHIFT))
+
+
+//
+// The pre-shifted value of the current revision
+//
+
+#define SHIFTED_STDTIME_REVISION (ShiftStandardTimeRevision(STDTIME_REVISION))
+
+
+//
+// The bit mask used to mask a STANDARD_TIME.TdfAndRevision field to retrieve
+// the Tdf value.
+//
+
+#define TDF_MASK ((USHORT) 0x0fff)
+
+
+//
+// USHORT
+// MaskStandardTimeTdf(
+// IN USHORT Tdf
+// )
+// Description:
+// This routine masks the given tdf field with TDF_MASK and returns the
+// result.
+//
+// BUG: Byte order dependant
+//
+
+#define MaskStandardTimeTdf(Tdf) ((USHORT) ((Tdf) & TDF_MASK))
+
+
+//
+// SHORT
+// GetStandardTimeTdf(
+// IN STANDARD_TIME
+// )
+// Description:
+// This routine gets the Time Differential Factor from a tdf field and
+// makes any adjustments necessary to preserve the sign of the TDF.
+// The resulting TDF is returned.
+//
+// Since the TDF is stored as a signed 12 bit int, it's sign bit is the
+// bit 0x0800. To make it a 16 bit negative, we subtract 0x1000 from the
+// bottome 12 bits of the TdfAndRevision field.
+//
+// BUG: Byte order dependant
+//
+
+#define GetStandardTimeTdf(StdTime) \
+ ((SHORT) \
+ (((StdTime)->TdfAndRevision) & 0x0800) \
+ ? (MaskStandardTimeTdf((StdTime)->TdfAndRevision) - 0x1000) \
+ : MaskStandardTimeTdf((StdTime)->TdfAndRevision) \
+ )
+
+
+//
+// USHORT
+// GetStandardTimeRev(
+// IN USHORT Tdf
+// )
+// Description:
+// This routine gets the revision number from a tdf field and returns it
+// shifted back down to its place as a SHORT.
+//
+
+#define GetStandardTimeRev(StdTime) \
+ ((USHORT) (((StdTime)->TdfAndRevision) >> STDTIME_REVISION_SHIFT))
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Tests for absolute and delta times //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// BOOLEAN
+// IsPositive(
+// IN LARGE_INTEGER Time
+// )
+// Returns:
+// TRUE - if the time in Time is positive.
+// FALSE - if Time is negative.
+//
+
+#define IsPositive(Time) \
+ ( ((Time).HighPart > 0) || (((Time).HighPart = 0) & ((Time).LowPart > 0)) )
+
+//
+// BOOLEAN
+// IsAbsoluteTime(
+// IN PSTANDARDTIME Time
+// )
+// Returns:
+// TRUE - if the given time is an absolute time
+// FALSE - If the given time is not an absolute time
+//
+
+#define IsAbsoluteTime(Time) \
+ ( IsPositive(Time->SimpleTime) )
+
+
+//
+// BOOLEAN
+// IsDeltaTime(
+// IN PSTANDARDTIME Time
+// )
+// Returns:
+// TRUE - if the given time is a delta time
+// FALSE - If the given time is not a delta time
+//
+
+#define IsDeltaTime(Time) \
+ ( !IsAbsoluteTime(Time) )
+
+
+//
+// BOOLEAN
+// GreaterThanTime(
+// IN PLARGE_INTEGER Time1,
+// IN PLARGE_INTEGER Time2
+// )
+// Returns:
+// TRUE - If Time1 is greater (older) than Time2
+// FALSE - If not
+//
+// BUG: Byte order dependant
+// BUG: Only works on absolute times
+//
+
+#define GreaterThanTime(Time1, Time2) \
+ ( \
+ ((Time1).HighPart > (Time2).HighPart) \
+ || \
+ ( \
+ ((Time1).HighPart == (Time2).HighPart) \
+ && \
+ ((Time1).LowPart > (Time2).LowPart) \
+ ) \
+ )
+
+
+//
+// BOOLEAN
+// GreaterThanStandardTime(
+// IN PSTANDARD_TIME Time1,
+// IN PSTANDARD_TIME Time2
+// )
+// Returns:
+// TRUE - If Time1 is greater (older) than Time2
+// FALSE - If not
+//
+
+#define GreaterThanStdTime(Time1, Time2) \
+ GreaterThanTime((Time1).SimpleTime, (Time2).SimpleTime)
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// /
+// The following definitions and declarations are some important constants /
+// used in the time conversion routines /
+// /
+//////////////////////////////////////////////////////////////////////////////
+
+//
+// This is the week day that January 1st, 1601 fell on (a Monday)
+//
+
+#define WEEKDAY_OF_1601 1
+
+//
+// These are known constants used to convert 1970 and 1980 times to 1601
+// times. They are the number of seconds from the 1601 base to the start
+// of 1970 and the start of 1980. The number of seconds from 1601 to
+// 1970 is 369 years worth, or (369 * 365) + 89 leap days = 134774 days, or
+// 134774 * 864000 seconds, which is equal to the large integer defined
+// below. The number of seconds from 1601 to 1980 is 379 years worth, or etc.
+//
+// These are declared in time.c
+//
+
+extern LARGE_INTEGER SecondsToStartOf1970;
+extern LARGE_INTEGER SecondsToStartOf1980;
+
+
+//
+// ULONG
+// ElapsedDaysToYears (
+// IN ULONG ElapsedDays
+// );
+//
+// To be completely true to the Gregorian calendar the equation to
+// go from days to years is really
+//
+// ElapsedDays / 365.2425
+//
+// But because we are doing the computation in ulong integer arithmetic
+// and the LARGE_INTEGER variable limits the number of expressible days to around
+// 11,000,000 we use the following computation
+//
+// (ElapsedDays * 128 + 127) / (365.2425 * 128)
+//
+// which will be off from the Gregorian calendar in about 150,000 years
+// but that doesn't really matter because LARGE_INTEGER can only express around
+// 30,000 years
+//
+
+#define ElapsedDaysToYears(DAYS) ( \
+ ((DAYS) * 128 + 127) / 46751 \
+ )
+
+//
+// ULONG
+// NumberOfLeapYears (
+// IN ULONG ElapsedYears
+// );
+//
+// The number of leap years is simply the number of years divided by 4
+// minus years divided by 100 plus years divided by 400. This says
+// that every four years is a leap year except centuries, and the
+// exception to the exception is the quadricenturies
+//
+
+#define NumberOfLeapYears(YEARS) ( \
+ ((YEARS) / 4) - ((YEARS) / 100) + ((YEARS) / 400) \
+ )
+
+//
+// ULONG
+// ElapsedYearsToDays (
+// IN ULONG ElapsedYears
+// );
+//
+// The number of days contained in elapsed years is simply the number
+// of years times 365 (because every year has at least 365 days) plus
+// the number of leap years there are (i.e., the number of 366 days years)
+//
+
+#define ElapsedYearsToDays(YEARS) ( \
+ ((YEARS) * 365) + NumberOfLeapYears(YEARS) \
+ )
+
+//
+// BOOLEAN
+// IsLeapYear (
+// IN ULONG ElapsedYears
+// );
+//
+// If it is an even 400 or a non century leapyear then the
+// answer is true otherwise it's false
+//
+
+#define IsLeapYear(YEARS) ( \
+ (((YEARS) % 400 == 0) || \
+ ((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
+ TRUE \
+ : \
+ FALSE \
+ )
+
+//
+// ULONG
+// MaxDaysInMonth (
+// IN ULONG Year,
+// IN ULONG Month
+// );
+//
+// The maximum number of days in a month depend on the year and month.
+// It is the difference between the days to the month and the days
+// to the following month
+//
+
+#define MaxDaysInMonth(YEAR,MONTH) ( \
+ IsLeapYear(YEAR) ? \
+ LeapYearDaysPrecedingMonth[(MONTH) + 1] - \
+ LeapYearDaysPrecedingMonth[(MONTH)] \
+ : \
+ NormalYearDaysPrecedingMonth[(MONTH) + 1] - \
+ NormalYearDaysPrecedingMonth[(MONTH)] \
+ )
+
+
+//
+// Local utlity function prototypes
+//
+
+VOID
+RtlpConvert48To64(
+ IN PSTDTIME_ERROR num48,
+ OUT LARGE_INTEGER *num64
+ );
+
+NTSTATUS
+RtlpConvert64To48(
+ IN LARGE_INTEGER num64,
+ OUT PSTDTIME_ERROR num48
+ );
+
+LARGE_INTEGER
+RtlpTimeToLargeInt(
+ IN LARGE_INTEGER Time
+ );
+
+LARGE_INTEGER
+RtlpLargeIntToTime(
+ IN LARGE_INTEGER Int
+ );
+
+NTSTATUS
+RtlpAdd48Int(
+ IN PSTDTIME_ERROR First48,
+ IN PSTDTIME_ERROR Second48,
+ IN PSTDTIME_ERROR Result48
+ );
+
+NTSTATUS
+RtlpAddTime(
+ IN LARGE_INTEGER Time1,
+ IN LARGE_INTEGER Time2,
+ OUT PLARGE_INTEGER Result
+ );
+
+NTSTATUS
+RtlpSubtractTime(
+ IN LARGE_INTEGER Time1,
+ IN LARGE_INTEGER Time2,
+ OUT PLARGE_INTEGER Result
+ );
+
+LARGE_INTEGER
+RtlpAbsTime(
+ IN LARGE_INTEGER Time
+ );
+
+#endif //_STD_TIME_P_
diff --git a/private/ntos/rtl/stktrace.c b/private/ntos/rtl/stktrace.c
new file mode 100644
index 000000000..3cc5e819f
--- /dev/null
+++ b/private/ntos/rtl/stktrace.c
@@ -0,0 +1,394 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ stktrace.c
+
+Abstract:
+
+ This module implements routines to snapshot a set of stack back traces
+ in a data base. Useful for heap allocators to track allocation requests
+ cheaply.
+
+Author:
+
+ Steve Wood (stevewo) 29-Jan-1992
+
+Revision History:
+
+--*/
+
+#include <ntos.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <zwapi.h>
+#include <stktrace.h>
+#include <heap.h>
+#include <heappriv.h>
+
+BOOLEAN
+NtdllOkayToLockRoutine(
+ IN PVOID Lock
+ );
+
+#ifndef RtlGetCallersAddress
+
+VOID
+RtlGetCallersAddress(
+ OUT PVOID *CallersAddress,
+ OUT PVOID *CallersCaller
+ )
+{
+ PVOID BackTrace[ 2 ];
+#if i386 && !NTOS_KERNEL_RUNTIME
+ ULONG Hash;
+
+ RtlCaptureStackBackTrace( 2,
+ 2,
+ BackTrace,
+ &Hash
+ );
+#else
+ BackTrace[ 0 ] = NULL;
+ BackTrace[ 1 ] = NULL;
+#endif // i386 && !NTOS_KERNEL_RUNTIME
+
+ if (ARGUMENT_PRESENT( CallersAddress )) {
+ *CallersAddress = BackTrace[ 0 ];
+ }
+
+ if (ARGUMENT_PRESENT( CallersCaller )) {
+ *CallersCaller = BackTrace[ 1 ];
+ }
+
+ return;
+}
+#endif // ndef RtlGetCallersAddress
+
+#if i386 && (!NTOS_KERNEL_RUNTIME || !FPO)
+PSTACK_TRACE_DATABASE RtlpStackTraceDataBase;
+
+PRTL_STACK_TRACE_ENTRY
+RtlpExtendStackTraceDataBase(
+ IN PRTL_STACK_TRACE_ENTRY InitialValue,
+ IN ULONG Size
+ );
+
+
+NTSTATUS
+RtlInitStackTraceDataBaseEx(
+ IN PVOID CommitBase,
+ IN ULONG CommitSize,
+ IN ULONG ReserveSize,
+ IN PRTL_INITIALIZE_LOCK_ROUTINE InitializeLockRoutine,
+ IN PRTL_ACQUIRE_LOCK_ROUTINE AcquireLockRoutine,
+ IN PRTL_RELEASE_LOCK_ROUTINE ReleaseLockRoutine,
+ IN PRTL_OKAY_TO_LOCK_ROUTINE OkayToLockRoutine
+ );
+
+NTSTATUS
+RtlInitStackTraceDataBaseEx(
+ IN PVOID CommitBase,
+ IN ULONG CommitSize,
+ IN ULONG ReserveSize,
+ IN PRTL_INITIALIZE_LOCK_ROUTINE InitializeLockRoutine,
+ IN PRTL_ACQUIRE_LOCK_ROUTINE AcquireLockRoutine,
+ IN PRTL_RELEASE_LOCK_ROUTINE ReleaseLockRoutine,
+ IN PRTL_OKAY_TO_LOCK_ROUTINE OkayToLockRoutine
+ )
+{
+ NTSTATUS Status;
+ PSTACK_TRACE_DATABASE DataBase;
+
+ DataBase = (PSTACK_TRACE_DATABASE)CommitBase;
+ if (CommitSize == 0) {
+ CommitSize = PAGE_SIZE;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&CommitBase,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ KdPrint(( "RTL: Unable to commit space to extend stack trace data base - Status = %lx\n",
+ Status
+ ));
+ return Status;
+ }
+
+ DataBase->PreCommitted = FALSE;
+ }
+ else
+ if (CommitSize == ReserveSize) {
+ RtlZeroMemory( DataBase, sizeof( *DataBase ) );
+ DataBase->PreCommitted = TRUE;
+ }
+ else {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ DataBase->CommitBase = CommitBase;
+ DataBase->NumberOfBuckets = 37;
+ DataBase->NextFreeLowerMemory = (PCHAR)
+ (&DataBase->Buckets[ DataBase->NumberOfBuckets ]);
+ DataBase->NextFreeUpperMemory = (PCHAR)CommitBase + ReserveSize;
+
+ if(!DataBase->PreCommitted) {
+ DataBase->CurrentLowerCommitLimit = (PCHAR)CommitBase + CommitSize;
+ DataBase->CurrentUpperCommitLimit = (PCHAR)CommitBase + ReserveSize;
+ }
+ else {
+ RtlZeroMemory( &DataBase->Buckets[ 0 ],
+ DataBase->NumberOfBuckets * sizeof( DataBase->Buckets[ 0 ] )
+ );
+ }
+
+ DataBase->EntryIndexArray = (PRTL_STACK_TRACE_ENTRY *)DataBase->NextFreeUpperMemory;
+
+ DataBase->AcquireLockRoutine = AcquireLockRoutine;
+ DataBase->ReleaseLockRoutine = ReleaseLockRoutine;
+ DataBase->OkayToLockRoutine = OkayToLockRoutine;
+
+ Status = (InitializeLockRoutine)( &DataBase->Lock.CriticalSection );
+ if (!NT_SUCCESS( Status )) {
+ KdPrint(( "RTL: Unable to initialize stack trace data base CriticalSection, Status = %lx\n",
+ Status
+ ));
+ return( Status );
+ }
+
+ RtlpStackTraceDataBase = DataBase;
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+RtlInitializeStackTraceDataBase(
+ IN PVOID CommitBase,
+ IN ULONG CommitSize,
+ IN ULONG ReserveSize
+ )
+{
+#ifdef NTOS_KERNEL_RUNTIME
+
+BOOLEAN
+ExOkayToLockRoutine(
+ IN PVOID Lock
+ );
+
+ return RtlInitStackTraceDataBaseEx(
+ CommitBase,
+ CommitSize,
+ ReserveSize,
+ ExInitializeResource,
+ ExAcquireResourceExclusive,
+ (PRTL_RELEASE_LOCK_ROUTINE)ExReleaseResourceLite,
+ ExOkayToLockRoutine
+ );
+#else
+ return RtlInitStackTraceDataBaseEx(
+ CommitBase,
+ CommitSize,
+ ReserveSize,
+ RtlInitializeCriticalSection,
+ RtlEnterCriticalSection,
+ RtlLeaveCriticalSection,
+ NtdllOkayToLockRoutine
+ );
+#endif
+}
+
+
+PSTACK_TRACE_DATABASE
+RtlpAcquireStackTraceDataBase( VOID )
+{
+ if (RtlpStackTraceDataBase != NULL) {
+ if (RtlpStackTraceDataBase->DumpInProgress ||
+ !(RtlpStackTraceDataBase->OkayToLockRoutine)( &RtlpStackTraceDataBase->Lock.CriticalSection )
+ ) {
+ return( NULL );
+ }
+
+ (RtlpStackTraceDataBase->AcquireLockRoutine)( &RtlpStackTraceDataBase->Lock.CriticalSection );
+ }
+
+ return( RtlpStackTraceDataBase );
+}
+
+VOID
+RtlpReleaseStackTraceDataBase( VOID )
+{
+ (RtlpStackTraceDataBase->ReleaseLockRoutine)( &RtlpStackTraceDataBase->Lock.CriticalSection );
+ return;
+}
+
+PRTL_STACK_TRACE_ENTRY
+RtlpExtendStackTraceDataBase(
+ IN PRTL_STACK_TRACE_ENTRY InitialValue,
+ IN ULONG Size
+ )
+{
+ NTSTATUS Status;
+ PRTL_STACK_TRACE_ENTRY p, *pp;
+ ULONG CommitSize;
+
+ pp = (PRTL_STACK_TRACE_ENTRY *)RtlpStackTraceDataBase->NextFreeUpperMemory;
+ if (!RtlpStackTraceDataBase->PreCommitted &&
+ (PCHAR)(pp - 1) < (PCHAR)RtlpStackTraceDataBase->CurrentUpperCommitLimit
+ ) {
+ RtlpStackTraceDataBase->CurrentUpperCommitLimit = (PVOID)
+ ((PCHAR)RtlpStackTraceDataBase->CurrentUpperCommitLimit - PAGE_SIZE);
+
+ if (RtlpStackTraceDataBase->CurrentUpperCommitLimit <
+ RtlpStackTraceDataBase->CurrentLowerCommitLimit
+ ) {
+ return( NULL );
+ }
+
+ CommitSize = PAGE_SIZE;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&RtlpStackTraceDataBase->CurrentUpperCommitLimit,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ KdPrint(( "RTL: Unable to commit space to extend stack trace data base - Status = %lx\n",
+ Status
+ ));
+ return( NULL );
+ }
+ }
+ RtlpStackTraceDataBase->NextFreeUpperMemory -= sizeof( *pp );
+
+ p = (PRTL_STACK_TRACE_ENTRY)RtlpStackTraceDataBase->NextFreeLowerMemory;
+ if (!RtlpStackTraceDataBase->PreCommitted &&
+ ((PCHAR)p + Size) > (PCHAR)RtlpStackTraceDataBase->CurrentLowerCommitLimit
+ ) {
+ if (RtlpStackTraceDataBase->CurrentLowerCommitLimit >=
+ RtlpStackTraceDataBase->CurrentUpperCommitLimit
+ ) {
+ return( NULL );
+ }
+
+ CommitSize = Size;
+ Status = ZwAllocateVirtualMemory( NtCurrentProcess(),
+ (PVOID *)&RtlpStackTraceDataBase->CurrentLowerCommitLimit,
+ 0,
+ &CommitSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ KdPrint(( "RTL: Unable to commit space to extend stack trace data base - Status = %lx\n",
+ Status
+ ));
+ return( NULL );
+ }
+
+ RtlpStackTraceDataBase->CurrentLowerCommitLimit =
+ (PCHAR)RtlpStackTraceDataBase->CurrentLowerCommitLimit + CommitSize;
+ }
+ RtlpStackTraceDataBase->NextFreeLowerMemory += Size;
+
+ if (RtlpStackTraceDataBase->PreCommitted &&
+ RtlpStackTraceDataBase->NextFreeLowerMemory >= RtlpStackTraceDataBase->NextFreeUpperMemory
+ ) {
+ RtlpStackTraceDataBase->NextFreeUpperMemory += sizeof( *pp );
+ RtlpStackTraceDataBase->NextFreeLowerMemory -= Size;
+ return( NULL );
+ }
+
+ RtlMoveMemory( p, InitialValue, Size );
+ p->HashChain = NULL;
+ p->TraceCount = 0;
+ p->Index = (USHORT)(++RtlpStackTraceDataBase->NumberOfEntriesAdded);
+ *--pp = p;
+
+ return( p );
+}
+
+USHORT
+RtlLogStackBackTrace( VOID )
+{
+ PSTACK_TRACE_DATABASE DataBase;
+ RTL_STACK_TRACE_ENTRY StackTrace;
+ PRTL_STACK_TRACE_ENTRY p, *pp;
+ ULONG Hash, RequestedSize, DepthSize;
+
+ if (RtlpStackTraceDataBase == NULL) {
+ return 0;
+ }
+
+ Hash = 0;
+ try {
+ StackTrace.Depth = RtlCaptureStackBackTrace( 1,
+ MAX_STACK_DEPTH,
+ StackTrace.BackTrace,
+ &Hash
+ );
+ }
+ except(EXCEPTION_EXECUTE_HANDLER) {
+ StackTrace.Depth = 0;
+ }
+
+ if (StackTrace.Depth == 0) {
+ return 0;
+ }
+
+ DataBase = RtlpAcquireStackTraceDataBase();
+ if (DataBase == NULL) {
+ return( 0 );
+ }
+
+ DataBase->NumberOfEntriesLookedUp++;
+
+ try {
+ DepthSize = StackTrace.Depth * sizeof( StackTrace.BackTrace[ 0 ] );
+ pp = &DataBase->Buckets[ Hash % DataBase->NumberOfBuckets ];
+ while (p = *pp) {
+ if (p->Depth == StackTrace.Depth &&
+ RtlCompareMemory( &p->BackTrace[ 0 ],
+ &StackTrace.BackTrace[ 0 ],
+ DepthSize
+ ) == DepthSize
+ ) {
+ break;
+ }
+ else {
+ pp = &p->HashChain;
+ }
+ }
+
+ if (p == NULL) {
+ RequestedSize = FIELD_OFFSET( RTL_STACK_TRACE_ENTRY, BackTrace ) +
+ DepthSize;
+
+ p = RtlpExtendStackTraceDataBase( &StackTrace, RequestedSize );
+ if (p != NULL) {
+ *pp = p;
+ }
+ }
+ }
+ except(EXCEPTION_EXECUTE_HANDLER) {
+ p = NULL;
+ }
+
+ RtlpReleaseStackTraceDataBase();
+
+ if (p != NULL) {
+ p->TraceCount++;
+ return( p->Index );
+ }
+ else {
+ return( 0 );
+ }
+
+ return 0;
+}
+
+
+#endif // i386 && !NTOS_KERNEL_RUNTIME
diff --git a/private/ntos/rtl/string.c b/private/ntos/rtl/string.c
new file mode 100644
index 000000000..984fdac94
--- /dev/null
+++ b/private/ntos/rtl/string.c
@@ -0,0 +1,779 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ strings.c
+
+Abstract:
+
+ This module defines functions for manipulating counted strings (STRING).
+ A counted string is a data structure containing three fields. The Buffer
+ field is a pointer to the string itself. The MaximumLength field contains
+ the maximum number of bytes that can be stored in the memory pointed to
+ by the Buffer field. The Length field contains the current length, in
+ bytes, of the string pointed to by the Buffer field. Users of counted
+ strings should not make any assumptions about the existence of a null
+ byte at the end of the string, unless the null byte is explicitly
+ included in the Length of the string.
+
+Author:
+
+ Steve Wood (stevewo) 31-Mar-1989
+
+Revision History:
+
+ 22-Sep-1993 JulieB Fixed TO_UPPER macro for chars above 0x7f.
+
+
+--*/
+
+#include "string.h"
+#include "nt.h"
+#include "ntrtlp.h"
+
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE,RtlUpperChar)
+#pragma alloc_text(PAGE,RtlUpperString)
+#endif
+
+//
+// Global data used for translations.
+//
+extern PUSHORT NlsAnsiToUnicodeData; // Ansi CP to Unicode translation table
+extern PCH NlsUnicodeToAnsiData; // Unicode to Ansi CP translation table
+extern PUSHORT NlsLeadByteInfo; // Lead byte info for ACP
+extern PUSHORT NlsUnicodeToMbAnsiData; // Unicode to Multibyte Ansi CP translation table
+extern BOOLEAN NlsMbCodePageTag; // TRUE -> Multibyte ACP, FALSE -> Singlebyte ACP
+
+
+
+#if !defined(_M_IX86)
+
+VOID
+RtlInitString(
+ OUT PSTRING DestinationString,
+ IN PCSZ SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlInitString 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.
+
+--*/
+
+{
+ ULONG Length;
+
+ DestinationString->Buffer = (PCHAR)SourceString;
+ if (ARGUMENT_PRESENT( SourceString )) {
+ Length = strlen(SourceString);
+ DestinationString->Length = (USHORT)Length;
+ DestinationString->MaximumLength = (USHORT)(Length+1);
+ }
+ else {
+ DestinationString->Length = 0;
+ DestinationString->MaximumLength = 0;
+ }
+}
+
+
+VOID
+RtlInitAnsiString(
+ OUT PANSI_STRING DestinationString,
+ IN PCSZ 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.
+
+--*/
+
+{
+ ULONG Length;
+
+ DestinationString->Buffer = (PCHAR)SourceString;
+ if (ARGUMENT_PRESENT( SourceString )) {
+ Length = strlen(SourceString);
+ DestinationString->Length = (USHORT)Length;
+ DestinationString->MaximumLength = (USHORT)(Length+1);
+ }
+ else {
+ DestinationString->Length = 0;
+ DestinationString->MaximumLength = 0;
+ }
+}
+
+
+VOID
+RtlInitUnicodeString(
+ OUT PUNICODE_STRING DestinationString,
+ IN PCWSTR 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.
+
+--*/
+
+{
+ ULONG Length;
+
+ DestinationString->Buffer = (PWSTR)SourceString;
+ if (ARGUMENT_PRESENT( SourceString )) {
+ Length = wcslen( SourceString ) * sizeof( WCHAR );
+ DestinationString->Length = (USHORT)Length;
+ DestinationString->MaximumLength = (USHORT)(Length + sizeof(UNICODE_NULL));
+ }
+ else {
+ DestinationString->MaximumLength = 0;
+ DestinationString->Length = 0;
+ }
+}
+
+#endif // !defined(_M_IX86)
+
+
+VOID
+RtlCopyString(
+ OUT PSTRING DestinationString,
+ IN PSTRING SourceString OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlCopyString function copies the SourceString to the
+ DestinationString. If SourceString is not specified, then
+ the Length field of DestinationString is set to zero. The
+ MaximumLength and Buffer fields of DestinationString are not
+ modified by this function.
+
+ The number of bytes copied from the SourceString is either the
+ Length of SourceString or the MaximumLength of DestinationString,
+ whichever is smaller.
+
+Arguments:
+
+ DestinationString - Pointer to the destination string.
+
+ SourceString - Optional pointer to the source string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSZ src, dst;
+ ULONG n;
+
+ if (ARGUMENT_PRESENT( SourceString )) {
+ dst = DestinationString->Buffer;
+ src = SourceString->Buffer;
+ n = SourceString->Length;
+ if ((USHORT)n > DestinationString->MaximumLength) {
+ n = DestinationString->MaximumLength;
+ }
+ DestinationString->Length = (USHORT)n;
+ while (n) {
+ *dst++ = *src++;
+ n--;
+ }
+ }
+ else {
+ DestinationString->Length = 0;
+ }
+}
+
+CHAR
+RtlUpperChar (
+ register IN CHAR Character
+ )
+
+/*++
+
+Routine Description:
+
+ This routine returns a character uppercased
+.
+Arguments:
+
+ IN CHAR Character - Supplies the character to upper case
+
+Return Value:
+
+ CHAR - Uppercased version of the charac
+ter
+--*/
+
+{
+ //
+ // NOTE: This assumes an ANSI string and it does NOT upper case
+ // DOUBLE BYTE characters properly.
+ //
+
+ //
+ // Handle a - z separately.
+ //
+ if (Character <= 'z') {
+ if (Character >= 'a') {
+ return Character ^ 0x20;
+ }
+ else {
+ return Character;
+ }
+ }
+ else {
+ WCHAR wCh;
+
+ /*
+ * Handle extended characters.
+ */
+ if (!NlsMbCodePageTag) {
+ //
+ // Single byte code page.
+ //
+ wCh = NlsAnsiToUnicodeData[(UCHAR)Character];
+ wCh = NLS_UPCASE(wCh);
+ return NlsUnicodeToAnsiData[(USHORT)wCh];
+ }
+ else {
+ //
+ // Multi byte code page. Do nothing to the character
+ // if it's a lead byte or if the translation of the
+ // upper case Unicode character is a DBCS character.
+ //
+ if (!NlsLeadByteInfo[Character]) {
+ wCh = NlsAnsiToUnicodeData[(UCHAR)Character];
+ wCh = NLS_UPCASE(wCh);
+ wCh = NlsUnicodeToMbAnsiData[(USHORT)wCh];
+ if (!HIBYTE(wCh)) {
+ return LOBYTE(wCh);
+ }
+ }
+ }
+ }
+
+ return Character;
+}
+
+LONG
+RtlCompareString(
+ IN PSTRING String1,
+ IN PSTRING String2,
+ IN BOOLEAN CaseInSensitive
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlCompareString function compares two counted strings. The return
+ value indicates if the strings are equal or String1 is less than String2
+ or String1 is greater than String2.
+
+ The CaseInSensitive parameter specifies if case is to be ignored when
+ doing the comparison.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+ CaseInsensitive - TRUE if case should be ignored when doing the
+ comparison.
+
+Return Value:
+
+ Signed value that gives the results of the comparison:
+
+ Zero - String1 equals String2
+
+ < Zero - String1 less than String2
+
+ > Zero - String1 greater than String2
+
+
+--*/
+
+{
+
+ PUCHAR s1, s2, Limit;
+ LONG n1, n2;
+ UCHAR c1, c2;
+
+ s1 = String1->Buffer;
+ s2 = String2->Buffer;
+ n1 = String1->Length;
+ n2 = String2->Length;
+ Limit = s1 + (n1 <= n2 ? n1 : n2);
+ if (CaseInSensitive) {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 !=c2) {
+ c1 = RtlUpperChar(c1);
+ c2 = RtlUpperChar(c2);
+ if (c1 != c2) {
+ return (LONG)c1 - (LONG)c2;
+ }
+ }
+ }
+
+ } else {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+ return (LONG)c1 - (LONG)c2;
+ }
+ }
+ }
+
+ return n1 - n2;
+}
+
+BOOLEAN
+RtlEqualString(
+ IN PSTRING String1,
+ IN PSTRING String2,
+ IN BOOLEAN CaseInSensitive
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlEqualString function compares two counted strings for equality.
+
+ The CaseInSensitive parameter specifies if case is to be ignored when
+ doing the comparison.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+ CaseInsensitive - TRUE if case should be ignored when doing the
+ comparison.
+
+Return Value:
+
+ Boolean value that is TRUE if String1 equals String2 and FALSE otherwise.
+
+--*/
+
+{
+
+ PUCHAR s1, s2, Limit;
+ LONG n1, n2;
+ UCHAR c1, c2;
+
+ n1 = String1->Length;
+ n2 = String2->Length;
+ if (n1 == n2) {
+ s1 = String1->Buffer;
+ s2 = String2->Buffer;
+ Limit = s1 + n1;
+ if (CaseInSensitive) {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+ c1 = RtlUpperChar(c1);
+ c2 = RtlUpperChar(c2);
+ if (c1 != c2) {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+
+ } else {
+ while (s1 < Limit) {
+ c1 = *s1++;
+ c2 = *s2++;
+ if (c1 != c2) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ } else {
+ return FALSE;
+ }
+}
+
+BOOLEAN
+RtlPrefixString(
+ IN PSTRING String1,
+ IN PSTRING String2,
+ IN BOOLEAN CaseInSensitive
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlPrefixString function determines if the String1 counted string
+ parameter is a prefix of the String2 counted string parameter.
+
+ The CaseInSensitive parameter specifies if case is to be ignored when
+ doing the comparison.
+
+Arguments:
+
+ String1 - Pointer to the first string.
+
+ String2 - Pointer to the second string.
+
+ CaseInsensitive - TRUE if case should be ignored when doing the
+ comparison.
+
+Return Value:
+
+ Boolean value that is TRUE if String1 equals a prefix of String2 and
+ FALSE otherwise.
+
+--*/
+
+{
+ PSZ s1, s2;
+ USHORT n;
+ UCHAR c1, c2;
+
+ s1 = String1->Buffer;
+ s2 = String2->Buffer;
+ n = String1->Length;
+ if (String2->Length < n) {
+ return( FALSE );
+ }
+
+ if (CaseInSensitive) {
+ while (n) {
+ c1 = *s1++;
+ c2 = *s2++;
+
+ if (c1 != c2 && RtlUpperChar(c1) != RtlUpperChar(c2)) {
+ return( FALSE );
+ }
+
+ n--;
+ }
+ }
+ else {
+ while (n) {
+ if (*s1++ != *s2++) {
+ return( FALSE );
+ }
+
+ n--;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+RtlCreateUnicodeStringFromAsciiz(
+ OUT PUNICODE_STRING DestinationString,
+ IN PCSZ SourceString
+ )
+{
+ ANSI_STRING AnsiString;
+ NTSTATUS Status;
+
+ RtlInitAnsiString( &AnsiString, SourceString );
+ Status = RtlAnsiStringToUnicodeString( DestinationString, &AnsiString, TRUE );
+ if (NT_SUCCESS( Status )) {
+ return( TRUE );
+ }
+ else {
+ return( FALSE );
+ }
+}
+
+
+VOID
+RtlUpperString(
+ IN PSTRING DestinationString,
+ IN PSTRING SourceString
+ )
+
+/*++
+
+Routine Description:
+
+ The RtlUpperString function copies the SourceString to the
+ DestinationString, converting it to upper case. The MaximumLength
+ and Buffer fields of DestinationString are not modified by this
+ function.
+
+ The number of bytes copied from the SourceString is either the
+ Length of SourceString or the MaximumLength of DestinationString,
+ whichever is smaller.
+
+Arguments:
+
+ DestinationString - Pointer to the destination string.
+
+ SourceString - Pointer to the source string.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSZ src, dst;
+ ULONG n;
+
+ dst = DestinationString->Buffer;
+ src = SourceString->Buffer;
+ n = SourceString->Length;
+ if ((USHORT)n > DestinationString->MaximumLength) {
+ n = DestinationString->MaximumLength;
+ }
+ DestinationString->Length = (USHORT)n;
+ while (n) {
+ *dst++ = RtlUpperChar(*src++);
+ n--;
+ }
+}
+
+
+NTSTATUS
+RtlAppendAsciizToString (
+ IN PSTRING Destination,
+ IN PCSZ Source OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine appends the supplied ASCIIZ string to an existing PSTRING.
+
+ It will copy bytes from the Source PSZ to the destination PSTRING up to
+ the destinations PSTRING->MaximumLength field.
+
+Arguments:
+
+ IN PSTRING Destination, - Supplies a pointer to the destination string
+ IN PSZ Source - Supplies the string to append to the destination
+
+Return Value:
+
+ STATUS_SUCCESS - The source string was successfully appended to the
+ destination counted string.
+
+ STATUS_BUFFER_TOO_SMALL - The destination string length was not big
+ enough to allow the source string to be appended. The Destination
+ string length is not updated.
+
+--*/
+
+{
+ USHORT n;
+
+ if (ARGUMENT_PRESENT( Source )) {
+ n = (USHORT)strlen( Source );
+
+ if ((n + Destination->Length) > Destination->MaximumLength) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ RtlMoveMemory( &Destination->Buffer[ Destination->Length ], Source, n );
+ Destination->Length += n;
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+
+NTSTATUS
+RtlAppendStringToString (
+ IN PSTRING Destination,
+ IN PSTRING Source
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will concatinate two PSTRINGs together. It will copy
+ bytes from the source up to the MaximumLength of the destination.
+
+Arguments:
+
+ IN PSTRING Destination, - Supplies the destination string
+ IN PSTRING Source - Supplies the source for the string copy
+
+Return Value:
+
+ STATUS_SUCCESS - The source string was successfully appended to the
+ destination counted string.
+
+ STATUS_BUFFER_TOO_SMALL - The destination string length was not big
+ enough to allow the source string to be appended. The Destination
+ string length is not updated.
+
+--*/
+
+{
+ USHORT n = Source->Length;
+
+ if (n) {
+ if ((n + Destination->Length) > Destination->MaximumLength) {
+ return( STATUS_BUFFER_TOO_SMALL );
+ }
+
+ RtlMoveMemory( &Destination->Buffer[ Destination->Length ],
+ Source->Buffer,
+ n
+ );
+ Destination->Length += n;
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+#ifndef i386
+
+ULONG
+RtlCompareMemoryUlong(
+ PVOID Source,
+ ULONG Length,
+ ULONG Pattern
+ )
+
+/*++
+
+Routine Description:
+
+ This function compares two blocks of memory and returns the number
+ of bytes that compared equal.
+
+ N.B. This routine requires that the source address is aligned on a
+ longword boundary and that the length is an even multiple of
+ longwords.
+
+Arguments:
+
+ Source - Supplies a pointer to the block of memory to compare against.
+
+ Length - Supplies the Length, in bytes, of the memory to be
+ compared.
+
+ Pattern - Supplies a 32-bit pattern to compare against the block of
+ memory.
+
+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. Returns zero if either the Source
+ address is not longword aligned or the length is not a multiple of
+ longwords.
+
+--*/
+{
+ ULONG CountLongs;
+ PULONG p = (PULONG)Source;
+ PCHAR p1, p2;
+
+ if (((ULONG)p & (sizeof( ULONG )-1)) ||
+ (Length & (sizeof( ULONG )-1))
+ ) {
+ return( 0 );
+ }
+
+ CountLongs = Length / sizeof( ULONG );
+ while (CountLongs--) {
+ if (*p++ != Pattern) {
+ p1 = (PCHAR)(p - 1);
+ p2 = (PCHAR)&Pattern;
+ Length = p1 - (PCHAR)Source;
+ while (*p1++ == *p2++) {
+ if (p1 > (PCHAR)p) {
+ break;
+ }
+
+ Length++;
+ }
+ }
+ }
+
+ return( Length );
+}
+
+#endif // ndef i386
diff --git a/private/ntos/rtl/tacl.c b/private/ntos/rtl/tacl.c
new file mode 100644
index 000000000..5ee9434b0
--- /dev/null
+++ b/private/ntos/rtl/tacl.c
@@ -0,0 +1,307 @@
+//////////////////////////////////////////////////////////////////////////
+// WARNING - WARNING - WARNING - WARNING - WARNING - WARNING - WARNING //
+// //
+// This test file is not current with the security implementation. //
+// This file contains references to data types and APIs that do not //
+// exist. //
+// //
+//////////////////////////////////////////////////////////////////////////
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tacl.c
+
+Abstract:
+
+ Test program for the acl editing package
+
+Author:
+
+ Gary Kimura [GaryKi] 19-Nov-1989
+
+Revision History:
+
+ v4: robertre
+ updated ACL_REVISION
+ RichardW - updated ACE_HEADER
+
+--*/
+
+#include <stdio.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+
+VOID
+RtlDumpAcl(
+ IN PACL Acl
+ );
+
+UCHAR FredAclBuffer[128];
+UCHAR WilmaAclBuffer[128];
+UCHAR PebbleAclBuffer[128];
+UCHAR DinoAclBuffer[128];
+UCHAR BarneyAclBuffer[128];
+UCHAR BettyAclBuffer[128];
+UCHAR BambamAclBuffer[128];
+
+UCHAR GuidMaskBuffer[512];
+STANDARD_ACE AceListBuffer[2];
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ PACL FredAcl = (PACL)FredAclBuffer;
+ PACL WilmaAcl = (PACL)WilmaAclBuffer;
+ PACL PebbleAcl = (PACL)PebbleAclBuffer;
+ PACL DinoAcl = (PACL)DinoAclBuffer;
+ PACL BarneyAcl = (PACL)BarneyAclBuffer;
+ PACL BettyAcl = (PACL)BettyAclBuffer;
+ PACL BambamAcl = (PACL)BambamAclBuffer;
+
+ PMASK_GUID_PAIRS GuidMasks = (PMASK_GUID_PAIRS)GuidMaskBuffer;
+
+ ACL_REVISION_INFORMATION AclRevisionInfo;
+ ACL_SIZE_INFORMATION AclSizeInfo;
+
+ //
+ // We're starting the test
+ //
+
+ DbgPrint("Start Acl Test\n");
+
+ //
+ // test create acl
+ //
+
+ if (!NT_SUCCESS(RtlCreateAcl(FredAcl, 128, 1))) {
+ DbgPrint("RtlCreateAcl Error\n");
+ }
+
+ RtlDumpAcl(FredAcl);
+ DbgPrint("\n");
+
+ //
+ // test add ace to add two aces to an empty acl
+ //
+
+ AceListBuffer[0].Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AceListBuffer[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceListBuffer[0].Header.AceFlags = 0;
+ AceListBuffer[0].Mask = 0x22222222;
+ CopyGuid(&AceListBuffer[0].Guid, &FredGuid);
+
+ AceListBuffer[1].Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AceListBuffer[1].Header.AceSize = sizeof(STANDARD_ACE);
+ AceListBuffer[1].Header.AceFlags = 0;
+ AceListBuffer[1].Mask = 0x44444444;
+ CopyGuid(&AceListBuffer[1].Guid, &WilmaGuid);
+
+ if (!NT_SUCCESS(RtlAddAce(FredAcl, 1, 0, AceListBuffer, 2*sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce Error\n");
+ }
+
+ RtlDumpAcl(FredAcl);
+ DbgPrint("\n");
+
+ //
+ // test add ace to add one to the beginning of an acl
+ //
+
+ AceListBuffer[0].Header.AceType = SYSTEM_AUDIT_ACE_TYPE;
+ AceListBuffer[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceListBuffer[0].Header.AceFlags = 0;
+ AceListBuffer[0].Mask = 0x11111111;
+ CopyGuid(&AceListBuffer[0].Guid, &PebbleGuid);
+
+ if (!NT_SUCCESS(RtlAddAce(FredAcl, 1, 0, AceListBuffer, sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce Error\n");
+ }
+
+ RtlDumpAcl(FredAcl);
+ DbgPrint("\n");
+
+ //
+ // test add ace to add one to the middle of an acl
+ //
+
+ AceListBuffer[0].Header.AceType = ACCESS_DENIED_ACE_TYPE;
+ AceListBuffer[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceListBuffer[0].Header.AceFlags = 0;
+ AceListBuffer[0].Mask = 0x33333333;
+ CopyGuid(&AceListBuffer[0].Guid, &DinoGuid);
+
+ if (!NT_SUCCESS(RtlAddAce(FredAcl, 1, 2, AceListBuffer, sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce Error\n");
+ }
+
+ RtlDumpAcl(FredAcl);
+ DbgPrint("\n");
+
+ //
+ // test add ace to add one to the end of an acl
+ //
+
+ AceListBuffer[0].Header.AceType = ACCESS_DENIED_ACE_TYPE;
+ AceListBuffer[0].Header.AceSize = sizeof(STANDARD_ACE);
+ AceListBuffer[0].Header.AceFlags = 0;
+ AceListBuffer[0].Mask = 0x55555555;
+ CopyGuid(&AceListBuffer[0].Guid, &FlintstoneGuid);
+
+ if (!NT_SUCCESS(RtlAddAce(FredAcl, 1, MAXULONG, AceListBuffer, sizeof(STANDARD_ACE)))) {
+ DbgPrint("RtlAddAce Error\n");
+ }
+
+ RtlDumpAcl(FredAcl);
+ DbgPrint("\n");
+
+ //
+ // Test get ace
+ //
+
+ {
+ PSTANDARD_ACE Ace;
+
+ if (!NT_SUCCESS(RtlGetAce(FredAcl, 2, (PVOID *)(&Ace)))) {
+ DbgPrint("RtlGetAce Error\n");
+ }
+
+ if ((Ace->Header.AceType != ACCESS_DENIED_ACE_TYPE) ||
+ (Ace->Mask != 0x33333333)) {
+ DbgPrint("Got bad ace from RtlGetAce\n");
+ }
+ }
+
+ //
+ // test delete ace middle ace
+ //
+
+ if (!NT_SUCCESS(RtlDeleteAce(FredAcl, 2))) {
+ DbgPrint("RtlDeleteAce Error\n");
+ }
+
+ RtlDumpAcl(FredAcl);
+ DbgPrint("\n");
+
+ //
+ // Test query information acl
+ //
+
+ if (!NT_SUCCESS(RtlQueryInformationAcl( FredAcl,
+ (PVOID)&AclRevisionInfo,
+ sizeof(ACL_REVISION_INFORMATION),
+ AclRevisionInformation))) {
+ DbgPrint("RtlQueryInformationAcl Error\n");
+ }
+ if (AclRevisionInfo.AclRevision != ACL_REVISION) {
+ DbgPrint("RtlAclRevision Error\n");
+ }
+
+ if (!NT_SUCCESS(RtlQueryInformationAcl( FredAcl,
+ (PVOID)&AclSizeInfo,
+ sizeof(ACL_SIZE_INFORMATION),
+ AclSizeInformation))) {
+ DbgPrint("RtlQueryInformationAcl Error\n");
+ }
+ if ((AclSizeInfo.AceCount != 4) ||
+ (AclSizeInfo.AclBytesInUse != (sizeof(ACL)+4*sizeof(STANDARD_ACE))) ||
+ (AclSizeInfo.AclBytesFree != 128 - AclSizeInfo.AclBytesInUse)) {
+ DbgPrint("RtlAclSize Error\n");
+ DbgPrint("AclSizeInfo.AceCount = %8lx\n", AclSizeInfo.AceCount);
+ DbgPrint("AclSizeInfo.AclBytesInUse = %8lx\n", AclSizeInfo.AclBytesInUse);
+ DbgPrint("AclSizeInfo.AclBytesFree = %8lx\n", AclSizeInfo.AclBytesFree);
+ DbgPrint("\n");
+ }
+
+ //
+ // Test make Mask from Acl
+ //
+
+ GuidMasks->PairCount = 11;
+ CopyGuid(&GuidMasks->MaskGuid[ 0].Guid, &FredGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 1].Guid, &WilmaGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 2].Guid, &PebbleGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 3].Guid, &DinoGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 4].Guid, &BarneyGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 5].Guid, &BettyGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 6].Guid, &BambamGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 7].Guid, &FlintstoneGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 8].Guid, &RubbleGuid);
+ CopyGuid(&GuidMasks->MaskGuid[ 9].Guid, &AdultGuid);
+ CopyGuid(&GuidMasks->MaskGuid[10].Guid, &ChildGuid);
+ if (!NT_SUCCESS(RtlMakeMaskFromAcl(FredAcl, GuidMasks))) {
+ DbgPrint("RtlMakeMaskFromAcl Error\n");
+ }
+ if ((GuidMasks->MaskGuid[ 0].Mask != 0x22222222) ||
+ (GuidMasks->MaskGuid[ 1].Mask != 0x44444444) ||
+ (GuidMasks->MaskGuid[ 2].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 3].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 4].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 5].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 6].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 7].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 8].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[ 9].Mask != 0x00000000) ||
+ (GuidMasks->MaskGuid[10].Mask != 0x00000000)) {
+ DbgPrint("Make Mask Error\n");
+ DbgPrint("Fred gets %8lx\n", GuidMasks->MaskGuid[ 0].Mask);
+ DbgPrint("Wilma gets %8lx\n", GuidMasks->MaskGuid[ 1].Mask);
+ DbgPrint("Pebble gets %8lx\n", GuidMasks->MaskGuid[ 2].Mask);
+ DbgPrint("Dino gets %8lx\n", GuidMasks->MaskGuid[ 3].Mask);
+ DbgPrint("Barney gets %8lx\n", GuidMasks->MaskGuid[ 4].Mask);
+ DbgPrint("Betty gets %8lx\n", GuidMasks->MaskGuid[ 5].Mask);
+ DbgPrint("Banbam gets %8lx\n", GuidMasks->MaskGuid[ 6].Mask);
+ DbgPrint("Flintstone gets %8lx\n", GuidMasks->MaskGuid[ 7].Mask);
+ DbgPrint("Rubble gets %8lx\n", GuidMasks->MaskGuid[ 8].Mask);
+ DbgPrint("Adult gets %8lx\n", GuidMasks->MaskGuid[ 9].Mask);
+ DbgPrint("Child gets %8lx\n", GuidMasks->MaskGuid[10].Mask);
+ }
+
+ //
+ // test make acl from mask
+ //
+
+ GuidMasks->PairCount = 2;
+ GuidMasks->MaskGuid[0].Mask = 0x55555555;
+ CopyGuid(&GuidMasks->MaskGuid[0].Guid, &BarneyGuid);
+ GuidMasks->MaskGuid[1].Mask = 0xaaaa5555;
+ CopyGuid(&GuidMasks->MaskGuid[1].Guid, &RubbleGuid);
+
+ //
+ // Initialize and dump a posix style acl
+ //
+
+ if (!NT_SUCCESS(RtlMakeAclFromMask(GuidMasks, AclPosixEnvironment, BarneyAcl, 128, 1))) {
+ DbgPrint("RtlMakeAclFromMask Error\n");
+ }
+
+ RtlDumpAcl(BarneyAcl);
+ DbgPrint("\n");
+
+ //
+ // Initialize and dump a OS/2 style acl
+ //
+
+ if (!NT_SUCCESS(RtlMakeAclFromMask(GuidMasks, AclOs2Environment, BettyAcl, 128, 1))) {
+ DbgPrint("RtlMakeAclFromMask Error\n");
+ }
+
+ RtlDumpAcl(BettyAcl);
+ DbgPrint("\n");
+
+ //
+ // We're done with the test
+ //
+
+ DbgPrint("End Acl Test\n");
+
+ return TRUE;
+}
+
+
diff --git a/private/ntos/rtl/tbitmap.c b/private/ntos/rtl/tbitmap.c
new file mode 100644
index 000000000..f24331a48
--- /dev/null
+++ b/private/ntos/rtl/tbitmap.c
@@ -0,0 +1,483 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tbitmap.c
+
+Abstract:
+
+ Test program for the Bitmap Procedures
+
+Author:
+
+ Gary Kimura [GaryKi] 30-Jan-1989
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+
+ULONG Buffer[512];
+RTL_BITMAP BitMapHeader;
+PRTL_BITMAP BitMap;
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ ULONG j;
+
+ DbgPrint("Start BitMapTest()\n");
+
+ //
+ // First create a new bitmap
+ //
+
+ BitMap = &BitMapHeader;
+ RtlInitializeBitMap( BitMap, Buffer, 2048*8 );
+
+ //
+ // >>>> Test setting bits
+ //
+
+ //
+ // Now clear all bits
+ //
+
+ RtlClearAllBits( BitMap );
+ if (RtlNumberOfClearBits( BitMap ) != 2048*8) { DbgPrint("Number of Clear bits error 1\n" ); }
+ if (RtlNumberOfSetBits( BitMap ) != 0) { DbgPrint("Number of Set bits error 1\n" ); }
+
+ //
+ // Now set some bit patterns, and test them
+ //
+
+ RtlSetBits( BitMap, 0, 1 );
+ RtlSetBits( BitMap, 63, 1 );
+ RtlSetBits( BitMap, 65, 30 );
+ RtlSetBits( BitMap, 127, 2 );
+ RtlSetBits( BitMap, 191, 34 );
+
+ if ((BitMap->Buffer[0] != 0x00000001) ||
+ (BitMap->Buffer[1] != 0x80000000) ||
+ (BitMap->Buffer[2] != 0x7ffffffe) ||
+ (BitMap->Buffer[3] != 0x80000000) ||
+ (BitMap->Buffer[4] != 0x00000001) ||
+ (BitMap->Buffer[5] != 0x80000000) ||
+ (BitMap->Buffer[6] != 0xffffffff) ||
+ (BitMap->Buffer[7] != 0x00000001)) {
+
+ DbgPrint("RtlSetBits Error\n");
+ return FALSE;
+ }
+
+ if (RtlNumberOfClearBits( BitMap ) != 2048*8 - 68) { DbgPrint("Number of Clear bits error 2\n" ); }
+ if (RtlNumberOfSetBits( BitMap ) != 68) { DbgPrint("Number of Set bits error 2\n" ); }
+
+ //
+ // Now test some RtlFindClearBitsAndSet
+ //
+
+ RtlSetAllBits( BitMap );
+
+ RtlClearBits( BitMap, 0 + 10*32, 1 );
+ RtlClearBits( BitMap, 5 + 11*32, 1 );
+ RtlClearBits( BitMap, 7 + 12*32, 1 );
+
+ RtlClearBits( BitMap, 0 + 13*32, 9 );
+ RtlClearBits( BitMap, 4 + 14*32, 9 );
+ RtlClearBits( BitMap, 7 + 15*32, 9 );
+
+ RtlClearBits( BitMap, 0 + 16*32, 10 );
+ RtlClearBits( BitMap, 4 + 17*32, 10 );
+ RtlClearBits( BitMap, 6 + 18*32, 10 );
+ RtlClearBits( BitMap, 7 + 19*32, 10 );
+
+ RtlClearBits( BitMap, 0 + 110*32, 14 );
+ RtlClearBits( BitMap, 1 + 111*32, 14 );
+ RtlClearBits( BitMap, 2 + 112*32, 14 );
+
+ RtlClearBits( BitMap, 0 + 113*32, 15 );
+ RtlClearBits( BitMap, 1 + 114*32, 15 );
+ RtlClearBits( BitMap, 2 + 115*32, 15 );
+
+ //DbgPrint("ClearBits = %08lx, ", RtlNumberOfClearBits( BitMap ));
+ //DbgPrint("SetBits = %08lx\n", RtlNumberOfSetBits( BitMap ));
+
+// {
+// ULONG i;
+// for (i = 0; i < 16; i++) {
+// DbgPrint("%2d: ", i);
+// DbgPrint("%08lx\n", BitMap->Buffer[i]);
+// }
+// }
+
+ if (RtlFindClearBitsAndSet(BitMap, 15, 0) != 0 + 113*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 113*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 15, 0) != 1 + 114*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 1 + 114*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 15, 0) != 2 + 115*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 2 + 115*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 14, 0) != 0 + 110*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 110*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 14, 0) != 1 + 111*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 1 + 111*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 14, 0) != 2 + 112*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 2 + 112*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 0 + 16*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 16*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 4 + 17*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 4 + 17*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 6 + 18*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 6 + 18*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 7 + 19*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 7 + 19*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 9, 0) != 0 + 13*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 13*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 9, 0) != 4 + 14*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 4 + 14*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 9, 0) != 7 + 15*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 7 + 15*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 1, 0) != 0 + 10*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 10*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 1, 0) != 5 + 11*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 5 + 11*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 1, 0) != 7 + 12*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 7 + 12*32\n");
+ return FALSE;
+ }
+
+ if (RtlNumberOfClearBits( BitMap ) != 0) { DbgPrint("Number of Clear bits error 3\n" ); }
+ if (RtlNumberOfSetBits( BitMap ) != 2048*8) { DbgPrint("Number of Set bits error 3\n" ); }
+
+ //
+ // Now test some RtlFindClearBitsAndSet
+ //
+
+ RtlSetAllBits( BitMap );
+
+ RtlClearBits( BitMap, 0 + 0*32, 1 );
+ RtlClearBits( BitMap, 5 + 1*32, 1 );
+ RtlClearBits( BitMap, 7 + 2*32, 1 );
+
+ RtlClearBits( BitMap, 0 + 3*32, 9 );
+ RtlClearBits( BitMap, 4 + 4*32, 9 );
+ RtlClearBits( BitMap, 7 + 5*32, 9 );
+
+ RtlClearBits( BitMap, 0 + 6*32, 10 );
+ RtlClearBits( BitMap, 4 + 7*32, 10 );
+ RtlClearBits( BitMap, 6 + 8*32, 10 );
+ RtlClearBits( BitMap, 7 + 9*32, 10 );
+
+ RtlClearBits( BitMap, 0 + 10*32, 14 );
+ RtlClearBits( BitMap, 1 + 11*32, 14 );
+ RtlClearBits( BitMap, 2 + 12*32, 14 );
+
+ RtlClearBits( BitMap, 0 + 13*32, 15 );
+ RtlClearBits( BitMap, 1 + 14*32, 15 );
+ RtlClearBits( BitMap, 2 + 15*32, 15 );
+
+ //DbgPrint("ClearBits = %08lx, ", RtlNumberOfClearBits( BitMap ));
+ //DbgPrint("SetBits = %08lx\n", RtlNumberOfSetBits( BitMap ));
+
+// {
+// ULONG i;
+// for (i = 0; i < 16; i++) {
+// DbgPrint("%2d: ", i);
+// DbgPrint("%08lx\n", BitMap->Buffer[i]);
+// }
+// }
+
+ if (RtlFindClearBitsAndSet(BitMap, 15, 0) != 0 + 13*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 13*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 15, 0) != 1 + 14*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 1 + 14*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 15, 0) != 2 + 15*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 2 + 15*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 14, 0) != 0 + 10*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 10*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 14, 0) != 1 + 11*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 1 + 11*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 14, 0) != 2 + 12*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 2 + 12*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 0 + 6*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 6*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 4 + 7*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 4 + 7*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 6 + 8*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 6 + 8*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 10, 0) != 7 + 9*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 7 + 9*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 9, 0) != 0 + 3*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 3*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 9, 0) != 4 + 4*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 4 + 4*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 9, 0) != 7 + 5*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 7 + 5*32\n");
+ return FALSE;
+ }
+
+ if (RtlFindClearBitsAndSet(BitMap, 1, 0) != 0 + 0*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 0 + 0*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 1, 0) != 5 + 1*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 5 + 1*32\n");
+ return FALSE;
+ }
+ if (RtlFindClearBitsAndSet(BitMap, 1, 0) != 7 + 2*32) {
+ DbgPrint("RtlFindClearBitsAndSet Error 7 + 2*32\n");
+ return FALSE;
+ }
+
+ if (RtlNumberOfClearBits( BitMap ) != 0) { DbgPrint("Number of Clear bits error 4\n" ); }
+ if (RtlNumberOfSetBits( BitMap ) != 2048*8) { DbgPrint("Number of Set bits error 4\n" ); }
+
+ //
+ // Test RtlAreBitsClear and AreBitsSet
+ //
+
+ DbgPrint("Start bit query tests\n");
+
+ RtlClearAllBits( BitMap );
+ if (!RtlAreBitsClear( BitMap, 0, 2048*8 )) { DbgPrint("RtlAreBitsClear Error 0\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 8, 8 )) { DbgPrint("AreBitsClear Error 1\n"); }
+ RtlClearBits( BitMap, 9, 6 );
+ if (RtlAreBitsClear( BitMap, 8, 8 )) { DbgPrint("AreBitsClear Error 2\n"); }
+ RtlClearBits( BitMap, 8, 1 );
+ if (RtlAreBitsClear( BitMap, 8, 8 )) { DbgPrint("AreBitsClear Error 3\n"); }
+ RtlClearBits( BitMap, 15, 1 );
+ if (!RtlAreBitsClear( BitMap, 8, 8 )) { DbgPrint("AreBitsClear Error 4\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 8, 7 )) { DbgPrint("AreBitsClear Error 5\n"); }
+ RtlClearBits( BitMap, 9, 5 );
+ if (RtlAreBitsClear( BitMap, 8, 7 )) { DbgPrint("AreBitsClear Error 6\n"); }
+ RtlClearBits( BitMap, 8, 1 );
+ if (RtlAreBitsClear( BitMap, 8, 7 )) { DbgPrint("AreBitsClear Error 7\n"); }
+ RtlClearBits( BitMap, 14, 1 );
+ if (!RtlAreBitsClear( BitMap, 8, 7 )) { DbgPrint("AreBitsClear Error 8\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 9, 7 )) { DbgPrint("AreBitsClear Error 9\n"); }
+ RtlClearBits( BitMap, 10, 5 );
+ if (RtlAreBitsClear( BitMap, 9, 7 )) { DbgPrint("AreBitsClear Error 10\n"); }
+ RtlClearBits( BitMap, 9, 1 );
+ if (RtlAreBitsClear( BitMap, 9, 7 )) { DbgPrint("AreBitsClear Error 11\n"); }
+ RtlClearBits( BitMap, 15, 1 );
+ if (!RtlAreBitsClear( BitMap, 9, 7 )) { DbgPrint("AreBitsClear Error 12\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 9, 5 )) { DbgPrint("AreBitsClear Error 13\n"); }
+ RtlClearBits( BitMap, 10, 3 );
+ if (RtlAreBitsClear( BitMap, 9, 5 )) { DbgPrint("AreBitsClear Error 14\n"); }
+ RtlClearBits( BitMap, 9, 1 );
+ if (RtlAreBitsClear( BitMap, 9, 5 )) { DbgPrint("AreBitsClear Error 15\n"); }
+ RtlClearBits( BitMap, 13, 1 );
+ if (!RtlAreBitsClear( BitMap, 9, 5 )) { DbgPrint("AreBitsClear Error 16\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 8, 24 )) { DbgPrint("AreBitsClear Error 17\n"); }
+ RtlClearBits( BitMap, 9, 22 );
+ if (RtlAreBitsClear( BitMap, 8, 24 )) { DbgPrint("AreBitsClear Error 18\n"); }
+ RtlClearBits( BitMap, 8, 1 );
+ if (RtlAreBitsClear( BitMap, 8, 24 )) { DbgPrint("AreBitsClear Error 19\n"); }
+ RtlClearBits( BitMap, 31, 1 );
+ if (!RtlAreBitsClear( BitMap, 8, 24 )) { DbgPrint("AreBitsClear Error 20\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 8, 23 )) { DbgPrint("AreBitsClear Error 21\n"); }
+ RtlClearBits( BitMap, 9, 21 );
+ if (RtlAreBitsClear( BitMap, 8, 23 )) { DbgPrint("AreBitsClear Error 22\n"); }
+ RtlClearBits( BitMap, 8, 1 );
+ if (RtlAreBitsClear( BitMap, 8, 23 )) { DbgPrint("AreBitsClear Error 23\n"); }
+ RtlClearBits( BitMap, 30, 1 );
+ if (!RtlAreBitsClear( BitMap, 8, 23 )) { DbgPrint("AreBitsClear Error 24\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 9, 23 )) { DbgPrint("AreBitsClear Error 25\n"); }
+ RtlClearBits( BitMap, 10, 21 );
+ if (RtlAreBitsClear( BitMap, 9, 23 )) { DbgPrint("AreBitsClear Error 26\n"); }
+ RtlClearBits( BitMap, 9, 1 );
+ if (RtlAreBitsClear( BitMap, 9, 23 )) { DbgPrint("AreBitsClear Error 27\n"); }
+ RtlClearBits( BitMap, 31, 1 );
+ if (!RtlAreBitsClear( BitMap, 9, 23 )) { DbgPrint("AreBitsClear Error 28\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 9, 21 )) { DbgPrint("AreBitsClear Error 29\n"); }
+ RtlClearBits( BitMap, 10, 19 );
+ if (RtlAreBitsClear( BitMap, 9, 21 )) { DbgPrint("AreBitsClear Error 30\n"); }
+ RtlClearBits( BitMap, 9, 1 );
+ if (RtlAreBitsClear( BitMap, 9, 21 )) { DbgPrint("AreBitsClear Error 31\n"); }
+ RtlClearBits( BitMap, 29, 1 );
+ if (!RtlAreBitsClear( BitMap, 9, 21 )) { DbgPrint("AreBitsClear Error 32\n"); }
+
+ RtlSetAllBits( BitMap );
+ if (RtlAreBitsClear( BitMap, 10, 1 )) { DbgPrint("AreBitsClear Error 33\n"); }
+ RtlClearBits( BitMap, 9, 1 );
+ if (RtlAreBitsClear( BitMap, 10, 1 )) { DbgPrint("AreBitsClear Error 34\n"); }
+ RtlClearBits( BitMap, 11, 1 );
+ if (RtlAreBitsClear( BitMap, 10, 1 )) { DbgPrint("AreBitsClear Error 35\n"); }
+ RtlClearBits( BitMap, 10, 1 );
+ if (!RtlAreBitsClear( BitMap, 10, 1 )) { DbgPrint("AreBitsClear Error 36\n"); }
+
+
+ RtlSetAllBits( BitMap );
+ if (!RtlAreBitsSet( BitMap, 0, 2048*8 )) { DbgPrint("RtlAreBitsSet Error 0\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 8, 8 )) { DbgPrint("AreBitsSet Error 1\n"); }
+ RtlSetBits( BitMap, 9, 6 );
+ if (RtlAreBitsSet( BitMap, 8, 8 )) { DbgPrint("AreBitsSet Error 2\n"); }
+ RtlSetBits( BitMap, 8, 1 );
+ if (RtlAreBitsSet( BitMap, 8, 8 )) { DbgPrint("AreBitsSet Error 3\n"); }
+ RtlSetBits( BitMap, 15, 1 );
+ if (!RtlAreBitsSet( BitMap, 8, 8 )) { DbgPrint("AreBitsSet Error 4\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 8, 7 )) { DbgPrint("AreBitsSet Error 5\n"); }
+ RtlSetBits( BitMap, 9, 5 );
+ if (RtlAreBitsSet( BitMap, 8, 7 )) { DbgPrint("AreBitsSet Error 6\n"); }
+ RtlSetBits( BitMap, 8, 1 );
+ if (RtlAreBitsSet( BitMap, 8, 7 )) { DbgPrint("AreBitsSet Error 7\n"); }
+ RtlSetBits( BitMap, 14, 1 );
+ if (!RtlAreBitsSet( BitMap, 8, 7 )) { DbgPrint("AreBitsSet Error 8\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 9, 7 )) { DbgPrint("AreBitsSet Error 9\n"); }
+ RtlSetBits( BitMap, 10, 5 );
+ if (RtlAreBitsSet( BitMap, 9, 7 )) { DbgPrint("AreBitsSet Error 10\n"); }
+ RtlSetBits( BitMap, 9, 1 );
+ if (RtlAreBitsSet( BitMap, 9, 7 )) { DbgPrint("AreBitsSet Error 11\n"); }
+ RtlSetBits( BitMap, 15, 1 );
+ if (!RtlAreBitsSet( BitMap, 9, 7 )) { DbgPrint("AreBitsSet Error 12\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 9, 5 )) { DbgPrint("AreBitsSet Error 13\n"); }
+ RtlSetBits( BitMap, 10, 3 );
+ if (RtlAreBitsSet( BitMap, 9, 5 )) { DbgPrint("AreBitsSet Error 14\n"); }
+ RtlSetBits( BitMap, 9, 1 );
+ if (RtlAreBitsSet( BitMap, 9, 5 )) { DbgPrint("AreBitsSet Error 15\n"); }
+ RtlSetBits( BitMap, 13, 1 );
+ if (!RtlAreBitsSet( BitMap, 9, 5 )) { DbgPrint("AreBitsSet Error 16\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 8, 24 )) { DbgPrint("AreBitsSet Error 17\n"); }
+ RtlSetBits( BitMap, 9, 22 );
+ if (RtlAreBitsSet( BitMap, 8, 24 )) { DbgPrint("AreBitsSet Error 18\n"); }
+ RtlSetBits( BitMap, 8, 1 );
+ if (RtlAreBitsSet( BitMap, 8, 24 )) { DbgPrint("AreBitsSet Error 19\n"); }
+ RtlSetBits( BitMap, 31, 1 );
+ if (!RtlAreBitsSet( BitMap, 8, 24 )) { DbgPrint("AreBitsSet Error 20\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 8, 23 )) { DbgPrint("AreBitsSet Error 21\n"); }
+ RtlSetBits( BitMap, 9, 21 );
+ if (RtlAreBitsSet( BitMap, 8, 23 )) { DbgPrint("AreBitsSet Error 22\n"); }
+ RtlSetBits( BitMap, 8, 1 );
+ if (RtlAreBitsSet( BitMap, 8, 23 )) { DbgPrint("AreBitsSet Error 23\n"); }
+ RtlSetBits( BitMap, 30, 1 );
+ if (!RtlAreBitsSet( BitMap, 8, 23 )) { DbgPrint("AreBitsSet Error 24\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 9, 23 )) { DbgPrint("AreBitsSet Error 25\n"); }
+ RtlSetBits( BitMap, 10, 21 );
+ if (RtlAreBitsSet( BitMap, 9, 23 )) { DbgPrint("AreBitsSet Error 26\n"); }
+ RtlSetBits( BitMap, 9, 1 );
+ if (RtlAreBitsSet( BitMap, 9, 23 )) { DbgPrint("AreBitsSet Error 27\n"); }
+ RtlSetBits( BitMap, 31, 1 );
+ if (!RtlAreBitsSet( BitMap, 9, 23 )) { DbgPrint("AreBitsSet Error 28\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 9, 21 )) { DbgPrint("AreBitsSet Error 29\n"); }
+ RtlSetBits( BitMap, 10, 19 );
+ if (RtlAreBitsSet( BitMap, 9, 21 )) { DbgPrint("AreBitsSet Error 30\n"); }
+ RtlSetBits( BitMap, 9, 1 );
+ if (RtlAreBitsSet( BitMap, 9, 21 )) { DbgPrint("AreBitsSet Error 31\n"); }
+ RtlSetBits( BitMap, 29, 1 );
+ if (!RtlAreBitsSet( BitMap, 9, 21 )) { DbgPrint("AreBitsSet Error 32\n"); }
+
+ RtlClearAllBits( BitMap );
+ if (RtlAreBitsSet( BitMap, 10, 1 )) { DbgPrint("AreBitsSet Error 33\n"); }
+ RtlSetBits( BitMap, 9, 1 );
+ if (RtlAreBitsSet( BitMap, 10, 1 )) { DbgPrint("AreBitsSet Error 34\n"); }
+ RtlSetBits( BitMap, 11, 1 );
+ if (RtlAreBitsSet( BitMap, 10, 1 )) { DbgPrint("AreBitsSet Error 35\n"); }
+ RtlSetBits( BitMap, 10, 1 );
+ if (!RtlAreBitsSet( BitMap, 10, 1 )) { DbgPrint("AreBitsSet Error 36\n"); }
+
+ DbgPrint("End BitMapTest()\n");
+
+ return TRUE;
+}
diff --git a/private/ntos/rtl/time.c b/private/ntos/rtl/time.c
new file mode 100644
index 000000000..1b73ded35
--- /dev/null
+++ b/private/ntos/rtl/time.c
@@ -0,0 +1,1332 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Time.c
+
+Abstract:
+
+ This module implements the absolute time conversion routines for NT.
+
+ Absolute LARGE_INTEGER in NT is represented by a 64-bit large integer accurate
+ to 100ns resolution. The smallest time resolution used by this package
+ is One millisecond. The basis for NT time is the start of 1601 which
+ was chosen because it is the start of a new quadricentury. Some facts
+ to note are:
+
+ o At 100ns resolution 32 bits is good for about 429 seconds (or 7 minutes)
+
+ o At 100ns resolution a large integer (i.e., 63 bits) is good for
+ about 29,247 years, or around 10,682,247 days.
+
+ o At 1 second resolution 31 bits is good for about 68 years
+
+ o At 1 second resolution 32 bits is good for about 136 years
+
+ o 100ns Time (ignoring time less than a millisecond) can be expressed
+ as two values, Days and Milliseconds. Where Days is the number of
+ whole days and Milliseconds is the number of milliseconds for the
+ partial day. Both of these values are ULONG.
+
+ Given these facts most of the conversions are done by first splitting
+ LARGE_INTEGER into Days and Milliseconds.
+
+Author:
+
+ Gary Kimura [GaryKi] 26-Aug-1989
+
+Environment:
+
+ Pure utility routine
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+
+#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
+#pragma alloc_text(PAGE, RtlCutoverTimeToSystemTime)
+#pragma alloc_text(PAGE, RtlTimeToElapsedTimeFields)
+#endif
+
+
+//
+// The following two tables map a day offset within a year to the month
+// containing the day. Both tables are zero based. For example, day
+// offset of 0 to 30 map to 0 (which is Jan).
+//
+
+UCHAR LeapYearDayToMonth[366] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // January
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // February
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // March
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // April
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // May
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // June
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // July
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // August
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // September
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // October
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // November
+ 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11}; // December
+
+UCHAR NormalYearDayToMonth[365] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // January
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // February
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // March
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // April
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // May
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // June
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // July
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // August
+ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // September
+ 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // October
+ 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, // November
+ 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11}; // December
+
+//
+// The following two tables map a month index to the number of days preceding
+// the month in the year. Both tables are zero based. For example, 1 (Feb)
+// has 31 days preceding it. To help calculate the maximum number of days
+// in a month each table has 13 entries, so the number of days in a month
+// of index i is the table entry of i+1 minus the table entry of i.
+//
+
+CSHORT LeapYearDaysPrecedingMonth[13] = {
+ 0, // January
+ 31, // February
+ 31+29, // March
+ 31+29+31, // April
+ 31+29+31+30, // May
+ 31+29+31+30+31, // June
+ 31+29+31+30+31+30, // July
+ 31+29+31+30+31+30+31, // August
+ 31+29+31+30+31+30+31+31, // September
+ 31+29+31+30+31+30+31+31+30, // October
+ 31+29+31+30+31+30+31+31+30+31, // November
+ 31+29+31+30+31+30+31+31+30+31+30, // December
+ 31+29+31+30+31+30+31+31+30+31+30+31};
+
+CSHORT NormalYearDaysPrecedingMonth[13] = {
+ 0, // January
+ 31, // February
+ 31+28, // March
+ 31+28+31, // April
+ 31+28+31+30, // May
+ 31+28+31+30+31, // June
+ 31+28+31+30+31+30, // July
+ 31+28+31+30+31+30+31, // August
+ 31+28+31+30+31+30+31+31, // September
+ 31+28+31+30+31+30+31+31+30, // October
+ 31+28+31+30+31+30+31+31+30+31, // November
+ 31+28+31+30+31+30+31+31+30+31+30, // December
+ 31+28+31+30+31+30+31+31+30+31+30+31};
+
+
+//
+// The following definitions and declarations are some important constants
+// used in the time conversion routines
+//
+
+//
+// This is the week day that January 1st, 1601 fell on (a Monday)
+//
+
+#define WEEKDAY_OF_1601 1
+
+//
+// These are known constants used to convert 1970 and 1980 times to 1601
+// times. They are the number of seconds from the 1601 base to the start
+// of 1970 and the start of 1980. The number of seconds from 1601 to
+// 1970 is 369 years worth, or (369 * 365) + 89 leap days = 134774 days, or
+// 134774 * 864000 seconds, which is equal to the large integer defined
+// below. The number of seconds from 1601 to 1980 is 379 years worth, or etc.
+//
+
+LARGE_INTEGER SecondsToStartOf1970 = {0xb6109100, 0x00000002};
+
+LARGE_INTEGER SecondsToStartOf1980 = {0xc8df3700, 0x00000002};
+
+//
+// These are the magic numbers needed to do our extended division. The
+// only numbers we ever need to divide by are
+//
+// 10,000 = convert 100ns tics to millisecond tics
+//
+// 10,000,000 = convert 100ns tics to one second tics
+//
+// 86,400,000 = convert Millisecond tics to one day tics
+//
+
+LARGE_INTEGER Magic10000 = {0xe219652c, 0xd1b71758};
+#define SHIFT10000 13
+
+LARGE_INTEGER Magic10000000 = {0xe57a42bd, 0xd6bf94d5};
+#define SHIFT10000000 23
+
+LARGE_INTEGER Magic86400000 = {0xfa67b90e, 0xc6d750eb};
+#define SHIFT86400000 26
+
+//
+// To make the code more readable we'll also define some macros to
+// do the actual division for use
+//
+
+#define Convert100nsToMilliseconds(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000, SHIFT10000 ) \
+ )
+
+#define ConvertMillisecondsTo100ns(MILLISECONDS) ( \
+ RtlExtendedIntegerMultiply( (MILLISECONDS), 10000 ) \
+ )
+
+#define Convert100nsToSeconds(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic10000000, SHIFT10000000 ) \
+ )
+
+#define ConvertSecondsTo100ns(SECONDS) ( \
+ RtlExtendedIntegerMultiply( (SECONDS), 10000000 ) \
+ )
+
+#define ConvertMillisecondsToDays(LARGE_INTEGER) ( \
+ RtlExtendedMagicDivide( (LARGE_INTEGER), Magic86400000, SHIFT86400000 ) \
+ )
+
+#define ConvertDaysToMilliseconds(DAYS) ( \
+ Int32x32To64( (DAYS), 86400000 ) \
+ )
+
+
+//
+// Local support routine
+//
+
+ULONG
+ElapsedDaysToYears (
+ IN ULONG ElapsedDays
+ )
+
+/*++
+
+Routine Description:
+
+ This routine computes the number of total years contained in the indicated
+ number of elapsed days. The computation is to first compute the number of
+ 400 years and subtract that it, then do the 100 years and subtract that out,
+ then do the number of 4 years and subtract that out. Then what we have left
+ is the number of days with in a normalized 4 year block. Normalized being that
+ the first three years are not leap years.
+
+Arguments:
+
+ ElapsedDays - Supplies the number of days to use
+
+Return Value:
+
+ ULONG - Returns the number of whole years contained within the input number
+ of days.
+
+--*/
+
+{
+ ULONG NumberOf400s;
+ ULONG NumberOf100s;
+ ULONG NumberOf4s;
+ ULONG Years;
+
+ //
+ // A 400 year time block is 365*400 + 400/4 - 400/100 + 400/400 = 146097 days
+ // long. So we simply compute the number of whole 400 year block and the
+ // the number days contained in those whole blocks, and subtract if from the
+ // elapsed day total
+ //
+
+ NumberOf400s = ElapsedDays / 146097;
+ ElapsedDays -= NumberOf400s * 146097;
+
+ //
+ // A 100 year time block is 365*100 + 100/4 - 100/100 = 36524 days long.
+ // The computation for the number of 100 year blocks is biased by 3/4 days per
+ // 100 years to account for the extra leap day thrown in on the last year
+ // of each 400 year block.
+ //
+
+ NumberOf100s = (ElapsedDays * 100 + 75) / 3652425;
+ ElapsedDays -= NumberOf100s * 36524;
+
+ //
+ // A 4 year time block is 365*4 + 4/4 = 1461 days long.
+ //
+
+ NumberOf4s = ElapsedDays / 1461;
+ ElapsedDays -= NumberOf4s * 1461;
+
+ //
+ // Now the number of whole years is the number of 400 year blocks times 400,
+ // 100 year blocks time 100, 4 year blocks times 4, and the number of elapsed
+ // whole years, taking into account the 3/4 day per year needed to handle the
+ // leap year.
+ //
+
+ Years = (NumberOf400s * 400) +
+ (NumberOf100s * 100) +
+ (NumberOf4s * 4) +
+ (ElapsedDays * 100 + 75) / 36525;
+
+ return Years;
+}
+
+
+//
+// ULONG
+// NumberOfLeapYears (
+// IN ULONG ElapsedYears
+// );
+//
+// The number of leap years is simply the number of years divided by 4
+// minus years divided by 100 plus years divided by 400. This says
+// that every four years is a leap year except centuries, and the
+// exception to the exception is the quadricenturies
+//
+
+#define NumberOfLeapYears(YEARS) ( \
+ ((YEARS) / 4) - ((YEARS) / 100) + ((YEARS) / 400) \
+ )
+
+//
+// ULONG
+// ElapsedYearsToDays (
+// IN ULONG ElapsedYears
+// );
+//
+// The number of days contained in elapsed years is simply the number
+// of years times 365 (because every year has at least 365 days) plus
+// the number of leap years there are (i.e., the number of 366 days years)
+//
+
+#define ElapsedYearsToDays(YEARS) ( \
+ ((YEARS) * 365) + NumberOfLeapYears(YEARS) \
+ )
+
+//
+// BOOLEAN
+// IsLeapYear (
+// IN ULONG ElapsedYears
+// );
+//
+// If it is an even 400 or a non century leapyear then the
+// answer is true otherwise it's false
+//
+
+#define IsLeapYear(YEARS) ( \
+ (((YEARS) % 400 == 0) || \
+ ((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
+ TRUE \
+ : \
+ FALSE \
+ )
+
+//
+// ULONG
+// MaxDaysInMonth (
+// IN ULONG Year,
+// IN ULONG Month
+// );
+//
+// The maximum number of days in a month depend on the year and month.
+// It is the difference between the days to the month and the days
+// to the following month
+//
+
+#define MaxDaysInMonth(YEAR,MONTH) ( \
+ IsLeapYear(YEAR) ? \
+ LeapYearDaysPrecedingMonth[(MONTH) + 1] - \
+ LeapYearDaysPrecedingMonth[(MONTH)] \
+ : \
+ NormalYearDaysPrecedingMonth[(MONTH) + 1] - \
+ NormalYearDaysPrecedingMonth[(MONTH)] \
+ )
+
+
+
+//
+// Internal Support routine
+//
+
+static
+VOID
+TimeToDaysAndFraction (
+ IN PLARGE_INTEGER Time,
+ OUT PULONG ElapsedDays,
+ OUT PULONG Milliseconds
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit time value to the number
+ of total elapsed days and the number of milliseconds in the
+ partial day.
+
+Arguments:
+
+ Time - Supplies the input time to convert from
+
+ ElapsedDays - Receives the number of elapsed days
+
+ Milliseconds - Receives the number of milliseconds in the partial day
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER TotalMilliseconds;
+ LARGE_INTEGER Temp;
+
+ //
+ // Convert the input time to total milliseconds
+ //
+
+ TotalMilliseconds = Convert100nsToMilliseconds( *(PLARGE_INTEGER)Time );
+
+ //
+ // Convert milliseconds to total days
+ //
+
+ Temp = ConvertMillisecondsToDays( TotalMilliseconds );
+
+ //
+ // Set the elapsed days from temp, we've divided it enough so that
+ // the high part must be zero.
+ //
+
+ *ElapsedDays = Temp.LowPart;
+
+ //
+ // Calculate the exact number of milliseconds in the elapsed days
+ // and subtract that from the total milliseconds to figure out
+ // the number of milliseconds left in the partial day
+ //
+
+ Temp.QuadPart = ConvertDaysToMilliseconds( *ElapsedDays );
+
+ Temp.QuadPart = TotalMilliseconds.QuadPart - Temp.QuadPart;
+
+ //
+ // Set the fraction part from temp, the total number of milliseconds in
+ // a day guarantees that the high part must be zero.
+ //
+
+ *Milliseconds = Temp.LowPart;
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+
+//
+// Internal Support routine
+//
+
+//static
+VOID
+DaysAndFractionToTime (
+ IN ULONG ElapsedDays,
+ IN ULONG Milliseconds,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input elapsed day count and partial time
+ in milliseconds to a 64-bit time value.
+
+Arguments:
+
+ ElapsedDays - Supplies the number of elapsed days
+
+ Milliseconds - Supplies the number of milliseconds in the partial day
+
+ Time - Receives the output time to value
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER Temp;
+ LARGE_INTEGER Temp2;
+
+ //
+ // Calculate the exact number of milliseconds in the elapsed days.
+ //
+
+ Temp.QuadPart = ConvertDaysToMilliseconds( ElapsedDays );
+
+ //
+ // Convert milliseconds to a large integer
+ //
+
+ Temp2.LowPart = Milliseconds;
+ Temp2.HighPart = 0;
+
+ //
+ // add milliseconds to the whole day milliseconds
+ //
+
+ Temp.QuadPart = Temp.QuadPart + Temp2.QuadPart;
+
+ //
+ // Finally convert the milliseconds to 100ns resolution
+ //
+
+ *(PLARGE_INTEGER)Time = ConvertMillisecondsTo100ns( Temp );
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+
+VOID
+RtlTimeToTimeFields (
+ IN PLARGE_INTEGER Time,
+ OUT PTIME_FIELDS TimeFields
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit LARGE_INTEGER variable to its corresponding
+ time field record. It will tell the caller the year, month, day, hour,
+ minute, second, millisecond, and weekday corresponding to the input time
+ variable.
+
+Arguments:
+
+ Time - Supplies the time value to interpret
+
+ TimeFields - Receives a value corresponding to Time
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG Years;
+ ULONG Month;
+ ULONG Days;
+
+ ULONG Hours;
+ ULONG Minutes;
+ ULONG Seconds;
+ ULONG Milliseconds;
+
+ //
+ // First divide the input time 64 bit time variable into
+ // the number of whole days and part days (in milliseconds)
+ //
+
+ TimeToDaysAndFraction( Time, &Days, &Milliseconds );
+
+ //
+ // Compute which weekday it is and save it away now in the output
+ // variable. We add the weekday of the base day to bias our computation
+ // which means that if one day has elapsed then we the weekday we want
+ // is the Jan 2nd, 1601.
+ //
+
+ TimeFields->Weekday = (CSHORT)((Days + WEEKDAY_OF_1601) % 7);
+
+ //
+ // Calculate the number of whole years contained in the elapsed days
+ // For example if Days = 500 then Years = 1
+ //
+
+ Years = ElapsedDaysToYears( Days );
+
+ //
+ // And subtract the number of whole years from our elapsed days
+ // For example if Days = 500, Years = 1, and the new days is equal
+ // to 500 - 365 (normal year).
+ //
+
+ Days = Days - ElapsedYearsToDays( Years );
+
+ //
+ // Now test whether the year we are working on (i.e., The year
+ // after the total number of elapsed years) is a leap year
+ // or not.
+ //
+
+ if (IsLeapYear( Years + 1 )) {
+
+ //
+ // The current year is a leap year, so figure out what month
+ // it is, and then subtract the number of days preceding the
+ // month from the days to figure out what day of the month it is
+ //
+
+ Month = LeapYearDayToMonth[Days];
+ Days = Days - LeapYearDaysPrecedingMonth[Month];
+
+ } else {
+
+ //
+ // The current year is a normal year, so figure out the month
+ // and days as described above for the leap year case
+ //
+
+ Month = NormalYearDayToMonth[Days];
+ Days = Days - NormalYearDaysPrecedingMonth[Month];
+
+ }
+
+ //
+ // Now we need to compute the elapsed hour, minute, second, milliseconds
+ // from the millisecond variable. This variable currently contains
+ // the number of milliseconds in our input time variable that did not
+ // fit into a whole day. To compute the hour, minute, second part
+ // we will actually do the arithmetic backwards computing milliseconds
+ // seconds, minutes, and then hours. We start by computing the
+ // number of whole seconds left in the day, and then computing
+ // the millisecond remainder.
+ //
+
+ Seconds = Milliseconds / 1000;
+ Milliseconds = Milliseconds % 1000;
+
+ //
+ // Now we compute the number of whole minutes left in the day
+ // and the number of remainder seconds
+ //
+
+ Minutes = Seconds / 60;
+ Seconds = Seconds % 60;
+
+ //
+ // Now compute the number of whole hours left in the day
+ // and the number of remainder minutes
+ //
+
+ Hours = Minutes / 60;
+ Minutes = Minutes % 60;
+
+ //
+ // As our final step we put everything into the time fields
+ // output variable
+ //
+
+ TimeFields->Year = (CSHORT)(Years + 1601);
+ TimeFields->Month = (CSHORT)(Month + 1);
+ TimeFields->Day = (CSHORT)(Days + 1);
+ TimeFields->Hour = (CSHORT)Hours;
+ TimeFields->Minute = (CSHORT)Minutes;
+ TimeFields->Second = (CSHORT)Seconds;
+ TimeFields->Milliseconds = (CSHORT)Milliseconds;
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+BOOLEAN
+RtlCutoverTimeToSystemTime(
+ PTIME_FIELDS CutoverTime,
+ PLARGE_INTEGER SystemTime,
+ PLARGE_INTEGER CurrentSystemTime,
+ BOOLEAN ThisYear
+ )
+{
+ TIME_FIELDS CurrentTimeFields;
+
+ //
+ // Get the current system time
+ //
+
+ RtlTimeToTimeFields(CurrentSystemTime,&CurrentTimeFields);
+
+ //
+ // check for absolute time field. If the year is specified,
+ // the the time is an abosulte time
+ //
+
+ if ( CutoverTime->Year ) {
+
+ //
+ // Convert this to a time value and make sure it
+ // is greater than the current system time
+ //
+
+ if ( !RtlTimeFieldsToTime(CutoverTime,SystemTime) ) {
+ return FALSE;
+ }
+
+ if (SystemTime->QuadPart < CurrentSystemTime->QuadPart) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+ else {
+
+ TIME_FIELDS WorkingTimeField;
+ TIME_FIELDS ScratchTimeField;
+ LARGE_INTEGER ScratchTime;
+ CSHORT BestWeekdayDate;
+ CSHORT WorkingWeekdayNumber;
+ CSHORT TargetWeekdayNumber;
+ CSHORT TargetYear;
+ CSHORT TargetMonth;
+ CSHORT TargetWeekday; // range [0..6] == [Sunday..Saturday]
+ BOOLEAN MonthMatches;
+ //
+ // The time is an day in the month style time
+ //
+ // the convention is the Day is 1-5 specifying 1st, 2nd... Last
+ // day within the month. The day is WeekDay.
+ //
+
+ //
+ // Compute the target month and year
+ //
+
+ TargetWeekdayNumber = CutoverTime->Day;
+ if ( TargetWeekdayNumber > 5 || TargetWeekdayNumber == 0 ) {
+ return FALSE;
+ }
+ TargetWeekday = CutoverTime->Weekday;
+ TargetMonth = CutoverTime->Month;
+ MonthMatches = FALSE;
+ if ( !ThisYear ) {
+ if ( TargetMonth < CurrentTimeFields.Month ) {
+ TargetYear = CurrentTimeFields.Year + 1;
+ }
+ else if ( TargetMonth > CurrentTimeFields.Month ) {
+ TargetYear = CurrentTimeFields.Year;
+ }
+ else {
+ TargetYear = CurrentTimeFields.Year;
+ MonthMatches = TRUE;
+ }
+ }
+ else {
+ TargetYear = CurrentTimeFields.Year;
+ }
+try_next_year:
+ BestWeekdayDate = 0;
+
+ WorkingTimeField.Year = TargetYear;
+ WorkingTimeField.Month = TargetMonth;
+ WorkingTimeField.Day = 1;
+ WorkingTimeField.Hour = CutoverTime->Hour;
+ WorkingTimeField.Minute = CutoverTime->Minute;
+ WorkingTimeField.Second = CutoverTime->Second;
+ WorkingTimeField.Milliseconds = CutoverTime->Milliseconds;
+ WorkingTimeField.Weekday = 0;
+
+ //
+ // Convert to time and then back to time fields so we can determine
+ // the weekday of day 1 on the month
+ //
+
+ if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
+ return FALSE;
+ }
+ RtlTimeToTimeFields(&ScratchTime,&ScratchTimeField);
+
+ //
+ // Compute bias to target weekday
+ //
+ if ( ScratchTimeField.Weekday > TargetWeekday ) {
+ WorkingTimeField.Day += (7-(ScratchTimeField.Weekday - TargetWeekday));
+ }
+ else if ( ScratchTimeField.Weekday < TargetWeekday ) {
+ WorkingTimeField.Day += (TargetWeekday - ScratchTimeField.Weekday);
+ }
+
+ //
+ // We are now at the first weekday that matches our target weekday
+ //
+
+ BestWeekdayDate = WorkingTimeField.Day;
+ WorkingWeekdayNumber = 1;
+
+ //
+ // Keep going one week at a time until we either pass the
+ // target weekday, or we match exactly
+ //
+
+ while ( WorkingWeekdayNumber < TargetWeekdayNumber ) {
+ WorkingTimeField.Day += 7;
+ if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
+ break;
+ }
+ RtlTimeToTimeFields(&ScratchTime,&ScratchTimeField);
+ WorkingWeekdayNumber++;
+ BestWeekdayDate = ScratchTimeField.Day;
+ }
+ WorkingTimeField.Day = BestWeekdayDate;
+
+ //
+ // If the months match, and the date is less than the current
+ // date, then be have to go to next year.
+ //
+
+ if ( !RtlTimeFieldsToTime(&WorkingTimeField,&ScratchTime) ) {
+ return FALSE;
+ }
+ if ( MonthMatches ) {
+ if ( WorkingTimeField.Day < CurrentTimeFields.Day ) {
+ MonthMatches = FALSE;
+ TargetYear++;
+ goto try_next_year;
+ }
+ if ( WorkingTimeField.Day == CurrentTimeFields.Day ) {
+
+ if (ScratchTime.QuadPart < CurrentSystemTime->QuadPart) {
+ MonthMatches = FALSE;
+ TargetYear++;
+ goto try_next_year;
+ }
+ }
+ }
+ *SystemTime = ScratchTime;
+
+ return TRUE;
+ }
+}
+
+
+BOOLEAN
+RtlTimeFieldsToTime (
+ IN PTIME_FIELDS TimeFields,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input Time Field variable to a 64-bit NT time
+ value. It ignores the WeekDay of the time field.
+
+Arguments:
+
+ TimeFields - Supplies the time field record to use
+
+ Time - Receives the NT Time corresponding to TimeFields
+
+Return Value:
+
+ BOOLEAN - TRUE if the Time Fields is well formed and within the
+ range of time expressible by LARGE_INTEGER and FALSE otherwise.
+
+--*/
+
+{
+ ULONG Year;
+ ULONG Month;
+ ULONG Day;
+ ULONG Hour;
+ ULONG Minute;
+ ULONG Second;
+ ULONG Milliseconds;
+
+ ULONG ElapsedDays;
+ ULONG ElapsedMilliseconds;
+
+ //
+ // Load the time field elements into local variables. This should
+ // ensure that the compiler will only load the input elements
+ // once, even if there are alias problems. It will also make
+ // everything (except the year) zero based. We cannot zero base the
+ // year because then we can't recognize cases where we're given a year
+ // before 1601.
+ //
+
+ Year = TimeFields->Year;
+ Month = TimeFields->Month - 1;
+ Day = TimeFields->Day - 1;
+ Hour = TimeFields->Hour;
+ Minute = TimeFields->Minute;
+ Second = TimeFields->Second;
+ Milliseconds = TimeFields->Milliseconds;
+
+ //
+ // Check that the time field input variable contains
+ // proper values.
+ //
+
+ if ((TimeFields->Month < 1) ||
+ (TimeFields->Day < 1) ||
+ (Year < 1601) ||
+ (Month > 11) ||
+ ((CSHORT)Day >= MaxDaysInMonth(Year, Month)) ||
+ (Hour > 23) ||
+ (Minute > 59) ||
+ (Second > 59) ||
+ (Milliseconds > 999)) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Compute the total number of elapsed days represented by the
+ // input time field variable
+ //
+
+ ElapsedDays = ElapsedYearsToDays( Year - 1601 );
+
+ if (IsLeapYear( Year - 1600 )) {
+
+ ElapsedDays += LeapYearDaysPrecedingMonth[ Month ];
+
+ } else {
+
+ ElapsedDays += NormalYearDaysPrecedingMonth[ Month ];
+
+ }
+
+ ElapsedDays += Day;
+
+ //
+ // Now compute the total number of milliseconds in the fractional
+ // part of the day
+ //
+
+ ElapsedMilliseconds = (((Hour*60) + Minute)*60 + Second)*1000 + Milliseconds;
+
+ //
+ // Given the elapsed days and milliseconds we can now build
+ // the output time variable
+ //
+
+ DaysAndFractionToTime( ElapsedDays, ElapsedMilliseconds, Time );
+
+ //
+ // And return to our caller
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlTimeToElapsedTimeFields (
+ IN PLARGE_INTEGER Time,
+ OUT PTIME_FIELDS TimeFields
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit LARGE_INTEGER variable to its corresponding
+ time field record. The input time is the elapsed time (difference
+ between to times). It will tell the caller the number of days, hour,
+ minute, second, and milliseconds that the elapsed time represents.
+
+Arguments:
+
+ Time - Supplies the time value to interpret
+
+ TimeFields - Receives a value corresponding to Time
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ ULONG Days;
+ ULONG Hours;
+ ULONG Minutes;
+ ULONG Seconds;
+ ULONG Milliseconds;
+
+ //
+ // First divide the input time 64 bit time variable into
+ // the number of whole days and part days (in milliseconds)
+ //
+
+ TimeToDaysAndFraction( Time, &Days, &Milliseconds );
+
+ //
+ // Now we need to compute the elapsed hour, minute, second, milliseconds
+ // from the millisecond variable. This variable currently contains
+ // the number of milliseconds in our input time variable that did not
+ // fit into a whole day. To compute the hour, minute, second part
+ // we will actually do the arithmetic backwards computing milliseconds
+ // seconds, minutes, and then hours. We start by computing the
+ // number of whole seconds left in the day, and then computing
+ // the millisecond remainder.
+ //
+
+ Seconds = Milliseconds / 1000;
+ Milliseconds = Milliseconds % 1000;
+
+ //
+ // Now we compute the number of whole minutes left in the day
+ // and the number of remainder seconds
+ //
+
+ Minutes = Seconds / 60;
+ Seconds = Seconds % 60;
+
+ //
+ // Now compute the number of whole hours left in the day
+ // and the number of remainder minutes
+ //
+
+ Hours = Minutes / 60;
+ Minutes = Minutes % 60;
+
+ //
+ // As our final step we put everything into the time fields
+ // output variable
+ //
+
+ TimeFields->Year = 0;
+ TimeFields->Month = 0;
+ TimeFields->Day = (CSHORT)Days;
+ TimeFields->Hour = (CSHORT)Hours;
+ TimeFields->Minute = (CSHORT)Minutes;
+ TimeFields->Second = (CSHORT)Seconds;
+ TimeFields->Milliseconds = (CSHORT)Milliseconds;
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+
+BOOLEAN
+RtlTimeToSecondsSince1980 (
+ IN PLARGE_INTEGER Time,
+ OUT PULONG ElapsedSeconds
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit NT Time variable to the
+ number of seconds since the start of 1980. The NT time must be
+ within the range 1980 to around 2115.
+
+Arguments:
+
+ Time - Supplies the Time to convert from
+
+ ElapsedSeconds - Receives the number of seconds since the start of 1980
+ denoted by Time
+
+Return Value:
+
+ BOOLEAN - TRUE if the input Time is within a range expressible by
+ ElapsedSeconds and FALSE otherwise
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // First convert time to seconds since 1601
+ //
+
+ Seconds = Convert100nsToSeconds( *(PLARGE_INTEGER)Time );
+
+ //
+ // Then subtract the number of seconds from 1601 to 1980.
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart - SecondsToStartOf1980.QuadPart;
+
+ //
+ // If the results is negative then the date was before 1980 or if
+ // the results is greater than a ulong then its too far in the
+ // future so we return FALSE
+ //
+
+ if (Seconds.HighPart != 0) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Otherwise we have the answer
+ //
+
+ *ElapsedSeconds = Seconds.LowPart;
+
+ //
+ // And return to our caller
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlSecondsSince1980ToTime (
+ IN ULONG ElapsedSeconds,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the seconds since the start of 1980 to an
+ NT Time value.
+
+Arguments:
+
+ ElapsedSeconds - Supplies the number of seconds from the start of 1980
+ to convert from
+
+ Time - Receives the converted Time value
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // Move elapsed seconds to a large integer
+ //
+
+ Seconds.LowPart = ElapsedSeconds;
+ Seconds.HighPart = 0;
+
+ //
+ // convert number of seconds from 1980 to number of seconds from 1601
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart + SecondsToStartOf1980.QuadPart;
+
+ //
+ // Convert seconds to 100ns resolution
+ //
+
+ *(PLARGE_INTEGER)Time = ConvertSecondsTo100ns( Seconds );
+
+ //
+ // and return to our caller
+ //
+
+ return;
+}
+
+
+BOOLEAN
+RtlTimeToSecondsSince1970 (
+ IN PLARGE_INTEGER Time,
+ OUT PULONG ElapsedSeconds
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an input 64-bit NT Time variable to the
+ number of seconds since the start of 1970. The NT time must be
+ within the range 1970 to around 2105.
+
+Arguments:
+
+ Time - Supplies the Time to convert from
+
+ ElapsedSeconds - Receives the number of seconds since the start of 1970
+ denoted by Time
+
+Return Value:
+
+ BOOLEAN - TRUE if the input time is within the range expressible by
+ ElapsedSeconds and FALSE otherwise
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // First convert time to seconds since 1601
+ //
+
+ Seconds = Convert100nsToSeconds( *(PLARGE_INTEGER)Time );
+
+ //
+ // Then subtract the number of seconds from 1601 to 1970.
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart - SecondsToStartOf1970.QuadPart;
+
+ //
+ // If the results is negative then the date was before 1970 or if
+ // the results is greater than a ulong then its too far in the
+ // future so we return FALSE
+ //
+
+ if (Seconds.HighPart != 0) {
+
+ return FALSE;
+
+ }
+
+ //
+ // Otherwise we have the answer
+ //
+
+ *ElapsedSeconds = Seconds.LowPart;
+
+ //
+ // And return to our caller
+ //
+
+ return TRUE;
+}
+
+
+VOID
+RtlSecondsSince1970ToTime (
+ IN ULONG ElapsedSeconds,
+ OUT PLARGE_INTEGER Time
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the seconds since the start of 1970 to an
+ NT Time value
+
+Arguments:
+
+ ElapsedSeconds - Supplies the number of seconds from the start of 1970
+ to convert from
+
+ Time - Receives the converted Time value
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ LARGE_INTEGER Seconds;
+
+ //
+ // Move elapsed seconds to a large integer
+ //
+
+ Seconds.LowPart = ElapsedSeconds;
+ Seconds.HighPart = 0;
+
+ //
+ // Convert number of seconds from 1970 to number of seconds from 1601
+ //
+
+ Seconds.QuadPart = Seconds.QuadPart + SecondsToStartOf1970.QuadPart;
+
+ //
+ // Convert seconds to 100ns resolution
+ //
+
+ *(PLARGE_INTEGER)Time = ConvertSecondsTo100ns( Seconds );
+
+ //
+ // return to our caller
+ //
+
+ return;
+}
+
+NTSTATUS
+RtlSystemTimeToLocalTime (
+ IN PLARGE_INTEGER SystemTime,
+ OUT PLARGE_INTEGER LocalTime
+ )
+{
+ NTSTATUS Status;
+ SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
+
+ Status = ZwQuerySystemInformation(
+ SystemTimeOfDayInformation,
+ &TimeOfDay,
+ sizeof(TimeOfDay),
+ NULL
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // LocalTime = SystemTime - TimeZoneBias
+ //
+
+ LocalTime->QuadPart = SystemTime->QuadPart - TimeOfDay.TimeZoneBias.QuadPart;
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RtlLocalTimeToSystemTime (
+ IN PLARGE_INTEGER LocalTime,
+ OUT PLARGE_INTEGER SystemTime
+ )
+{
+
+ NTSTATUS Status;
+ SYSTEM_TIMEOFDAY_INFORMATION TimeOfDay;
+
+ Status = ZwQuerySystemInformation(
+ SystemTimeOfDayInformation,
+ &TimeOfDay,
+ sizeof(TimeOfDay),
+ NULL
+ );
+ if ( !NT_SUCCESS(Status) ) {
+ return Status;
+ }
+
+ //
+ // SystemTime = LocalTime + TimeZoneBias
+ //
+
+ SystemTime->QuadPart = LocalTime->QuadPart + TimeOfDay.TimeZoneBias.QuadPart;
+
+ return STATUS_SUCCESS;
+}
diff --git a/private/ntos/rtl/tnlsxlat.c b/private/ntos/rtl/tnlsxlat.c
new file mode 100644
index 000000000..045557538
--- /dev/null
+++ b/private/ntos/rtl/tnlsxlat.c
@@ -0,0 +1,101 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tnlsxlat.c
+
+Abstract:
+
+ Test program for the Nlsxlat Procedures
+
+Author:
+
+ Ian James [IanJa] 03-Feb-1994
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+
+#define NELEM(p) (sizeof(p) / sizeof(*(p)))
+
+char OEMBuff[1000];
+char ABuff[1000];
+WCHAR UBuff[2000];
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ ULONG j;
+ ULONG cb;
+ char *pch;
+
+ printf("Start NlsXlatTest()\n");
+
+ //
+ // First initialize the buffers
+ //
+
+ for (j = 0; j < sizeof(OEMBuff); j++) {
+ OEMBuff[j] = (char)(j * 17);
+ ABuff[j] = (char)(j * 19);
+ }
+
+ //
+ // TEST 1
+ // RtlMultiByteToUnicodeN, RtlUnicodeToMultiByteN
+ //
+
+ printf("Test 1: MultiByteToUnicodeN & RtlUnicodeToMultiByteN\n");
+
+ // TEST 1.1
+ //
+ printf(" Test 1.1: A->U U->A\n");
+
+ RtlMultiByteToUnicodeN(UBuff, sizeof(UBuff), &cb, ABuff, sizeof(ABuff));
+ printf(" %d bytes converted to Unicode\n", cb);
+ RtlUnicodeToMultiByteN(ABuff, sizeof(ABuff), &cb, UBuff, sizeof(UBuff));
+ printf(" %d bytes converted back to ANSI\n", cb);
+
+ for (j = 0; j < sizeof(ABuff); j++) {
+ if (ABuff[j] != (char)(j * 19)) {
+ printf("ABuff[%d] was 0x%02x, now 0x%02x\n",
+ j, (char)(j * 19), ABuff[j]);
+ return FALSE;
+ }
+ }
+ printf(" Test 1.1 OK\n");
+
+ // TEST 1.2
+ //
+ printf(" Test 1.2: A->U U->A (source & dest buffers the same)\n");
+ RtlCopyMemory(UBuff, ABuff, sizeof(ABuff));
+
+ RtlMultiByteToUnicodeN(UBuff, sizeof(UBuff), &cb, UBuff, sizeof(ABuff));
+ printf(" %d bytes converted to Unicode\n", cb);
+ RtlUnicodeToMultiByteN(UBuff, sizeof(ABuff), &cb, UBuff, sizeof(UBuff));
+ printf(" %d bytes converted back to ANSI\n", cb);
+
+ pch = (LPSTR)UBuff;
+ for (j = 0; j < sizeof(ABuff); j++) {
+ if (pch[j] != ABuff[j]) {
+ printf(" ABuff[%d] was 0x%02x, was turned into 0x%02x\n",
+ j, ABuff[j], pch[j]);
+ printf(" Test 1.2 FAILED!\n");
+ return FALSE;
+ }
+ }
+
+ printf(" Test 1.2 OK!\n");
+
+ return TRUE;
+}
diff --git a/private/ntos/rtl/tprefix.c b/private/ntos/rtl/tprefix.c
new file mode 100644
index 000000000..27d7d6988
--- /dev/null
+++ b/private/ntos/rtl/tprefix.c
@@ -0,0 +1,339 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tprefix.c
+
+Abstract:
+
+ Test program for the Prefix table package
+
+Author:
+
+ Gary Kimura [GaryKi] 03-Aug-1989
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+
+//
+// Routines and types for generating random prefixes
+//
+
+ULONG RtlRandom ( IN OUT PULONG Seed );
+ULONG Seed;
+
+PSZ AnotherPrefix(IN ULONG MaxNameLength);
+ULONG AlphabetLength;
+
+//PSZ Alphabet = "AlphaBravoCharlieDeltaEchoFoxtrotGolfHotelIndiaJuliettKiloLimaMikeNovemberOscarPapaQuebecRomeoSierraTangoUniformVictorWhiskeyXrayYankeeZulu";
+
+PSZ Alphabet = "\
+Aa\
+BBbb\
+CCCccc\
+DDDDdddd\
+EEEEEeeeee\
+FFFFFFffffff\
+GGGGGGGggggggg\
+HHHHHHHHhhhhhhhh\
+IIIIIIIIIiiiiiiiii\
+JJJJJJJJJJjjjjjjjjjj\
+KKKKKKKKKKKkkkkkkkkkkk\
+LLLLLLLLLLLLllllllllllll\
+MMMMMMMMMMMMMmmmmmmmmmmmmm\
+NNNNNNNNNNNNNNnnnnnnnnnnnnnn\
+OOOOOOOOOOOOOOOooooooooooooooo\
+PPPPPPPPPPPPPPPPpppppppppppppppp\
+QQQQQQQQQQQQQQQQQqqqqqqqqqqqqqqqqq\
+RRRRRRRRRRRRRRRRRRrrrrrrrrrrrrrrrrrr\
+SSSSSSSSSSSSSSSSSSSsssssssssssssssssss\
+TTTTTTTTTTTTTTTTTTTTtttttttttttttttttttt\
+UUUUUUUUUUUUUUUUUUUUUuuuuuuuuuuuuuuuuuuuuu\
+VVVVVVVVVVVVVVVVVVVVVVvvvvvvvvvvvvvvvvvvvvvv\
+WWWWWWWWWWWWWWWWWWWWWWWwwwwwwwwwwwwwwwwwwwwwww\
+XXXXXXXXXXXXXXXXXXXXXXXXxxxxxxxxxxxxxxxxxxxxxxxx\
+YYYYYYYYYYYYYYYYYYYYYYYYYyyyyyyyyyyyyyyyyyyyyyyyyy\
+ZZZZZZZZZZZZZZZZZZZZZZZZZZzzzzzzzzzzzzzzzzzzzzzzzzzz";
+
+#define BUFFER_LENGTH 8192
+
+CHAR Buffer[BUFFER_LENGTH];
+ULONG NextBufferChar = 0;
+
+//
+// record structure and variables for the prefix table and it
+// elements
+//
+
+typedef struct _PREFIX_NODE {
+ PREFIX_TABLE_ENTRY PfxEntry;
+ STRING String;
+} PREFIX_NODE;
+typedef PREFIX_NODE *PPREFIX_NODE;
+
+#define PREFIXES 512
+
+PREFIX_NODE Prefixes[PREFIXES];
+
+PREFIX_TABLE PrefixTable;
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ ULONG i;
+ PSZ Psz;
+
+ PPREFIX_TABLE_ENTRY PfxEntry;
+ PPREFIX_NODE PfxNode;
+
+ STRING String;
+
+ //
+ // We're starting the test
+ //
+
+ DbgPrint("Start Prefix Test\n");
+
+ //
+ // Calculate the alphabet size for use by AnotherPrefix
+ //
+
+ AlphabetLength = strlen(Alphabet);
+
+ //
+ // Initialize the prefix table
+ //
+
+ PfxInitialize(&PrefixTable);
+
+ //
+ // Insert the root prefix
+ //
+
+ RtlInitString( &Prefixes[i].String, "\\" );
+ if (PfxInsertPrefix( &PrefixTable,
+ &Prefixes[0].String,
+ &Prefixes[0].PfxEntry )) {
+ DbgPrint("Insert root prefix\n");
+ } else {
+ DbgPrint("error inserting root prefix\n");
+ }
+
+ //
+ // Insert prefixes
+ //
+
+ Seed = 0;
+
+ for (i = 1, Psz = AnotherPrefix(3);
+ (i < PREFIXES) && (Psz != NULL);
+ i += 1, Psz = AnotherPrefix(3)) {
+
+ DbgPrint("[0x%x] = ", i);
+ DbgPrint("\"%s\"", Psz);
+
+ RtlInitString(&Prefixes[i].String, Psz);
+
+ if (PfxInsertPrefix( &PrefixTable,
+ &Prefixes[i].String,
+ &Prefixes[i].PfxEntry )) {
+
+ DbgPrint(" inserted in table\n");
+
+ } else {
+
+ DbgPrint(" already in table\n");
+
+ }
+
+ }
+
+ //
+ // Enumerate the prefix table
+ //
+
+ DbgPrint("Enumerate Prefix Table the first time\n");
+
+ for (PfxEntry = PfxNextPrefix(&PrefixTable, TRUE);
+ PfxEntry != NULL;
+ PfxEntry = PfxNextPrefix(&PrefixTable, FALSE)) {
+
+ PfxNode = CONTAINING_RECORD(PfxEntry, PREFIX_NODE, PfxEntry);
+
+ DbgPrint("%s\n", PfxNode->String.Buffer);
+
+ }
+
+ DbgPrint("Start Prefix search 0x%x\n", NextBufferChar);
+
+ //
+ // Search for prefixes
+ //
+
+ for (Psz = AnotherPrefix(4); Psz != NULL; Psz = AnotherPrefix(4)) {
+
+ DbgPrint("0x%x ", NextBufferChar);
+
+ RtlInitString(&String, Psz);
+
+ PfxEntry = PfxFindPrefix( &PrefixTable, &String, FALSE );
+
+ if (PfxEntry == NULL) {
+
+ PfxEntry = PfxFindPrefix( &PrefixTable, &String, TRUE );
+
+ if (PfxEntry == NULL) {
+
+ DbgPrint("Not found \"%s\"\n", Psz);
+
+ NOTHING;
+
+ } else {
+
+ PfxNode = CONTAINING_RECORD(PfxEntry, PREFIX_NODE, PfxEntry);
+
+ DbgPrint("Case blind \"%s\" is \"%s\"\n", Psz, PfxNode->String.Buffer);
+
+ PfxRemovePrefix( &PrefixTable, PfxEntry );
+
+ }
+
+ } else {
+
+ PfxNode = CONTAINING_RECORD(PfxEntry, PREFIX_NODE, PfxEntry);
+
+ DbgPrint( "Case sensitive \"%s\" is \"%s\"\n", Psz, PfxNode->String.Buffer);
+
+ if (PfxNode != &Prefixes[0]) {
+
+ PfxRemovePrefix( &PrefixTable, PfxEntry );
+
+ }
+
+ }
+
+ }
+
+ //
+ // Enumerate the prefix table
+ //
+
+ DbgPrint("Enumerate Prefix Table a second time\n");
+
+ for (PfxEntry = PfxNextPrefix(&PrefixTable, TRUE);
+ PfxEntry != NULL;
+ PfxEntry = PfxNextPrefix(&PrefixTable, FALSE)) {
+
+ PfxNode = CONTAINING_RECORD(PfxEntry, PREFIX_NODE, PfxEntry);
+
+ DbgPrint("%s\n", PfxNode->String.Buffer);
+
+ }
+
+ //
+ // Now enumerate and zero out the table
+ //
+
+ for (PfxEntry = PfxNextPrefix(&PrefixTable, TRUE);
+ PfxEntry != NULL;
+ PfxEntry = PfxNextPrefix(&PrefixTable, FALSE)) {
+
+ PfxNode = CONTAINING_RECORD(PfxEntry, PREFIX_NODE, PfxEntry);
+
+ DbgPrint("Delete %s\n", PfxNode->String.Buffer);
+
+ PfxRemovePrefix( &PrefixTable, PfxEntry );
+
+ }
+
+ //
+ // Enumerate again but this time the table should be empty
+ //
+
+ for (PfxEntry = PfxNextPrefix(&PrefixTable, TRUE);
+ PfxEntry != NULL;
+ PfxEntry = PfxNextPrefix(&PrefixTable, FALSE)) {
+
+ PfxNode = CONTAINING_RECORD(PfxEntry, PREFIX_NODE, PfxEntry);
+
+ DbgPrint("This Node should be gone \"%s\"\n", PfxNode->String.Buffer);
+
+ }
+
+ DbgPrint("End PrefixTest()\n");
+
+ return TRUE;
+}
+
+
+PSZ
+AnotherPrefix(IN ULONG MaxNameLength)
+{
+ ULONG AlphabetPosition;
+
+ ULONG NameLength;
+ ULONG IndividualNameLength;
+
+ ULONG StartBufferPosition;
+ ULONG i;
+ ULONG j;
+
+ //
+ // Check if there is enough room for another name
+ //
+
+ if (NextBufferChar > (BUFFER_LENGTH - (MaxNameLength * 4))) {
+ return NULL;
+ }
+
+ //
+ // Where in the alphabet soup we start
+ //
+
+ AlphabetPosition = RtlRandom(&Seed) % AlphabetLength;
+
+ //
+ // How many names we want in our prefix
+ //
+
+ NameLength = (RtlRandom(&Seed) % MaxNameLength) + 1;
+
+ //
+ // Compute each name
+ //
+
+ StartBufferPosition = NextBufferChar;
+
+ for (i = 0; i < NameLength; i += 1) {
+
+ Buffer[NextBufferChar++] = '\\';
+
+ IndividualNameLength = (RtlRandom(&Seed) % 3) + 1;
+
+ for (j = 0; j < IndividualNameLength; j += 1) {
+
+ Buffer[NextBufferChar++] = Alphabet[AlphabetPosition];
+ AlphabetPosition = (AlphabetPosition + 1) % AlphabetLength;
+
+ }
+
+ }
+
+ Buffer[NextBufferChar++] = '\0';
+
+ return &Buffer[StartBufferPosition];
+
+}
+
diff --git a/private/ntos/rtl/trace.c b/private/ntos/rtl/trace.c
new file mode 100644
index 000000000..b2e826e99
--- /dev/null
+++ b/private/ntos/rtl/trace.c
@@ -0,0 +1,157 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ trace.c
+
+Abstract:
+
+ This is the source file that implements the a general purpose trace
+ package. The trace package is a debug facility for generating
+ arbitrary events into a circular buffer. The debugger than has a
+ !trace command to dump out the last N events from a trace buffer.
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1994
+
+Revision History:
+
+--*/
+
+#include "ntrtlp.h"
+#include "heap.h"
+
+#ifndef THEAP
+#define DPRINTF DbgPrint
+#endif
+
+NTSYSAPI
+PRTL_TRACE_BUFFER
+RtlCreateTraceBuffer(
+ IN ULONG BufferSize,
+ IN ULONG NumberOfEventIds
+ )
+{
+ NTSTATUS Status;
+ PRTL_TRACE_BUFFER TraceBuffer;
+
+ TraceBuffer = NULL;
+ BufferSize += FIELD_OFFSET( RTL_TRACE_BUFFER, EventIdFormatString ) +
+ (NumberOfEventIds * sizeof( PCHAR ));
+
+ Status = NtAllocateVirtualMemory( NtCurrentProcess(),
+ &TraceBuffer,
+ 0,
+ &BufferSize,
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (!NT_SUCCESS( Status )) {
+ KdPrint(( "RTL: Unable to allocate trace buffer (%x bytes) - Status == %x\n", BufferSize, Status ));
+ return NULL;
+ }
+
+ TraceBuffer->Signature = RTL_TRACE_SIGNATURE;
+ TraceBuffer->NumberOfRecords = 0;
+ TraceBuffer->NumberOfEventIds = (USHORT)NumberOfEventIds;
+ TraceBuffer->StartBuffer = (PRTL_TRACE_RECORD)(&TraceBuffer->EventIdFormatString[ NumberOfEventIds ]);
+ TraceBuffer->EndBuffer = (PRTL_TRACE_RECORD)((PCHAR)TraceBuffer + BufferSize);
+ TraceBuffer->ReadRecord = NULL;
+ TraceBuffer->WriteRecord = TraceBuffer->StartBuffer;
+ return TraceBuffer;
+}
+
+
+NTSYSAPI
+void
+RtlDestroyTraceBuffer(
+ IN PRTL_TRACE_BUFFER TraceBuffer
+ )
+{
+ ULONG RegionSize;
+
+ RegionSize = 0;
+ NtFreeVirtualMemory( NtCurrentProcess(),
+ &TraceBuffer,
+ &RegionSize,
+ MEM_RELEASE
+ );
+ return;
+}
+
+
+NTSYSAPI
+void
+RtlTraceEvent(
+ IN PRTL_TRACE_BUFFER TraceBuffer,
+ IN ULONG EventId,
+ IN ULONG NumberOfArguments,
+ ...
+ )
+{
+ va_list arglist;
+ ULONG i, Size;
+ PRTL_TRACE_RECORD p, p1;
+
+ if (TraceBuffer == NULL ||
+ EventId >= TraceBuffer->NumberOfEventIds ||
+ NumberOfArguments > RTL_TRACE_MAX_ARGUMENTS_FOR_EVENT
+ ) {
+ return;
+ }
+
+ Size = FIELD_OFFSET( RTL_TRACE_RECORD, Arguments ) +
+ (NumberOfArguments * sizeof( ULONG ));
+
+ // DPRINTF( "Trace - R: %x W: %x Size: %u", TraceBuffer->ReadRecord, TraceBuffer->WriteRecord, Size );
+
+ p = TraceBuffer->WriteRecord;
+ p1 = (PRTL_TRACE_RECORD)((PCHAR)p + Size);
+ while (TraceBuffer->ReadRecord >= p && TraceBuffer->ReadRecord <= p1) {
+ TraceBuffer->ReadRecord = (PRTL_TRACE_RECORD)((PCHAR)TraceBuffer->ReadRecord + TraceBuffer->ReadRecord->Size);
+ if (TraceBuffer->ReadRecord >= TraceBuffer->EndBuffer) {
+ TraceBuffer->ReadRecord = TraceBuffer->StartBuffer;
+ }
+ }
+
+ if (p1 >= TraceBuffer->EndBuffer) {
+ if (p != TraceBuffer->EndBuffer) {
+ p->Size = (PCHAR)TraceBuffer->EndBuffer - (PCHAR)p;
+ p->EventId = RTL_TRACE_FILLER_EVENT_ID;
+ }
+ p = TraceBuffer->StartBuffer;
+ }
+ p1 = (PRTL_TRACE_RECORD)((PCHAR)p + Size);
+ if (((PCHAR)TraceBuffer->EndBuffer - (PCHAR)p1) < FIELD_OFFSET( RTL_TRACE_RECORD, Arguments )) {
+ Size += (PCHAR)TraceBuffer->EndBuffer - (PCHAR)p1;
+ p1 = (PRTL_TRACE_RECORD)((PCHAR)p + Size);
+ }
+
+ while (TraceBuffer->ReadRecord >= p && TraceBuffer->ReadRecord <= p1) {
+ TraceBuffer->ReadRecord = (PRTL_TRACE_RECORD)((PCHAR)TraceBuffer->ReadRecord + TraceBuffer->ReadRecord->Size);
+ if (TraceBuffer->ReadRecord >= TraceBuffer->EndBuffer) {
+ TraceBuffer->ReadRecord = TraceBuffer->StartBuffer;
+ }
+ }
+
+ p->Size = Size;
+ p->EventId = (USHORT)EventId;
+ p->NumberOfArguments = (USHORT)NumberOfArguments;
+ va_start( arglist, NumberOfArguments );
+ for (i=0; i<NumberOfArguments; i++) {
+ p->Arguments[ i ] = va_arg( arglist, ULONG );
+ }
+ va_end( arglist );
+
+ if (TraceBuffer->ReadRecord == NULL) {
+ TraceBuffer->ReadRecord = p;
+ }
+ TraceBuffer->WriteRecord = p1;
+
+ // DPRINTF( " R: %x W: %x O: %d N: %d\n", TraceBuffer->ReadRecord, TraceBuffer->WriteRecord, OldReadWriteDifference, NewReadWriteDifference );
+
+ return;
+}
diff --git a/private/ntos/rtl/trandom.c b/private/ntos/rtl/trandom.c
new file mode 100644
index 000000000..23602375b
--- /dev/null
+++ b/private/ntos/rtl/trandom.c
@@ -0,0 +1,86 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ trandom.c
+
+Abstract:
+
+ Test program for the random number generator
+
+Author:
+
+ Gary Kimura [GaryKi] 26-May-1989
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+
+int
+_CDECL
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ ULONG Seed;
+ ULONG Temp;
+ ULONG i;
+ CHAR Str[64];
+
+ DbgPrint("Start IntegerToChar and CharToInteger Test\n");
+
+ Seed = 0x12345678;
+ RtlIntegerToChar(Seed, 2, sizeof(Str), Str);
+ DbgPrint("Seed = 0b%s\n", Str);
+ RtlCharToInteger(Str, 2, &Temp);
+ ASSERTMSG( "RtlCharToInteger(2)", (Seed == Temp) );
+
+ RtlIntegerToChar(Seed, 8, sizeof(Str), Str);
+ DbgPrint("Seed = 0o%s\n", Str);
+ RtlCharToInteger(Str, 8, &Temp);
+ ASSERTMSG( "RtlCharToInteger(8)", (Seed == Temp) );
+
+ RtlIntegerToChar(Seed, 10, sizeof(Str), Str);
+ DbgPrint("Seed = %s\n", Str);
+ RtlCharToInteger(Str, 10, &Temp);
+ ASSERTMSG( "RtlCharToInteger(10)", (Seed == Temp) );
+
+ RtlIntegerToChar(Seed, 16, -8, Str);
+ Str[ 8 ] = '\0';
+ DbgPrint("Seed = 0x%s\n", Str);
+ RtlCharToInteger(Str, 16, &Temp);
+ ASSERTMSG( "RtlCharToInteger(16)", (Seed == Temp) );
+
+ DbgPrint("End IntegerToChar and CharToInteger Test\n");
+
+ DbgPrint("Start RandomTest()\n");
+
+ Seed = 0;
+ for (i=0; i<2048; i++) {
+ if ((i % 3) == 0) {
+ DbgPrint("\n");
+ }
+
+ RtlRandom(&Seed);
+ DbgPrint("%p ", Seed);
+
+ RtlIntegerToChar(Seed, 16, sizeof(Str), Str);
+ DbgPrint("= %s ", Str);
+
+ }
+
+ DbgPrint("\n");
+
+ DbgPrint("End RandomTest()\n");
+
+ return TRUE;
+
+}
diff --git a/private/ntos/rtl/triangle.c b/private/ntos/rtl/triangle.c
new file mode 100644
index 000000000..185d26d5e
--- /dev/null
+++ b/private/ntos/rtl/triangle.c
@@ -0,0 +1,1420 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ Triangle.c
+
+Abstract:
+
+ This module implements the general splay utilities for a two link
+ triangular splay structure.
+
+Author:
+
+ Gary Kimura [GaryKi] 28-May-1989
+
+Environment:
+
+ Pure utility routine
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include "triangle.h"
+
+
+//
+// There are three type of swap macros. The first two (are really the same)
+// are used to swap pointer and ulongs. The last macro is used to swap refs
+// but it does not swap the ref type flags.
+//
+
+#define SwapPointers(Ptr1, Ptr2) { \
+ PVOID _SWAP_POINTER_TEMP; \
+ _SWAP_POINTER_TEMP = (PVOID)(Ptr1); \
+ (Ptr1) = (Ptr2); \
+ (Ptr2) = _SWAP_POINTER_TEMP; \
+ }
+
+#define SwapUlongs(Ptr1, Ptr2) { \
+ ULONG _SWAP_POINTER_TEMP; \
+ _SWAP_POINTER_TEMP = (ULONG)(Ptr1); \
+ (Ptr1) = (Ptr2); \
+ (Ptr2) = _SWAP_POINTER_TEMP; \
+ }
+
+#define SwapRefsButKeepFlags(Ref1, Ref2) { \
+ ULONG _SWAP_ULONG_TEMP; \
+ _SWAP_ULONG_TEMP = (ULONG)(Ref1); \
+ (Ref1) = ((Ref2) & 0xfffffffc) | ((Ref1) & 0x00000003); \
+ (Ref2) = (_SWAP_ULONG_TEMP & 0xfffffffc) | ((Ref2) & 0x00000003); \
+ }
+
+//
+// The macro SetRefViaPointer takes a pointer to a ref and checks to see if
+// it is a valid pointer. If it is a valid pointer it copies in the ref
+// a ulong, but does not overwrite the ref flags already in the ref.
+//
+
+#define SetRefViaPointer(Ref, Ulong) { \
+ if (Ref != NULL) { \
+ (*(Ref)) = (((ULONG)(Ulong)) & 0xfffffffc) | ((ULONG)(*(Ref)) & 0x00000003); \
+ } \
+}
+
+
+//
+// The following five procedures are local to triangle.c and are used to
+// help manipluate the splay links. The first two procedures take a pointer
+// to a splay link and returns the address of the ref that points back to the
+// input link, via either the parent or child. They return NULL if there is
+// not a back pointer. The result of these two procedures is often used in
+// the code with the SetRefViaPointer macro. The third procedure is used
+// to swap the position to two splay links in the tree (i.e., the links swap
+// position, but everyone else stays stationary). This is a general procedure
+// that can will swap any two nodes, irregardless of their relative positions
+// in the tree. The last two procedures do a single rotation about a
+// tree node. They either rotate left or rotate right and assume that the
+// appropriate child exists (i.e., for rotate left a right child exists and
+// for rotate right a left child exists).
+//
+
+PULONG
+TriAddressOfBackRefViaParent (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+PULONG
+TriAddressOfBackRefViaChild (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+VOID
+TriSwapSplayLinks (
+ IN PTRI_SPLAY_LINKS Link1,
+ IN PTRI_SPLAY_LINKS Link2
+ );
+
+VOID
+TriRotateRight (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+VOID
+TriRotateLeft (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+PTRI_SPLAY_LINKS
+TriSplay (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ This Splay function takes as input a pointer to a splay link in a tree
+ and splays the tree. Its function return value is a pointer to the
+ root of the splayed tree.
+
+Arguments:
+
+ Links - Supplies the pointer to a splay link in a tree
+
+Return Values:
+
+ PRTI_SPLAY_LINKS - Returns a pointer to the root of the splayed tree
+
+--*/
+
+{
+ PTRI_SPLAY_LINKS Parent;
+ PTRI_SPLAY_LINKS GrandParent;
+
+ //
+ // While Links is not the root we test and rotate until it is the root.
+ //
+
+ while (!TriIsRoot(Links)) {
+
+ //
+ // Get Parent and then check if we don't have a grandparent.
+ //
+
+ Parent = TriParent(Links);
+
+ if (TriIsRoot(Parent)) {
+
+ //
+ // No grandparent so check for single rotation
+ //
+
+ if (TriIsLeftChild(Links)) {
+
+ //
+ // do the following single rotation
+ //
+ // Parent Links
+ // / ==> \
+ // Links Parent
+ //
+
+ TriRotateRight(Parent);
+
+ } else { // TriIsRightChild(Links)
+
+ //
+ // do the following single rotation
+ //
+ //
+ // Parent Links
+ // \ ==> /
+ // Links Parent
+ //
+
+ TriRotateLeft(Parent);
+
+ }
+
+ } else { // !TriIsRoot(Parent)
+
+ //
+ // Get grandparent and check for the four double rotation
+ // cases
+ //
+
+ GrandParent = TriParent(Parent);
+
+ if (TriIsLeftChild(Links)) {
+
+ if (TriIsLeftChild(Parent)) {
+
+ //
+ // do the following double rotation
+ //
+ // GP L
+ // / \
+ // P ==> P
+ // / \
+ // L GP
+ //
+
+ TriRotateRight(GrandParent);
+ TriRotateRight(Parent);
+
+ } else { // TriIsRightChild(Parent)
+
+ //
+ // do the following double rotation
+ //
+ // GP L
+ // \ / \
+ // P ==> GP P
+ // /
+ // L
+ //
+
+ TriRotateRight(Parent);
+ TriRotateLeft(GrandParent);
+
+ }
+
+ } else { // TriIsRightChild(Links);
+
+ if (TriIsLeftChild(Parent)) {
+
+ //
+ // do the following double rotation
+ //
+ // GP L
+ // / / \
+ // P ==> P GP
+ // \
+ // L
+ //
+
+ TriRotateLeft(Parent);
+ TriRotateRight(GrandParent);
+
+ } else { // TriIsRightChild(Parent)
+
+ //
+ // do the following double rotation
+ //
+ // GP L
+ // \ /
+ // P ==> P
+ // \ /
+ // L GP
+ //
+
+ TriRotateLeft(GrandParent);
+ TriRotateLeft(Parent);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return Links;
+
+}
+
+
+PTRI_SPLAY_LINKS
+TriDelete (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ This Delete function takes as input a pointer to a splay link in a tree
+ and deletes that node from the tree. Its function return value is a
+ pointer to the root the tree. If the tree is now empty, the return
+ value is NULL.
+
+Arguments:
+
+ Links - Supplies the pointer to a splay link in a tree
+
+Return Values:
+
+ PRTI_SPLAY_LINKS - Returns a pointer to the root of the splayed tree
+
+--*/
+
+{
+ PTRI_SPLAY_LINKS Predecessor;
+ PTRI_SPLAY_LINKS Parent;
+ PTRI_SPLAY_LINKS Child;
+
+ PULONG ParentChildRef;
+
+ //
+ // First check to see if Links as two children. If it does then swap
+ // Links with its subtree predecessor. Now we are guaranteed that Links
+ // has at most one child.
+ //
+
+ if ((TriLeftChild(Links) != NULL) && (TriRightChild(Links) != NULL)) {
+
+ //
+ // get the predecessor, and swap their position in the tree
+ //
+
+ Predecessor = TriSubtreePredecessor(Links);
+ TriSwapSplayLinks(Predecessor, Links);
+
+ }
+
+ //
+ // If Links has no children then delete links by checking if it is
+ // already the root or has a parent. If it is the root then the
+ // tree is now empty, otherwise set the appropriate parent's child
+ // pointer, and possibly sibling, and splay the parent.
+ //
+
+ if ((TriLeftChild(Links) == NULL) && (TriRightChild(Links) == NULL)) {
+
+ //
+ // Links has no children, if it is the root then return NULL
+ //
+
+ if (TriIsRoot(Links)) {
+
+ return NULL;
+
+ }
+
+ //
+ // Links has no children, check to see if links is an only child
+ //
+
+ Parent = TriParent(Links);
+ if (MakeIntoPointer(Parent->Refs.Child) == Links &&
+ MakeIntoPointer(Links->Refs.ParSib) == Parent) {
+
+ //
+ // Links has no children and is an only child. So simply make
+ // our parent have no children and splay our parent.
+ //
+ // Parent Parent
+ // | ==>
+ // Links
+ //
+
+ Parent->Refs.Child = 0;
+ return TriSplay(Parent);
+
+ } else if (TriIsLeftChild(Links)) {
+
+ //
+ // Links has no children and has a right sibling. So make the
+ // parent's child Ref be the right sibling, splay the parent.
+ //
+ // Parent Parent
+ // / \ ==> \
+ // Links Sibling Sibling
+ //
+
+ Parent->Refs.Child = MakeIntoRightChildRef(Links->Refs.ParSib);
+ return TriSplay(Parent);
+
+ } else { // TriIsRightChild(Links)
+
+ //
+ // Links has no children and has a left sibling. So make link's
+ // back via its parent into a parent ref of link's parent, and
+ // splay the parent.
+ //
+ // Parent Parent
+ // / \ /
+ // Sibling Links ==> Sibling
+ //
+
+ ParentChildRef = TriAddressOfBackRefViaParent(Links);
+ *ParentChildRef = MakeIntoParentRef(Parent);
+ return TriSplay(Parent);
+
+ }
+
+ }
+
+ //
+ // otherwise Links has one child. If it is the root then make the child
+ // the new root, otherwise link together the child and parent, and splay
+ // the parent. But first remember who our child is.
+ //
+
+ if (TriLeftChild(Links) != NULL) {
+ Child = TriLeftChild(Links);
+ } else {
+ Child = TriRightChild(Links);
+ }
+
+ //
+ // If links is the root then we make the child the root and return the
+ // child.
+ //
+
+ if (TriIsRoot(Links)) {
+ Child->Refs.ParSib = MakeIntoParentRef(Child);
+ return Child;
+ }
+
+ //
+ // Links is not the root, so set links's back ref via its parent to be
+ // links's child and the set the child's ParSib to be link's ParSib, and
+ // splay the parent. This will handle the case where link is an only
+ // or has a sibling on either side.
+ //
+
+ Parent = TriParent(Links);
+ ParentChildRef = TriAddressOfBackRefViaParent(Links);
+ SetRefViaPointer(ParentChildRef, Child);
+ Child->Refs.ParSib = Links->Refs.ParSib;
+
+ return TriSplay(Parent);
+
+}
+
+
+PTRI_SPLAY_LINKS
+TriSubtreeSuccessor (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ This SubTreeSuccessor function takes as input a pointer to a splay link
+ in a tree and returns a pointer to the successor of the input node of
+ the subtree rooted at the input node. If there is not a successor, the
+ return value is NULL.
+
+Arguments:
+
+ Links - Supplies the pointer to a splay link in a tree
+
+Return Values:
+
+ PRTI_SPLAY_LINKS - Returns a pointer to the successor in the subtree
+
+--*/
+
+{
+ PTRI_SPLAY_LINKS Ptr;
+
+ //
+ // check to see if there is a right subtree to the input link
+ // if there is then the subtree successor is the left most node in
+ // the right subtree. That is find and return P in the following diagram
+ //
+ // Links
+ // \
+ // .
+ // .
+ // .
+ // /
+ // P
+ // \
+ //
+
+ if ((Ptr = TriRightChild(Links)) != NULL) {
+
+ while (TriLeftChild(Ptr) != NULL) {
+ Ptr = TriLeftChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ //
+ // Otherwise we do not have a subtree successor so we simply return NULL
+ //
+
+ return NULL;
+
+}
+
+
+PTRI_SPLAY_LINKS
+TriSubtreePredecessor (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ This SubTreePredecessor function takes as input a pointer to a splay link
+ in a tree and returns a pointer to the predecessor of the input node of
+ the subtree rooted at the input node. If there is not a predecessor,
+ the return value is NULL.
+
+Arguments:
+
+ Links - Supplies the pointer to a splay link in a tree
+
+Return Values:
+
+ PRTI_SPLAY_LINKS - Returns a pointer to the predecessor in the subtree
+
+--*/
+
+{
+ PTRI_SPLAY_LINKS Ptr;
+
+ //
+ // check to see if there is a left subtree to the input link
+ // if there is then the subtree predecessor is the right most node in
+ // the left subtree. That is find and return P in the following diagram
+ //
+ // Links
+ // /
+ // .
+ // .
+ // .
+ // P
+ // /
+ //
+
+ if ((Ptr = TriLeftChild(Links)) != NULL) {
+
+ while (TriRightChild(Ptr) != NULL) {
+ Ptr = TriRightChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ //
+ // Otherwise we do not have a subtree predecessor so we simply return NULL
+ //
+
+ return NULL;
+
+}
+
+
+PTRI_SPLAY_LINKS
+TriRealSuccessor (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ This RealSuccess function takes as input a pointer to a splay link in a
+ tree and returns a pointer to the successor of the input node within the
+ entire tire. If there is not a successor, the return value is NULL.
+
+Arguments:
+
+ Links - Supplies the pointer to a splay link in a tree
+
+Return Values:
+
+ PRTI_SPLAY_LINKS - Returns a pointer to the successor in the entire tree
+
+--*/
+
+{
+ PTRI_SPLAY_LINKS Ptr;
+
+ //
+ // first check to see if there is a right subtree to the input link
+ // if there is then the real successor is the left most node in
+ // the right subtree. That is find and return P in the following diagram
+ //
+ // Links
+ // \
+ // .
+ // .
+ // .
+ // /
+ // P
+ // \
+ //
+
+ if ((Ptr = TriRightChild(Links)) != NULL) {
+
+ while (TriLeftChild(Ptr) != NULL) {
+ Ptr = TriLeftChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ //
+ // we do not have a right child so check to see if have a parent and if
+ // so find the first ancestor that we are a left decendent of. That
+ // is find and return P in the following diagram
+ //
+ // P
+ // /
+ // .
+ // .
+ // .
+ // Links
+ //
+
+ Ptr = Links;
+ while (!TriIsLeftChild(Ptr) && !TriIsRoot(Ptr)) { // (TriIsRightChild(Ptr)) {
+ Ptr = TriParent(Ptr);
+ }
+
+ if (TriIsLeftChild(Ptr)) {
+ return TriParent(Ptr);
+ }
+
+ //
+ // Otherwise we do not have a real successor so we simply return NULL
+ //
+
+ return NULL;
+
+}
+
+
+PTRI_SPLAY_LINKS
+TriRealPredecessor (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+/*++
+
+Routine Description:
+
+ This RealPredecessor function takes as input a pointer to a splay link in
+ a tree and returns a pointer to the predecessor of the input node within
+ the entire tree. If there is not a predecessor, the return value is NULL.
+
+Arguments:
+
+ Links - Supplies the pointer to a splay link in a tree
+
+Return Values:
+
+ PRTI_SPLAY_LINKS - Returns a pointer to the predecessor in the entire tree
+
+--*/
+
+{
+ PTRI_SPLAY_LINKS Ptr;
+
+ //
+ // first check to see if there is a left subtree to the input link
+ // if there is then the real predecessor is the right most node in
+ // the left subtree. That is find and return P in the following diagram
+ //
+ // Links
+ // /
+ // .
+ // .
+ // .
+ // P
+ // /
+ //
+
+ if ((Ptr = TriLeftChild(Links)) != NULL) {
+
+ while (TriRightChild(Ptr) != NULL) {
+ Ptr = TriRightChild(Ptr);
+ }
+
+ return Ptr;
+
+ }
+
+ //
+ // we do not have a left child so check to see if have a parent and if
+ // so find the first ancestor that we are a right decendent of. That
+ // is find and return P in the following diagram
+ //
+ // P
+ // \
+ // .
+ // .
+ // .
+ // Links
+ //
+
+ Ptr = Links;
+ while (TriIsLeftChild(Ptr)) {
+ Ptr = TriParent(Ptr);
+ }
+
+ if (!TriIsLeftChild(Ptr) && !TriIsRoot(Ptr)) { // (TriIsRightChild(Ptr)) {
+ return TriParent(Ptr);
+ }
+
+ //
+ // Otherwise we do not have a real predecessor so we simply return NULL
+ //
+
+ return NULL;
+
+}
+
+
+PULONG
+TriAddressOfBackRefViaParent (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+{
+ PTRI_SPLAY_LINKS Ptr;
+
+ //
+ // If Links is the root then we do not have a back pointer via our parent
+ // so return NULL
+ //
+
+ if (TriIsRoot(Links)) {
+
+ return NULL;
+
+ }
+
+ //
+ // We are not the root so find our parent and if our parent directly points
+ // to us we return the address of our parent's reference to us. Otherwise
+ // (we must be a right child with a sibling) so return the address of
+ // our sibling's ParSib reference to us.
+ //
+
+ Ptr = TriParent(Links);
+ if (MakeIntoPointer(Ptr->Refs.Child) == Links) {
+ return &(Ptr->Refs.Child);
+ } else {
+ return &(MakeIntoPointer(Ptr->Refs.Child)->Refs.ParSib);
+ }
+
+}
+
+
+PULONG
+TriAddressOfBackRefViaChild (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+{
+ PTRI_SPLAY_LINKS Ptr;
+
+ //
+ // Make Ptr be the same reference as found in our child field.
+ //
+
+ Ptr = MakeIntoPointer(Links->Refs.Child);
+
+ //
+ // If our child pointer is null then we don't have a back pointer
+ // via our child so return NULL.
+ //
+
+ if (Ptr == NULL) {
+ return NULL;
+
+ //
+ // if our child directly reference's us (then we only have one child)
+ // return the address of the ParSib of our only child.
+ //
+
+ } else if (MakeIntoPointer(Ptr->Refs.ParSib) == Links) {
+ return &(Ptr->Refs.ParSib);
+
+ //
+ // otherwise we have two children so return the address of the ParSib
+ // of the second child.
+ //
+
+ } else {
+ return &(MakeIntoPointer(Ptr->Refs.ParSib)->Refs.ParSib);
+
+ }
+
+}
+
+
+VOID
+TriSwapSplayLinks (
+ IN PTRI_SPLAY_LINKS Link1,
+ IN PTRI_SPLAY_LINKS Link2
+ )
+
+{
+ PULONG Parent1ChildRef;
+ PULONG Parent2ChildRef;
+
+ PULONG Child1ParSibRef;
+ PULONG Child2ParSibRef;
+
+ //
+ // We have the following situation
+ //
+ //
+ // Parent1 Parent2
+ // | |
+ // | |
+ // Link1 Link2
+ // / \ / \
+ // / \ / \
+ // LC1 RC1 LC2 RC2
+ //
+ // where one of the links can possibly be the root and one of the links
+ // can possibly be a direct child of the other, or can be connected
+ // via their sibling pointers. Without loss of generality we'll make
+ // link2 be the possible and root and link1 be the possible child, or
+ // link2 have a parsib pointer to link1
+ //
+
+ if ((TriIsRoot(Link1)) ||
+ (TriParent(Link2) == Link1) ||
+ (MakeIntoPointer(Link1->Refs.ParSib) == Link2)) {
+
+ SwapPointers(Link1, Link2);
+
+ }
+
+ //
+ // The cases we need to handle are
+ //
+ // 1. Link1 is not a child of link2, link2 is not the root, and they are not siblings
+ // 2. Link1 is not a child of link2, link2 is not the root, and they are siblings
+ //
+ // 3. Link1 is not a child of link2, link2 is the root
+ //
+ // 4. Link1 is an only child of link2, and link2 is not the root
+ // 5. Link1 is an only child of link2, and link2 is the root
+ //
+ // 6. Link1 is a left child of link2 (has a sibling), and link2 is not the root
+ // 7. Link1 is a left child of link2 (has a sibling), and link2 is the root
+ //
+ // 8. Link1 is a right child of link2 (has a sibling), and link2 is not the root
+ // 9. Link1 is a right child of link2 (has a sibling), and link2 is the root
+ //
+ // Each case will be handled separately
+ //
+
+ if (TriParent(Link1) != Link2) {
+
+ if (!TriIsRoot(Link2)) {
+
+ if (MakeIntoPointer(Link2->Refs.ParSib) != Link1) {
+
+ //
+ // Case 1 - Link1 is not a child of link2,
+ // Link2 is not the root, and
+ // they are not siblings
+ //
+
+ Parent1ChildRef = TriAddressOfBackRefViaParent(Link1);
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Parent2ChildRef = TriAddressOfBackRefViaParent(Link2);
+ Child2ParSibRef = TriAddressOfBackRefViaChild(Link2);
+ SwapUlongs(Link1->Refs.Child, Link2->Refs.Child);
+ SwapUlongs(Link1->Refs.ParSib, Link2->Refs.ParSib);
+ SetRefViaPointer(Parent1ChildRef, Link2);
+ SetRefViaPointer(Parent2ChildRef, Link1);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Child2ParSibRef, Link1);
+
+ } else {
+
+ //
+ // Case 2 - Link1 is not a child of link2,
+ // Link2 is not the root, and
+ // they are siblings
+ //
+
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Parent2ChildRef = TriAddressOfBackRefViaParent(Link2);
+ Child2ParSibRef = TriAddressOfBackRefViaChild(Link2);
+ SwapUlongs(Link1->Refs.Child, Link2->Refs.Child);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Child2ParSibRef, Link1);
+ *Parent2ChildRef = MakeIntoLeftChildRef(Link1);
+ Link2->Refs.ParSib = Link1->Refs.ParSib;
+ Link1->Refs.ParSib = MakeIntoSiblingRef(Link2);
+
+ }
+
+ } else {
+
+ //
+ // Case 3 - Link1 is not a child of link2, and
+ // Link2 is the root
+ //
+
+ Parent1ChildRef = TriAddressOfBackRefViaParent(Link1);
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Child2ParSibRef = TriAddressOfBackRefViaChild(Link2);
+ SwapUlongs(Link1->Refs.Child, Link2->Refs.Child);
+ Link2->Refs.ParSib = Link1->Refs.ParSib;
+ Link1->Refs.ParSib = MakeIntoParentRef(Link1);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Child2ParSibRef, Link1);
+ SetRefViaPointer(Parent1ChildRef, Link2);
+
+ }
+
+ } else { // TriParent(Link1) == Link2
+
+ if (MakeIntoPointer(Link2->Refs.Child) == Link1 &&
+ MakeIntoPointer(Link1->Refs.ParSib) == Link2) { // Link1 is an only child
+
+ if (!TriIsRoot(Link2)) {
+
+ //
+ // Case 4 - Link1 is an only child of link2, and
+ // Link2 is not the root
+ //
+
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Parent2ChildRef = TriAddressOfBackRefViaParent(Link2);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Parent2ChildRef, Link1);
+ Link1->Refs.ParSib = Link2->Refs.ParSib;
+ Link2->Refs.ParSib = MakeIntoParentRef(Link1);
+ SwapRefsButKeepFlags(Link1->Refs.Child, Link2->Refs.Child);
+ SetRefViaPointer(&Link1->Refs.Child, Link2);
+
+ } else {
+
+ //
+ // Case 5 - Link1 is an only child of link2, and
+ // Link2 is the root
+ //
+
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ Link1->Refs.ParSib = MakeIntoParentRef(Link1);
+ Link2->Refs.ParSib = MakeIntoParentRef(Link1);
+ SwapRefsButKeepFlags(Link1->Refs.Child, Link2->Refs.Child);
+ SetRefViaPointer(&Link1->Refs.Child, Link2);
+
+ }
+
+ } else if (TriIsLeftChild(Link1)) { // and link1 has a sibling
+
+ if (!TriIsRoot(Link2)) {
+
+ //
+ // Case 6 - Link1 is a left child of link2 (has a sibling), and
+ // Link2 is not the root
+ //
+
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Parent2ChildRef = TriAddressOfBackRefViaParent(Link2);
+ Child2ParSibRef = TriAddressOfBackRefViaChild(Link2);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Parent2ChildRef, Link1);
+ SetRefViaPointer(Child2ParSibRef, Link1);
+ Link2->Refs.Child = Link1->Refs.Child;
+ Link1->Refs.Child = MakeIntoLeftChildRef(Link2);
+ SwapUlongs(Link1->Refs.ParSib, Link2->Refs.ParSib);
+
+ } else {
+
+ //
+ // Case 7 - Link1 is a left child of link2 (has a sibling), and
+ // Link2 is the root
+ //
+
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Child2ParSibRef = TriAddressOfBackRefViaChild(Link2);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Child2ParSibRef, Link1);
+ Link2->Refs.Child = Link1->Refs.Child;
+ Link1->Refs.Child = MakeIntoLeftChildRef(Link2);
+ Link2->Refs.ParSib = Link1->Refs.ParSib;
+ Link1->Refs.ParSib = MakeIntoParentRef(Link1);
+
+ }
+
+ } else { // TriIsRightChild(Link1) and Link1 has a sibling
+
+ if (!TriIsRoot(Link2)) {
+
+ //
+ // Case 8 - Link1 is a right child of link2 (has a sibling), and
+ // Link2 is not the root
+ //
+
+ Parent1ChildRef = TriAddressOfBackRefViaParent(Link1);
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ Parent2ChildRef = TriAddressOfBackRefViaParent(Link2);
+ SetRefViaPointer(Parent1ChildRef, Link2);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SetRefViaPointer(Parent2ChildRef, Link1);
+ SwapUlongs(Link1->Refs.Child, Link2->Refs.Child);
+ Link1->Refs.ParSib = Link2->Refs.ParSib;
+ Link2->Refs.ParSib = MakeIntoParentRef(Link1);
+
+ } else {
+
+ //
+ // Case 9 - Link1 is a right child of link2 (has a sibling), and
+ // Link2 is the root
+ //
+
+ Parent1ChildRef = TriAddressOfBackRefViaParent(Link1);
+ Child1ParSibRef = TriAddressOfBackRefViaChild(Link1);
+ SetRefViaPointer(Parent1ChildRef, Link2);
+ SetRefViaPointer(Child1ParSibRef, Link2);
+ SwapUlongs(Link1->Refs.Child, Link2->Refs.Child);
+ Link1->Refs.ParSib = MakeIntoParentRef(Link1);
+ Link1->Refs.ParSib = MakeIntoParentRef(Link1);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+VOID
+TriRotateRight (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+{
+ BOOLEAN IsRoot;
+ PULONG ParentChildRef;
+ ULONG SavedParSibRef;
+ PTRI_SPLAY_LINKS LeftChild;
+ PTRI_SPLAY_LINKS a,b,c;
+
+ //
+ // We perform the following rotation
+ //
+ // -Links- -LeftChild-
+ // / \ / \
+ // LeftChild c ==> a Links
+ // / \ / \
+ // a b b c
+ //
+ // where Links is a possible root and a,b, and c are all optional.
+ // We will consider each combination of optional children individually
+ // and handle the case of the root when we set T's parsib pointer and
+ // the backpointer to T.
+ //
+
+ //
+ // First remember if we are the root and if not also remember our
+ // back ref via our parent.
+ //
+
+ if (TriIsRoot(Links)) {
+ IsRoot = TRUE;
+ } else {
+ IsRoot = FALSE;
+ ParentChildRef = TriAddressOfBackRefViaParent(Links);
+ SavedParSibRef = Links->Refs.ParSib;
+ }
+
+ //
+ // Now we set LeftChild, a, b, and c, and then later check for the
+ // different combinations. In the diagrams only those links that
+ // need to change are shown in the after part.
+ //
+
+ LeftChild = TriLeftChild(Links);
+ a = TriLeftChild(LeftChild);
+ b = TriRightChild(LeftChild);
+ c = TriRightChild(Links);
+
+ if ((a != NULL) && (b != NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / \ ==> \
+ // LeftChild c a ----- Links
+ // / \ /
+ // a b b - c
+ //
+
+ a->Refs.ParSib = MakeIntoSiblingRef(Links);
+ b->Refs.ParSib = MakeIntoSiblingRef(c);
+ Links->Refs.Child = MakeIntoLeftChildRef(b);
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+
+ } else if ((a != NULL) && (b != NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / ==> \
+ // LeftChild a ----- Links
+ // / \ /
+ // a b b --
+ //
+
+ a->Refs.ParSib = MakeIntoSiblingRef(Links);
+ b->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.Child = MakeIntoLeftChildRef(b);
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+
+ } else if ((a != NULL) && (b == NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / \ ==> \
+ // LeftChild c a ----- Links
+ // / /
+ // a c
+ //
+
+ a->Refs.ParSib = MakeIntoSiblingRef(Links);
+ Links->Refs.Child = MakeIntoRightChildRef(c);
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+
+ } else if ((a != NULL) && (b == NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / ==> \
+ // LeftChild a ----- Links
+ // / /
+ // a
+ //
+
+ a->Refs.ParSib = MakeIntoSiblingRef(Links);
+ Links->Refs.Child = 0L;
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+
+ } else if ((a == NULL) && (b != NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / \ ==> / \
+ // LeftChild c Links
+ // \ /
+ // b b - c
+ //
+
+ b->Refs.ParSib = MakeIntoSiblingRef(c);
+ Links->Refs.Child = MakeIntoLeftChildRef(b);
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+ LeftChild->Refs.Child = MakeIntoRightChildRef(Links);
+
+ } else if ((a == NULL) && (b != NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / ==> / \
+ // LeftChild Links
+ // \ /
+ // b b -
+ //
+
+ b->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.Child = MakeIntoLeftChildRef(b);
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+ LeftChild->Refs.Child = MakeIntoRightChildRef(Links);
+
+ } else if ((a == NULL) && (b == NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / \ ==> / \
+ // LeftChild c Links
+ // /
+ // c
+ //
+
+ Links->Refs.Child = MakeIntoRightChildRef(c);
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+ LeftChild->Refs.Child = MakeIntoRightChildRef(Links);
+
+ } else if ((a == NULL) && (b == NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links LeftChild
+ // / ==> / \
+ // LeftChild Links
+ // /
+ //
+
+ Links->Refs.Child = 0L;
+ Links->Refs.ParSib = MakeIntoParentRef(LeftChild);
+ LeftChild->Refs.Child = MakeIntoRightChildRef(Links);
+
+ }
+
+ if (IsRoot) {
+ LeftChild->Refs.ParSib = MakeIntoParentRef(LeftChild);
+ } else {
+ LeftChild->Refs.ParSib = SavedParSibRef;
+ SetRefViaPointer(ParentChildRef, LeftChild);
+ }
+
+}
+
+
+VOID
+TriRotateLeft (
+ IN PTRI_SPLAY_LINKS Links
+ )
+
+{
+ BOOLEAN IsRoot;
+ PULONG ParentChildRef;
+ ULONG SavedParSibRef;
+ PTRI_SPLAY_LINKS RightChild;
+ PTRI_SPLAY_LINKS a,b,c;
+
+ //
+ // We perform the following rotation
+ //
+ // -Links- -RightChild-
+ // / \ / \
+ // a RightChild ==> Links c
+ // / \ / \
+ // b c a b
+ //
+ // where Links is a possible root and a,b, and c are all optional.
+ // We will consider each combination of optional children individually
+ // and handle the case of the root when we set T's parsib pointer and
+ // the backpointer to T.
+ //
+
+ //
+ // First remember if we are the root and if not also remember our
+ // back ref via our parent.
+ //
+
+ if (TriIsRoot(Links)) {
+ IsRoot = TRUE;
+ } else {
+ IsRoot = FALSE;
+ ParentChildRef = TriAddressOfBackRefViaParent(Links);
+ SavedParSibRef = Links->Refs.ParSib;
+ }
+
+ //
+ // Now we set RightChild, a, b, and c, and then later check for the
+ // different combinations. In the diagrams only those links that
+ // need to change are shown in the after part.
+ //
+
+ RightChild = TriRightChild(Links);
+ a = TriLeftChild(Links);
+ b = TriLeftChild(RightChild);
+ c = TriRightChild(RightChild);
+
+ if ((a != NULL) && (b != NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // / \ /
+ // a RightChild ==> Links ----- c
+ // / \ \
+ // b c a - b
+ //
+
+ a->Refs.ParSib = MakeIntoSiblingRef(b);
+ b->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.ParSib = MakeIntoSiblingRef(c);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a != NULL) && (b != NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // / \ /
+ // a RightChild ==> Links -----
+ // / \
+ // b a - b
+ //
+
+ a->Refs.ParSib = MakeIntoSiblingRef(b);
+ b->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.ParSib = MakeIntoParentRef(RightChild);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a != NULL) && (b == NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // / \ /
+ // a RightChild ==> Links ----- c
+ // \
+ // c a -
+ //
+
+ a->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.ParSib = MakeIntoSiblingRef(c);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a != NULL) && (b == NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // / \ /
+ // a RightChild ==> Links -----
+ //
+ // a -
+ //
+
+ a->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.ParSib = MakeIntoParentRef(RightChild);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a == NULL) && (b != NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // \ /
+ // RightChild ==> Links ----- c
+ // / \ / \
+ // b c b
+ //
+
+ b->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.Child = MakeIntoRightChildRef(b);
+ Links->Refs.ParSib = MakeIntoSiblingRef(c);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a == NULL) && (b != NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // \ /
+ // RightChild ==> Links -----
+ // / / \
+ // b b
+ //
+
+ b->Refs.ParSib = MakeIntoParentRef(Links);
+ Links->Refs.Child = MakeIntoRightChildRef(b);
+ Links->Refs.ParSib = MakeIntoParentRef(RightChild);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a == NULL) && (b == NULL) && (c != NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // \ /
+ // RightChild ==> Links ----- c
+ // \ /
+ // c
+ //
+
+ Links->Refs.Child = 0L;
+ Links->Refs.ParSib = MakeIntoSiblingRef(c);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ } else if ((a == NULL) && (b == NULL) && (c == NULL)) {
+
+ //
+ // Handle the following case
+ //
+ // Links RightChild
+ // \ /
+ // RightChild ==> Links -----
+ // /
+ //
+ //
+
+ Links->Refs.Child = 0L;
+ Links->Refs.ParSib = MakeIntoParentRef(RightChild);
+ RightChild->Refs.Child = MakeIntoLeftChildRef(Links);
+
+ }
+
+ if (IsRoot) {
+ RightChild->Refs.ParSib = MakeIntoParentRef(RightChild);
+ } else {
+ RightChild->Refs.ParSib = SavedParSibRef;
+ SetRefViaPointer(ParentChildRef, RightChild);
+ }
+
+}
diff --git a/private/ntos/rtl/triangle.h b/private/ntos/rtl/triangle.h
new file mode 100644
index 000000000..323d8e274
--- /dev/null
+++ b/private/ntos/rtl/triangle.h
@@ -0,0 +1,325 @@
+//
+// Define the two pointer triangle splay links and the associated
+// manipuliation macros and routines. Note that the tri_splay_links should
+// be an opaque type. Routine are provided to traverse and manipulate the
+// structure.
+//
+// The structure of a tri_splay_links record is really
+//
+// typedef struct _TRI_SPLAY_LINKS {
+// ULONG ParSib; // struct _TRI_SPLAY_LINKS *ParSib;
+// ULONG Child; // struct _TRI_SPLAY_LINKS *Child;
+// } TRI_SPLAY_LINKS;
+//
+// However to aid in debugging (and without extra cost) we declare the
+// structure to be a union so we can also reference the links as pointers
+// in the debugger.
+//
+
+typedef union _TRI_SPLAY_LINKS {
+ struct {
+ ULONG ParSib;
+ ULONG Child;
+ } Refs;
+ struct {
+ union _TRI_SPLAY_LINKS *ParSibPtr;
+ union _TRI_SPLAY_LINKS *ChildPtr;
+ } Ptrs;
+} TRI_SPLAY_LINKS;
+typedef TRI_SPLAY_LINKS *PTRI_SPLAY_LINKS;
+
+//
+// The macro procedure InitializeSplayLinks takes as input a pointer to
+// splay link and initializes its substructure. All splay link nodes must
+// be initialized before they are used in the different splay routines and
+// macros.
+//
+// VOID
+// TriInitializeSplayLinks (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriInitializeSplayLinks(Links) { \
+ (Links)->Refs.ParSib = MakeIntoParentRef(Links); \
+ (Links)->Refs.Child = 0; \
+ }
+
+//
+// The macro function Parent takes as input a pointer to a splay link in a
+// tree and returns a pointer to the splay link of the parent of the input
+// node. If the input node is the root of the tree the return value is
+// equal to the input value.
+//
+// PTRI_SPLAY_LINKS
+// TriParent (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriParent(Links) ( \
+ (IsParentRef((Links)->Refs.ParSib)) ? \
+ MakeIntoPointer((Links)->Refs.ParSib) \
+ : \
+ MakeIntoPointer(MakeIntoPointer((Links)->Refs.ParSib)->Refs.ParSib) \
+ )
+
+//
+// The macro function LeftChild takes as input a pointer to a splay link in
+// a tree and returns a pointer to the splay link of the left child of the
+// input node. If the left child does not exist, the return value is NULL.
+//
+// PTRI_SPLAY_LINKS
+// TriLeftChild (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriLeftChild(Links) ( \
+ (IsLeftChildRef((Links)->Refs.Child)) ? \
+ MakeIntoPointer((Links)->Refs.Child) \
+ : \
+ 0 \
+ )
+
+//
+// The macro function RightChild takes as input a pointer to a splay link
+// in a tree and returns a pointer to the splay link of the right child of
+// the input node. If the right child does not exist, the return value is
+// NULL.
+//
+// PTRI_SPLAY_LINKS
+// TriRightChild (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriRightChild(Links) ( \
+ (IsRightChildRef((Links)->Refs.Child)) ? \
+ MakeIntoPointer((Links)->Refs.Child) \
+ : ( \
+ (IsLeftChildRef((Links)->Refs.Child) && \
+ IsSiblingRef(MakeIntoPointer((Links)->Refs.Child)->Refs.ParSib)) ? \
+ MakeIntoPointer(MakeIntoPointer((Links)->Refs.Child)->Refs.ParSib) \
+ : \
+ 0 \
+ ) \
+ )
+
+//
+// The macro function IsRoot takes as input a pointer to a splay link
+// in a tree and returns TRUE if the input node is the root of the tree,
+// otherwise it returns FALSE.
+//
+// BOOLEAN
+// TriIsRoot (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriIsRoot(Links) ( \
+ (IsParentRef((Links)->Refs.ParSib) && MakeIntoPointer((Links)->Refs.ParSib) == (Links)) ? \
+ TRUE \
+ : \
+ FALSE \
+ )
+
+//
+// The macro function IsLeftChild takes as input a pointer to a splay link
+// in a tree and returns TRUE if the input node is the left child of its
+// parent, otherwise it returns FALSE. Note that if the input link is the
+// root node this function returns FALSE.
+//
+// BOOLEAN
+// TriIsLeftChild (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriIsLeftChild(Links) ( \
+ (TriLeftChild(TriParent(Links)) == (Links)) ? \
+ TRUE \
+ : \
+ FALSE \
+ )
+
+//
+// The macro function IsRightChild takes as input a pointer to a splay link
+// in a tree and returns TRUE if the input node is the right child of its
+// parent, otherwise it returns FALSE. Note that if the input link is the
+// root node this function returns FALSE.
+//
+// BOOLEAN
+// TriIsRightChild (
+// IN PTRI_SPLAY_LINKS Links
+// );
+//
+
+#define TriIsRightChild(Links) ( \
+ (TriRightChild(TriParent(Links)) == (Links)) ? \
+ TRUE \
+ : \
+ FALSE \
+ )
+
+//
+// The macro procedure InsertAsLeftChild takes as input a pointer to a splay
+// link in a tree and a pointer to a node not in a tree. It inserts the
+// second node as the left child of the first node. The first node must not
+// already have a left child, and the second node must not already have a
+// parent.
+//
+// VOID
+// TriInsertAsLeftChild (
+// IN PTRI_SPLAY_LINKS ParentLinks,
+// IN PTRI_SPLAY_LINKS ChildLinks
+// );
+//
+
+#define TriInsertAsLeftChild(ParentLinks,ChildLinks) { \
+ PTRI_SPLAY_LINKS RightChild; \
+ if ((ParentLinks)->Refs.Child == 0) { \
+ (ParentLinks)->Refs.Child = MakeIntoLeftChildRef(ChildLinks); \
+ (ChildLinks)->Refs.ParSib = MakeIntoParentRef(ParentLinks); \
+ } else { \
+ RightChild = TriRightChild(ParentLinks); \
+ (ParentLinks)->Refs.Child = MakeIntoLeftChildRef(ChildLinks); \
+ (ChildLinks)->Refs.ParSib = MakeIntoSiblingRef(RightChild); \
+ } \
+}
+
+//
+// The macro procedure InsertAsRightChild takes as input a pointer to a splay
+// link in a tree and a pointer to a node not in a tree. It inserts the
+// second node as the right child of the first node. The first node must not
+// already have a right child, and the second node must not already have a
+// parent.
+//
+// VOID
+// TriInsertAsRightChild (
+// IN PTRI_SPLAY_LINKS ParentLinks,
+// IN PTRI_SPLAY_LINKS ChildLinks
+// );
+//
+
+#define TriInsertAsRightChild(ParentLinks,ChildLinks) { \
+ PTRI_SPLAY_LINKS LeftChild; \
+ if ((ParentLinks)->Refs.Child == 0) { \
+ (ParentLinks)->Refs.Child = MakeIntoRightChildRef(ChildLinks); \
+ (ChildLinks)->Refs.ParSib = MakeIntoParentRef(ParentLinks); \
+ } else { \
+ LeftChild = TriLeftChild(ParentLinks); \
+ LeftChild->Refs.ParSib = MakeIntoSiblingRef(ChildLinks); \
+ (ChildLinks)->Refs.ParSib = MakeIntoParentRef(ParentLinks); \
+ } \
+}
+
+//
+// The Splay function takes as input a pointer to a splay link in a tree
+// and splays the tree. Its function return value is a pointer to the
+// root of the splayed tree.
+//
+
+PTRI_SPLAY_LINKS
+TriSplay (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+//
+// The Delete function takes as input a pointer to a splay link in a tree
+// and deletes that node from the tree. Its function return value is a
+// pointer to the root of the tree. If the tree is now empty, the return
+// value is NULL.
+//
+
+PTRI_SPLAY_LINKS
+TriDelete (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+//
+// The SubtreeSuccessor function takes as input a pointer to a splay link
+// in a tree and returns a pointer to the successor of the input node of
+// the substree rooted at the input node. If there is not a successor, the
+// return value is NULL.
+//
+
+PTRI_SPLAY_LINKS
+TriSubtreeSuccessor (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+//
+// The SubtreePredecessor function takes as input a pointer to a splay link
+// in a tree and returns a pointer to the predecessor of the input node of
+// the substree rooted at the input node. If there is not a predecessor,
+// the return value is NULL.
+//
+
+PTRI_SPLAY_LINKS
+TriSubtreePredecessor (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+//
+// The RealSuccessor function takes as input a pointer to a splay link
+// in a tree and returns a pointer to the successor of the input node within
+// the entire tree. If there is not a successor, the return value is NULL.
+//
+
+PTRI_SPLAY_LINKS
+TriRealSuccessor (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+//
+// The RealPredecessor function takes as input a pointer to a splay link
+// in a tree and returns a pointer to the predecessor of the input node
+// within the entire tree. If there is not a predecessor, the return value
+// is NULL.
+//
+
+PTRI_SPLAY_LINKS
+TriRealPredecessor (
+ IN PTRI_SPLAY_LINKS Links
+ );
+
+
+//
+// The remainder of this module really belong in triangle.c None of
+// the macros or routines are (logically) exported for use by the programmer
+// however they need to appear in this module to allow the earlier macros
+// to function properly.
+//
+// In the splay record (declared earlier) the low order bit of the
+// ParSib field indicates whether the link is to a Parent or a Sibling, and
+// the low order bit of the Child field is used to indicate if the link
+// is to a left child or a right child. The values are:
+//
+// A parent field has the lower bit set to 0
+// A sibling field has the lower bit set to 1
+// A left child field has the lower bit set to 0
+// A right child field has the lower bit set to 1
+//
+// The comments and code in triangle.c use the term "Ref" to indicate a
+// ParSib field or a Child field with the low order bit to indicate its type.
+// A ref cannot be directly used as a pointer. The following macros help
+// in deciding the type of a ref and making refs from pointers. There is
+// also a macro (MakeIntoPointer) that takes a ref and returns a pointer.
+//
+
+#define IsParentRef(Ulong) (((((ULONG)Ulong) & 1) == 0) && ((Ulong) != 0) ? TRUE : FALSE)
+#define MakeIntoParentRef(Ulong) (((ULONG)Ulong) & 0xfffffffc)
+
+#define IsSiblingRef(Ulong) ((((ULONG)Ulong) & 1) == 1 ? TRUE : FALSE)
+#define MakeIntoSiblingRef(Ulong) (((ULONG)Ulong) | 1)
+
+#define IsLeftChildRef(Ulong) (((((ULONG)Ulong) & 1) == 0) && ((Ulong) != 0) ? TRUE : FALSE)
+#define MakeIntoLeftChildRef(Ulong) (((ULONG)Ulong) & 0xfffffffc)
+
+#define IsRightChildRef(Ulong) ((((ULONG)Ulong) & 1) == 1 ? TRUE : FALSE)
+#define MakeIntoRightChildRef(Ulong) (((ULONG)Ulong) | 1)
+
+#define MakeIntoPointer(Ulong) ((PTRI_SPLAY_LINKS)((Ulong) & 0xfffffffc))
+
+
diff --git a/private/ntos/rtl/trtl.c b/private/ntos/rtl/trtl.c
new file mode 100644
index 000000000..930b695d4
--- /dev/null
+++ b/private/ntos/rtl/trtl.c
@@ -0,0 +1,190 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ trtl.c
+
+Abstract:
+
+ Test program for the NT OS Runtime Library (RTL)
+
+Author:
+
+ Steve Wood (stevewo) 31-Mar-1989
+
+Revision History:
+
+--*/
+
+#include <os2.h>
+#include <stdio.h>
+#include <process.h>
+#include "nt.h"
+#include "ntrtl.h"
+
+char *TestMemoryStrings[] = {
+ "",
+ "1",
+ "12",
+ "123",
+ "1234",
+ "12345",
+ "123456",
+ "1234567",
+ "12345678",
+ "123456789",
+ "123456789A",
+ NULL
+};
+
+
+BOOLEAN
+StringCompare(
+ IN PSTRING String1,
+ IN PSTRING String2,
+ IN BOOLEAN CaseInSensitive,
+ IN LONG ExpectedResult
+ )
+{
+ LONG Result = RtlCompareString( String1, String2, CaseInSensitive );
+
+ if (Result < 0) {
+ Result = -1L;
+ }
+ else {
+ if (Result > 0) {
+ Result = 1L;
+ }
+ }
+
+ if (Result != ExpectedResult) {
+ DbgPrint( "RtlCompareString( \"%.*s\", \"%.*s\", %d ) == %ld (%ld)\n",
+ String1->Length, String1->Buffer,
+ String2->Length, String2->Buffer,
+ CaseInSensitive,
+ Result, ExpectedResult
+ );
+ return( FALSE );
+ }
+ else {
+ return( TRUE );
+ }
+}
+
+BOOLEAN
+StringEqual(
+ IN PSTRING String1,
+ IN PSTRING String2,
+ IN BOOLEAN CaseInSensitive,
+ IN BOOLEAN ExpectedResult
+ )
+{
+ BOOLEAN Result = RtlEqualString( String1, String2, CaseInSensitive );
+
+ if (Result != ExpectedResult) {
+ DbgPrint( "RtlEqualString( \"%.*s\", \"%.*s\", %d ) == %d (%d)\n",
+ String1->Length, String1->Buffer,
+ String2->Length, String2->Buffer,
+ CaseInSensitive,
+ Result, ExpectedResult );
+ return( FALSE );
+ }
+ else {
+ return( TRUE );
+ }
+}
+
+VOID
+DumpString(
+ IN PCH StringTitle,
+ IN PSTRING String
+ )
+{
+ DbgPrint( "%s: (%d, %d) \"%.*s\"\n", StringTitle,
+ String->MaximumLength,
+ String->Length,
+ String->Length,
+ String->Buffer );
+}
+
+
+BOOLEAN
+TestString( void )
+{
+ BOOLEAN Result;
+ char buffer5[ 80 ], buffer6[ 15 ], buffer7[ 3 ];
+ STRING String1, String2, String3, String4;
+ STRING String5, String6, String7, String8;
+ // 1 2
+ //12345678901234567890
+ //
+ RtlInitString( &String1, " One" );
+ RtlInitString( &String2, " Two" );
+ RtlInitString( &String3, " Three" );
+ RtlInitString( &String4, " Four" );
+ String5.Buffer = buffer5;
+ String5.MaximumLength = sizeof( buffer5 );
+ String5.Length = 0;
+ String6.Buffer = buffer6;
+ String6.MaximumLength = sizeof( buffer6 );
+ String6.Length = 0;
+ String7.Buffer = buffer7;
+ String7.MaximumLength = sizeof( buffer7 );
+ String7.Length = 0;
+ String8.Buffer = NULL;
+ String8.MaximumLength = 0;
+ String8.Length = 0;
+ RtlCopyString( &String5, &String1 );
+ RtlCopyString( &String6, &String2 );
+ RtlCopyString( &String7, &String3 );
+ RtlCopyString( &String8, &String4 );
+
+ DumpString( "String1", &String1 );
+ DumpString( "String2", &String2 );
+ DumpString( "String3", &String3 );
+ DumpString( "String4", &String4 );
+ DumpString( "String5", &String5 );
+ DumpString( "String6", &String6 );
+ DumpString( "String7", &String7 );
+ DumpString( "String8", &String8 );
+
+ Result = TRUE;
+ Result &= StringCompare( &String1, &String1, FALSE, 0L );
+ Result &= StringCompare( &String1, &String2, FALSE, -1L);
+ Result &= StringCompare( &String1, &String3, FALSE, -1L);
+ Result &= StringCompare( &String1, &String4, FALSE, 1L );
+ Result &= StringCompare( &String1, &String5, FALSE, 0L );
+ Result &= StringCompare( &String1, &String6, FALSE, -1L);
+ Result &= StringCompare( &String1, &String7, FALSE, -1L);
+ Result &= StringCompare( &String1, &String8, FALSE, 1L );
+
+ Result &= StringEqual( &String1, &String1, FALSE, 1 );
+ Result &= StringEqual( &String1, &String2, FALSE, 0 );
+ Result &= StringEqual( &String1, &String3, FALSE, 0 );
+ Result &= StringEqual( &String1, &String4, FALSE, 0 );
+ Result &= StringEqual( &String1, &String5, FALSE, 1 );
+ Result &= StringEqual( &String1, &String6, FALSE, 0 );
+ Result &= StringEqual( &String1, &String7, FALSE, 0 );
+ Result &= StringEqual( &String1, &String8, FALSE, 0 );
+
+ return( Result );
+}
+
+
+int
+_CDECL
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ if (!TestString()) {
+ DbgPrint( "TRTL: TestString failed\n" );
+ exit( 1 );
+ }
+
+ exit( 0 );
+ return( 0 );
+}
diff --git a/private/ntos/rtl/tsplay.c b/private/ntos/rtl/tsplay.c
new file mode 100644
index 000000000..76a263b65
--- /dev/null
+++ b/private/ntos/rtl/tsplay.c
@@ -0,0 +1,184 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tsplay.c
+
+Abstract:
+
+ Test program for the Splay Procedures
+
+Author:
+
+ Gary Kimura [GaryKi] 24-May-1989
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+
+ULONG RtlRandom ( IN OUT PULONG Seed );
+
+typedef struct _TREE_NODE {
+ CLONG Data;
+ RTL_SPLAY_LINKS Links;
+} TREE_NODE;
+typedef TREE_NODE *PTREE_NODE;
+
+TREE_NODE Buffer[2048];
+
+PTREE_NODE
+TreeInsert (
+ IN PTREE_NODE Root,
+ IN PTREE_NODE Node
+ );
+
+VOID
+PrintTree (
+ IN PTREE_NODE Node
+ );
+
+int
+_CDECL
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ PTREE_NODE Root;
+ ULONG i;
+ ULONG Seed;
+
+ DbgPrint("Start SplayTest()\n");
+
+ Root = NULL;
+ Seed = 0;
+ for (i=0; i<2048; i++) {
+ Buffer[i].Data = RtlRandom(&Seed);
+ Buffer[i].Data = Buffer[i].Data % 512;
+ RtlInitializeSplayLinks(&Buffer[i].Links);
+ Root = TreeInsert(Root, &Buffer[i]);
+ }
+
+ PrintTree(Root);
+
+ DbgPrint("End SplayTest()\n");
+
+ return TRUE;
+
+}
+
+PTREE_NODE
+TreeInsert (
+ IN PTREE_NODE Root,
+ IN PTREE_NODE Node
+ )
+
+{
+ PRTL_SPLAY_LINKS Temp;
+
+ if (Root == NULL) {
+
+ //DbgPrint("Add as root %u\n", Node->Data);
+ return Node;
+
+ }
+
+ while (TRUE) {
+
+ if (Root->Data == Node->Data) {
+
+ //DbgPrint("Delete %u\n", Node->Data);
+
+ Temp = RtlDelete(&Root->Links);
+ if (Temp == NULL) {
+ return NULL;
+ } else {
+ return CONTAINING_RECORD(Temp, TREE_NODE, Links);
+ }
+
+ }
+
+ if (Root->Data < Node->Data) {
+
+ //
+ // Go right
+ //
+
+ if (RtlRightChild(&Root->Links) == NULL) {
+
+ //DbgPrint("Add as right child %u\n", Node->Data);
+ RtlInsertAsRightChild(&Root->Links, &Node->Links);
+ return CONTAINING_RECORD(RtlSplay(&Node->Links), TREE_NODE, Links);
+
+ } else {
+
+ Root = CONTAINING_RECORD(RtlRightChild(&Root->Links), TREE_NODE, Links);
+
+ }
+
+ } else {
+
+ //
+ // Go Left
+ //
+
+ if (RtlLeftChild(&Root->Links) == NULL) {
+
+ //DbgPrint("Add as left child %u\n", Node->Data);
+ RtlInsertAsLeftChild(&Root->Links, &Node->Links);
+ return CONTAINING_RECORD(RtlSplay(&Node->Links), TREE_NODE, Links);
+
+ } else {
+
+ Root = CONTAINING_RECORD(RtlLeftChild(&Root->Links), TREE_NODE, Links);
+
+ }
+
+ }
+ }
+}
+
+VOID
+PrintTree (
+ IN PTREE_NODE Node
+ )
+
+{
+ PRTL_SPLAY_LINKS Temp;
+ ULONG LastValue;
+
+ if (Node == NULL) {
+ return;
+ }
+
+ //
+ // find smallest value
+ //
+
+ while (RtlLeftChild(&Node->Links) != NULL) {
+ Node = CONTAINING_RECORD(RtlLeftChild(&Node->Links), TREE_NODE, Links);
+ }
+ LastValue = Node->Data;
+ //DbgPrint("%u\n", Node->Data);
+
+ //
+ // while the is a real successor we print the successor value
+ //
+
+ while ((Temp = RtlRealSuccessor(&Node->Links)) != NULL) {
+ Node = CONTAINING_RECORD(Temp, TREE_NODE, Links);
+ if (LastValue >= Node->Data) {
+ DbgPrint("TestSplay Error\n");
+ }
+ LastValue = Node->Data;
+ //DbgPrint("%u\n", Node->Data);
+ }
+
+}
diff --git a/private/ntos/rtl/ttime.c b/private/ntos/rtl/ttime.c
new file mode 100644
index 000000000..cd71ec3b1
--- /dev/null
+++ b/private/ntos/rtl/ttime.c
@@ -0,0 +1,325 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ttime.c
+
+Abstract:
+
+ Test program for the time conversion package
+
+Author:
+
+ Gary Kimura [GaryKi] 27-Aug-1989
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+
+VOID
+PrintTimeFields(
+ IN PTIME_FIELDS TimeFields
+ );
+
+LARGE_INTEGER Zero;
+LARGE_INTEGER OneSecond;
+LARGE_INTEGER OneMinute;
+LARGE_INTEGER OneHour;
+LARGE_INTEGER OneDay;
+LARGE_INTEGER OneWeek;
+LARGE_INTEGER OneNormalYear;
+LARGE_INTEGER OneLeapYear;
+LARGE_INTEGER OneCentury;
+LARGE_INTEGER TwoCenturies;
+LARGE_INTEGER ThreeCenturies;
+LARGE_INTEGER FourCenturies;
+
+LARGE_INTEGER Sum;
+
+TIME_FIELDS TimeFields;
+LARGE_INTEGER Time;
+
+LARGE_INTEGER StartOf1970;
+LARGE_INTEGER StartOf1980;
+
+int
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ ULONG i;
+
+ //
+ // We're starting the test
+ //
+
+ DbgPrint("Start Time Test\n");
+
+ //
+ // Start by initializing some constants and making sure they
+ // are correct
+ //
+
+ Zero.QuadPart = 0;
+ OneSecond.QuadPart = 10000000;
+ OneMinute.QuadPart = OneSecond.QuadPart * 60;
+ OneHour.QuadPart = OneMinute.QuadPart * 60;
+ OneDay.QuadPart = OneHour.QuadPart * 24;
+ OneWeek.QuadPart = OneDay.QuadPart * 7;
+ OneNormalYear.QuadPart = OneDay.QuadPart * 365;
+ OneLeapYear.QuadPart = OneDay.QuadPart * 366;
+ OneCentury.QuadPart = (OneNormalYear.QuadPart * 76) + (OneLeapYear.QuadPart * 24);
+ TwoCenturies.QuadPart = OneCentury.QuadPart * 2;
+ ThreeCenturies.QuadPart = OneCentury.QuadPart * 3;
+ FourCenturies.QuadPart = (OneCentury.QuadPart * 4) + OneDay.QuadPart;
+
+ Sum.QuadPart = Zero.QuadPart +
+ OneSecond.QuadPart +
+ OneMinute.QuadPart +
+ OneHour.QuadPart +
+ OneDay.QuadPart +
+ OneWeek.QuadPart +
+ OneNormalYear.QuadPart +
+ ThreeCenturies.QuadPart;
+
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&Zero, &TimeFields );
+ DbgPrint("StartOf1601 = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to Zero\n");
+ }
+ if ((Time.LowPart != Zero.LowPart) || (Time.HighPart != Zero.HighPart)) {
+ DbgPrint("****ERROR Time != Zero\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneSecond, &TimeFields );
+ DbgPrint(" + 1 Second = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneSecond\n");
+ }
+ if ((Time.LowPart != OneSecond.LowPart) || (Time.HighPart != OneSecond.HighPart)) {
+ DbgPrint("****ERROR Time != OneSecond\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneMinute, &TimeFields );
+ DbgPrint(" + 1 Minute = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneMinute\n");
+ }
+ if ((Time.LowPart != OneMinute.LowPart) || (Time.HighPart != OneMinute.HighPart)) {
+ DbgPrint("****ERROR Time != OneMinute\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneHour, &TimeFields );
+ DbgPrint(" + 1 Hour = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneHour\n");
+ }
+ if ((Time.LowPart != OneHour.LowPart) || (Time.HighPart != OneHour.HighPart)) {
+ DbgPrint("****ERROR Time != OneHour\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneDay, &TimeFields );
+ DbgPrint(" + 1 Day = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneDay\n");
+ }
+ if ((Time.LowPart != OneDay.LowPart) || (Time.HighPart != OneDay.HighPart)) {
+ DbgPrint("****ERROR Time != OneDay\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneWeek, &TimeFields );
+ DbgPrint(" + 1 Week = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneWeek\n");
+ }
+ if ((Time.LowPart != OneWeek.LowPart) || (Time.HighPart != OneWeek.HighPart)) {
+ DbgPrint("****ERROR Time != OneWeek\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneNormalYear, &TimeFields );
+ DbgPrint(" + 1 NormalYear = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneNormalYear\n");
+ }
+ if ((Time.LowPart != OneNormalYear.LowPart) || (Time.HighPart != OneNormalYear.HighPart)) {
+ DbgPrint("****ERROR Time != OneNormalYear\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneLeapYear, &TimeFields );
+ DbgPrint(" + 1 LeapYear = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneLeapYear\n");
+ }
+ if ((Time.LowPart != OneLeapYear.LowPart) || (Time.HighPart != OneLeapYear.HighPart)) {
+ DbgPrint("****ERROR Time != OneLeapYear\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&OneCentury, &TimeFields );
+ DbgPrint(" + 1 Century = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to OneCentury\n");
+ }
+ if ((Time.LowPart != OneCentury.LowPart) || (Time.HighPart != OneCentury.HighPart)) {
+ DbgPrint("****ERROR Time != OneCentury\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&TwoCenturies, &TimeFields );
+ DbgPrint(" + 2 Centuries = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to TwoCenturies\n");
+ }
+ if ((Time.LowPart != TwoCenturies.LowPart) || (Time.HighPart != TwoCenturies.HighPart)) {
+ DbgPrint("****ERROR Time != TwoCenturies\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&ThreeCenturies, &TimeFields );
+ DbgPrint(" + 3 Centuries = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to ThreeCenturies\n");
+ }
+ if ((Time.LowPart != ThreeCenturies.LowPart) || (Time.HighPart != ThreeCenturies.HighPart)) {
+ DbgPrint("****ERROR Time != ThreeCenturies\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&FourCenturies, &TimeFields );
+ DbgPrint(" + 4 Centuries = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to FourCenturies\n");
+ }
+ if ((Time.LowPart != FourCenturies.LowPart) || (Time.HighPart != FourCenturies.HighPart)) {
+ DbgPrint("****ERROR Time != FourCenturies\n");
+ }
+
+ RtlTimeToTimeFields( (PLARGE_INTEGER)&Sum, &TimeFields );
+ DbgPrint(" + sum = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to Sum\n");
+ }
+ if ((Time.LowPart != Sum.LowPart) || (Time.HighPart != Sum.HighPart)) {
+ DbgPrint("****ERROR Time != Sum\n");
+ }
+
+ DbgPrint("\n");
+
+ //
+ // Setup and test the start 1970 time
+ //
+
+ RtlSecondsSince1970ToTime( 0, &StartOf1970 );
+ RtlTimeToTimeFields( &StartOf1970, &TimeFields );
+ DbgPrint(" Start of 1970 = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to start of 1970\n");
+ }
+ if ((Time.LowPart != StartOf1970.LowPart) || (Time.HighPart != StartOf1970.HighPart)) {
+ DbgPrint("****ERROR Time != StartOf1970\n");
+ }
+ if (!RtlTimeToSecondsSince1970( &StartOf1970, &i )) {
+ DbgPrint("****ERROR converting time to seconds since 1970\n");
+ }
+ if (i != 0) {
+ DbgPrint("****ERROR seconds since 1970 != 0\n");
+ }
+
+ //
+ // Setup and test the start 1980 time
+ //
+
+ RtlSecondsSince1980ToTime( 0, &StartOf1980 );
+ RtlTimeToTimeFields( &StartOf1980, &TimeFields );
+ DbgPrint(" Start of 1980 = "); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields back to start of 1980\n");
+ }
+ if ((Time.LowPart != StartOf1980.LowPart) || (Time.HighPart != StartOf1980.HighPart)) {
+ DbgPrint("****ERROR Time != StartOf1980\n");
+ }
+ if (!RtlTimeToSecondsSince1980( &StartOf1980, &i )) {
+ DbgPrint("****ERROR converting time to seconds since 1980\n");
+ }
+ if (i != 0) {
+ DbgPrint("****ERROR seconds since 1980 != 0\n");
+ }
+
+ //
+ // Lets try to print the Christmas when Santa arrives for 1901 to 2001
+ // every 10 years
+ //
+
+ TimeFields.Month = 12;
+ TimeFields.Day = 25;
+ TimeFields.Hour = 3;
+ TimeFields.Minute = 30;
+ TimeFields.Second = 15;
+ TimeFields.Milliseconds = 250;
+
+ for (i = 1901; i < 2002; i += 10) {
+
+ TimeFields.Year = i;
+
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields to Christmas %4d\n", TimeFields.Year);
+ }
+ RtlTimeToTimeFields( &Time, &TimeFields );
+ DbgPrint(" Christmas %4d = ", i); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+
+ }
+
+ //
+ // Let's see how old I really am, when I turn 10, 20, 30, ...
+ //
+
+ TimeFields.Month = 12;
+ TimeFields.Day = 5;
+ TimeFields.Hour = 3;
+ TimeFields.Minute = 14;
+ TimeFields.Second = 0;
+ TimeFields.Milliseconds = 0;
+
+ for (i = 1956; i <= 1956+60; i += 10) {
+
+ TimeFields.Year = i;
+
+ if (!RtlTimeFieldsToTime( &TimeFields, &Time )) {
+ DbgPrint("****ERROR converting TimeFields to DOB %4d\n", TimeFields.Year);
+ }
+ RtlTimeToTimeFields( &Time, &TimeFields );
+ DbgPrint(" DOB + %4d = ", i-1956); PrintTimeFields( &TimeFields ); DbgPrint("\n");
+
+ }
+
+ DbgPrint("End Time Test\n");
+
+ return TRUE;
+}
+
+VOID
+PrintTimeFields (
+ IN PTIME_FIELDS TimeFields
+ )
+{
+ static PCHAR Months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+ static PCHAR Days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+ DbgPrint(" %d", TimeFields->Year);
+ DbgPrint("-%s", Months[TimeFields->Month-1]);
+ DbgPrint("-%d", TimeFields->Day);
+
+ DbgPrint(" %2d", TimeFields->Hour);
+ DbgPrint(":%2d", TimeFields->Minute);
+ DbgPrint(":%2d", TimeFields->Second);
+ DbgPrint(".%3d", TimeFields->Milliseconds);
+
+ DbgPrint(" (%s)", Days[TimeFields->Weekday]);
+}
diff --git a/private/ntos/rtl/ttri.c b/private/ntos/rtl/ttri.c
new file mode 100644
index 000000000..4f1de6eed
--- /dev/null
+++ b/private/ntos/rtl/ttri.c
@@ -0,0 +1,187 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tsplay.c
+
+Abstract:
+
+ Test program for the Splay Procedures
+
+Author:
+
+ Gary Kimura [GaryKi] 24-May-1989
+
+Revision History:
+
+--*/
+
+#define DbgPrint DbgPrint
+
+#include <stdio.h>
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "triangle.h"
+
+ULONG RtlRandom (IN OUT PULONG Seed);
+
+typedef struct _TREE_NODE {
+ CLONG Data;
+ TRI_SPLAY_LINKS Links;
+} TREE_NODE;
+typedef TREE_NODE *PTREE_NODE;
+
+TREE_NODE Buffer[2048];
+
+PTREE_NODE
+TreeInsert (
+ IN PTREE_NODE Root,
+ IN PTREE_NODE Node
+ );
+
+VOID
+PrintTree (
+ IN PTREE_NODE Node
+ );
+
+int
+_CDECL
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ PTREE_NODE Root;
+ ULONG i;
+ ULONG Seed;
+
+ DbgPrint("Start TriangleTest()\n");
+
+ Root = NULL;
+ Seed = 0;
+ for (i=0; i<2048; i++) {
+ Buffer[i].Data = RtlRandom(&Seed);
+ Buffer[i].Data = Buffer[i].Data % 512;
+ TriInitializeSplayLinks(&Buffer[i].Links);
+ Root = TreeInsert(Root, &Buffer[i]);
+ }
+
+ PrintTree(Root);
+
+ DbgPrint("End TriangleTest()\n");
+
+ return TRUE;
+
+}
+
+PTREE_NODE
+TreeInsert (
+ IN PTREE_NODE Root,
+ IN PTREE_NODE Node
+ )
+
+{
+ PTRI_SPLAY_LINKS Temp;
+
+ if (Root == NULL) {
+
+ //DbgPrint("Add as root %u\n", Node->Data);
+ return Node;
+
+ }
+
+ while (TRUE) {
+
+ if (Root->Data == Node->Data) {
+
+ //DbgPrint("Delete %u\n", Node->Data);
+
+ Temp = TriDelete(&Root->Links);
+ if (Temp == NULL) {
+ return NULL;
+ } else {
+ return CONTAINING_RECORD(Temp, TREE_NODE, Links);
+ }
+
+ }
+
+ if (Root->Data < Node->Data) {
+
+ //
+ // Go right
+ //
+
+ if (TriRightChild(&Root->Links) == NULL) {
+
+ //DbgPrint("Add as right child %u\n", Node->Data);
+ TriInsertAsRightChild(&Root->Links, &Node->Links);
+ return CONTAINING_RECORD(TriSplay(&Node->Links), TREE_NODE, Links);
+
+ } else {
+
+ Root = CONTAINING_RECORD(TriRightChild(&Root->Links), TREE_NODE, Links);
+
+ }
+
+ } else {
+
+ //
+ // Go Left
+ //
+
+ if (TriLeftChild(&Root->Links) == NULL) {
+
+ //DbgPrint("Add as left child %u\n", Node->Data);
+ TriInsertAsLeftChild(&Root->Links, &Node->Links);
+ return CONTAINING_RECORD(TriSplay(&Node->Links), TREE_NODE, Links);
+
+ } else {
+
+ Root = CONTAINING_RECORD(TriLeftChild(&Root->Links), TREE_NODE, Links);
+
+ }
+
+ }
+ }
+}
+
+VOID
+PrintTree (
+ IN PTREE_NODE Node
+ )
+
+{
+ PTRI_SPLAY_LINKS Temp;
+ ULONG LastValue;
+
+ if (Node == NULL) {
+ return;
+ }
+
+ //
+ // find smallest value
+ //
+
+ while (TriLeftChild(&Node->Links) != NULL) {
+ Node = CONTAINING_RECORD(TriLeftChild(&Node->Links), TREE_NODE, Links);
+ }
+ LastValue = Node->Data;
+ //DbgPrint("%u\n", Node->Data);
+
+ //
+ // while the is a real successor we print the successor value
+ //
+
+ while ((Temp = TriRealSuccessor(&Node->Links)) != NULL) {
+ Node = CONTAINING_RECORD(Temp, TREE_NODE, Links);
+ if (LastValue >= Node->Data) {
+ DbgPrint("TestSplay Error\n");
+ }
+ LastValue = Node->Data;
+ //DbgPrint("%u\n", Node->Data);
+ }
+
+}
diff --git a/private/ntos/rtl/ucli.c b/private/ntos/rtl/ucli.c
new file mode 100644
index 000000000..8b859b717
--- /dev/null
+++ b/private/ntos/rtl/ucli.c
@@ -0,0 +1,51 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ ucli.c
+
+Abstract:
+
+ Test program for the NT OS User Mode Runtime Library (URTL)
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntsm.h>
+
+#include <string.h>
+
+NTSTATUS
+main(
+ IN int argc,
+ IN char *argv[],
+ IN char *envp[],
+ IN ULONG DebugParameter OPTIONAL
+ )
+{
+ PCH InitialCommandLine = NULL;
+ CHAR Buffer[ 256 ];
+
+ if (argc-- > 1) {
+ InitialCommandLine = Buffer;
+ *Buffer = '\0';
+ while (argc--) {
+ strcat( Buffer, *++argv );
+ strcat( Buffer, " " );
+ }
+ }
+
+ RtlCommandLineInterpreter( "UCLI> ", envp, InitialCommandLine );
+
+ return( STATUS_SUCCESS );
+}
diff --git a/private/ntos/rtl/uexec.c b/private/ntos/rtl/uexec.c
new file mode 100644
index 000000000..71fe7a684
--- /dev/null
+++ b/private/ntos/rtl/uexec.c
@@ -0,0 +1,140 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ uexec.c
+
+Abstract:
+
+ Test program for the NT OS User Mode Runtime Library (URTL)
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <string.h>
+
+PVOID MyHeap = NULL;
+
+NTSTATUS
+main(
+ IN ULONG argc,
+ IN PCH argv[],
+ IN PCH envp[],
+ IN ULONG DebugParameter OPTIONAL
+ )
+{
+ NTSTATUS Status;
+ STRING ImagePathName;
+ PCHAR PathVariable, *pp;
+ CHAR ImageNameBuffer[ 128 ];
+ RTL_USER_PROCESS_INFORMATION ProcessInformation;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+ ULONG i, CountBytes, envc, Bogus;
+ PSTRING DstString;
+ PCH Src, Dst;
+ PCH Parameters[ RTL_USER_PROC_PARAMS_DEBUGFLAG+2 ];
+
+ Parameters[ RTL_USER_PROC_PARAMS_IMAGEFILE ] =
+ "Full Path Specification of Image File goes here";
+
+ Parameters[ RTL_USER_PROC_PARAMS_CMDLINE ] =
+ "Complete Command Line goes here";
+
+ Parameters[ RTL_USER_PROC_PARAMS_DEBUGFLAG ] =
+ "Debugging String goes here";
+
+ Parameters[ RTL_USER_PROC_PARAMS_DEBUGFLAG+1 ] = NULL;
+
+ MyHeap = RtlProcessHeap();
+
+#if DBG
+ DbgPrint( "Entering UEXEC User Mode Test Program\n" );
+ DbgPrint( "argc = %ld\n", argc );
+ for (i=0; i<=argc; i++) {
+ DbgPrint( "argv[ %ld ]: %s\n",
+ i,
+ argv[ i ] ? argv[ i ] : "<NULL>"
+ );
+ }
+ DbgPrint( "\n" );
+ for (i=0; envp[i]; i++) {
+ DbgPrint( "envp[ %ld ]: %s\n", i, envp[ i ] );
+ }
+
+#endif
+
+ PathVariable = "\\SystemRoot";
+ if (envp != NULL) {
+ pp = envp;
+ while (Src = *pp++) {
+ if (!_strnicmp( Src, "PATH=", 5 )) {
+ PathVariable = Src+5;
+ break;
+ }
+ }
+ }
+
+ DbgPrint( "PATH=%s\n", PathVariable );
+
+ ProcessParameters = (PRTL_USER_PROCESS_PARAMETERS)
+ RtlAllocateHeap( MyHeap, 0, 2048 );
+ ProcessParameters->MaximumLength = 2048;
+
+ argv[ argc ] = NULL;
+ Status = RtlVectorsToProcessParameters(
+ argv,
+ envp,
+ Parameters,
+ ProcessParameters
+ );
+
+ ImagePathName.Buffer = ImageNameBuffer;
+ ImagePathName.Length = 0;
+ ImagePathName.MaximumLength = sizeof( ImageNameBuffer );
+ if (RtlSearchPath( PathVariable, "uexec1.exe", NULL, &ImagePathName )) {
+ Status = RtlCreateUserProcess( &ImagePathName,
+ NULL,
+ NULL,
+ NULL,
+ TRUE,
+ NULL,
+ NULL,
+ ProcessParameters,
+ &ProcessInformation,
+ NULL
+ );
+ if (NT_SUCCESS( Status )) {
+ Status = NtResumeThread( ProcessInformation.Thread, &Bogus );
+ if (NT_SUCCESS( Status )) {
+#if DBG
+ DbgPrint( "UEXEC waiting for UEXEC1...\n" );
+#endif
+ Status = NtWaitForSingleObject( ProcessInformation.Process,
+ TRUE,
+ NULL
+ );
+ }
+ }
+ }
+ else {
+ DbgPrint( "UEXEC1.EXE not found in %s\n", PathVariable );
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+#if DBG
+ DbgPrint( "Leaving UEXEC User Mode Test Program\n" );
+#endif
+
+ return( Status );
+}
diff --git a/private/ntos/rtl/uexec1.c b/private/ntos/rtl/uexec1.c
new file mode 100644
index 000000000..a93d87cb5
--- /dev/null
+++ b/private/ntos/rtl/uexec1.c
@@ -0,0 +1,50 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ tUEXEC1.c
+
+Abstract:
+
+ Sub-Test program for the NT OS User Mode Runtime Library (URTL)
+
+Author:
+
+ Steve Wood (stevewo) 18-Aug-1989
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+NTSTATUS
+main(
+ int argc,
+ char *argv[],
+ char *envp[]
+ )
+{
+ ULONG i;
+
+ DbgPrint( "Entering UEXEC1 User Mode Test Program\n" );
+ DbgPrint( "argc = %ld\n", argc );
+ for (i=0; i<=argc; i++) {
+ DbgPrint( "argv[ %ld ]: %s\n",
+ i,
+ argv[ i ] ? argv[ i ] : "<NULL>"
+ );
+ }
+ DbgPrint( "\n" );
+ for (i=0; envp[i]; i++) {
+ DbgPrint( "envp[ %ld ]: %s\n", i, envp[ i ] );
+ }
+
+ DbgPrint( "Leaving UEXEC1 User Mode Test Program\n" );
+
+ return( STATUS_SUCCESS );
+}
diff --git a/private/ntos/rtl/uexec2.c b/private/ntos/rtl/uexec2.c
new file mode 100644
index 000000000..8174d71b6
--- /dev/null
+++ b/private/ntos/rtl/uexec2.c
@@ -0,0 +1,258 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ uexec.c
+
+Abstract:
+
+ Test program for the NT OS User Mode Runtime Library (URTL)
+
+Author:
+
+ Mark Lucovsyt (markl) 14-Jun-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+NTSTATUS
+main(
+ IN ULONG argc,
+ IN PCH argv[],
+ IN PCH envp[],
+ IN ULONG DebugParameter OPTIONAL
+ )
+{
+ NTSTATUS st;
+ STRING ImagePathName;
+ UNICODE_STRING ConfigFilePathname;
+ RTL_USER_PROCESS_INFORMATION ProcessInformation;
+ PEB_SM_DATA PebSessionInformation;
+ HANDLE FileHandle;
+ ULONG FileIndexNumber;
+ IO_STATUS_BLOCK IoStatus;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ FILE_INTERNAL_INFORMATION FileInformation;
+ PPEB Peb;
+
+ Peb = NtCurrentPeb();
+ RtlZeroMemory(&PebSessionInformation,sizeof(PebSessionInformation));
+
+ //
+ // If we started from cli then do all this work to
+ // pass thru stdin
+ //
+
+ if ( !Peb->Sm.StandardInput.FileHandle ) {
+
+ RtlInitUnicodeString(&ConfigFilePathname,L"\\SystemRoot\\nt.cfg");
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &ConfigFilePathname,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ st = NtOpenFile(
+ &FileHandle,
+ SYNCHRONIZE | FILE_READ_DATA,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ 0
+ );
+
+ if (!NT_SUCCESS( st )) {
+ DbgPrint("NtOpenFile: %wZ failed 0x%lx\n",&ConfigFilePathname,st);
+ ASSERT(NT_SUCCESS(st));
+ }
+
+ //
+ // get the file serial number
+ //
+
+ st = NtQueryInformationFile(
+ FileHandle,
+ &IoStatus,
+ (PVOID) &FileInformation,
+ sizeof(FileInformation),
+ FileInternalInformation
+ );
+
+ if (!NT_SUCCESS( st )) {
+ DbgPrint("NtQueryInformationFile: %wZ failed 0x%lx\n",&ConfigFilePathname,st);
+ ASSERT(NT_SUCCESS(st));
+ }
+
+ PebSessionInformation.Length = sizeof(PebSessionInformation);
+ PebSessionInformation.StandardInput.FileHandle = FileHandle;
+ PebSessionInformation.StandardInput.Context = (PVOID) FileInformation.IndexNumber;
+
+ RtlInitString(&ImagePathName,"\\A:\\uexec2.exe");
+
+ st = RtlCreateUserProcess(
+ &ImagePathName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ NULL,
+ NULL,
+ NULL,
+ &ProcessInformation,
+ &PebSessionInformation
+ );
+
+ ASSERT(NT_SUCCESS(st));
+
+ NtResumeThread(ProcessInformation.Thread,NULL);
+ NtWaitForSingleObject(ProcessInformation.Process,FALSE,NULL);
+ NtClose(ProcessInformation.Process);
+ NtClose(ProcessInformation.Thread);
+ NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
+
+ } else {
+
+ if ( !Peb->Sm.StandardOutput.FileHandle ) {
+
+ //
+ // Started from this program. Stdin is inherited
+ //
+
+ st = NtQueryInformationFile(
+ Peb->Sm.StandardInput.FileHandle,
+ &IoStatus,
+ (PVOID) &FileInformation,
+ sizeof(FileInformation),
+ FileInternalInformation
+ );
+
+ if (!NT_SUCCESS( st )) {
+ DbgPrint("NtQueryInformationFile: failed 0x%lx\n",st);
+ ASSERT(NT_SUCCESS(st));
+ }
+
+ ASSERT(Peb->Sm.StandardInput.Context == (PVOID) FileInformation.IndexNumber);
+
+ PebSessionInformation.Length = sizeof(PebSessionInformation);
+ PebSessionInformation.StandardInput.FileHandle = (HANDLE)PEB_STDIO_HANDLE_SUBSYS;
+ PebSessionInformation.StandardOutput.FileHandle = Peb->Sm.StandardInput.FileHandle;
+ PebSessionInformation.StandardOutput.Context = (PVOID) FileInformation.IndexNumber;
+
+ RtlInitString(&ImagePathName,"\\A:\\uexec2.exe");
+
+ st = RtlCreateUserProcess(
+ &ImagePathName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ NULL,
+ NULL,
+ NULL,
+ &ProcessInformation,
+ &PebSessionInformation
+ );
+
+ ASSERT(NT_SUCCESS(st));
+
+ NtResumeThread(ProcessInformation.Thread,NULL);
+ NtWaitForSingleObject(ProcessInformation.Process,FALSE,NULL);
+ NtClose(ProcessInformation.Process);
+ NtClose(ProcessInformation.Thread);
+ NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
+
+ } else {
+
+ ASSERT(Peb->Sm.StandardInput.FileHandle == (HANDLE)PEB_STDIO_HANDLE_SUBSYS);
+
+ if ( !Peb->Sm.StandardError.FileHandle ) {
+
+ //
+ // Started by this program with StandardOutput Inherited
+ //
+
+ st = NtQueryInformationFile(
+ Peb->Sm.StandardOutput.FileHandle,
+ &IoStatus,
+ (PVOID) &FileInformation,
+ sizeof(FileInformation),
+ FileInternalInformation
+ );
+
+ if (!NT_SUCCESS( st )) {
+ DbgPrint("NtQueryInformationFile: failed 0x%lx\n",st);
+ ASSERT(NT_SUCCESS(st));
+ }
+
+ ASSERT(Peb->Sm.StandardOutput.Context == (PVOID) FileInformation.IndexNumber);
+
+ PebSessionInformation.Length = sizeof(PebSessionInformation);
+ PebSessionInformation.StandardInput.FileHandle = (HANDLE)PEB_STDIO_HANDLE_SUBSYS;
+ PebSessionInformation.StandardOutput.FileHandle = (HANDLE)PEB_STDIO_HANDLE_PM;
+ PebSessionInformation.StandardError.FileHandle = Peb->Sm.StandardOutput.FileHandle;
+ PebSessionInformation.StandardError.Context = (PVOID) FileInformation.IndexNumber;
+
+ RtlInitString(&ImagePathName,"\\A:\\uexec2.exe");
+
+ st = RtlCreateUserProcess(
+ &ImagePathName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ NULL,
+ NULL,
+ NULL,
+ &ProcessInformation,
+ &PebSessionInformation
+ );
+
+ ASSERT(NT_SUCCESS(st));
+
+ NtResumeThread(ProcessInformation.Thread,NULL);
+ NtWaitForSingleObject(ProcessInformation.Process,FALSE,NULL);
+ NtClose(ProcessInformation.Process);
+ NtClose(ProcessInformation.Thread);
+ NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
+
+ } else {
+
+ ASSERT(Peb->Sm.StandardOutput.FileHandle == (HANDLE)PEB_STDIO_HANDLE_PM);
+
+ //
+ // Started by this program with StandardError Inherited
+ //
+
+ st = NtQueryInformationFile(
+ Peb->Sm.StandardError.FileHandle,
+ &IoStatus,
+ (PVOID) &FileInformation,
+ sizeof(FileInformation),
+ FileInternalInformation
+ );
+
+ if (!NT_SUCCESS( st )) {
+ DbgPrint("NtQueryInformationFile: failed 0x%lx\n",st);
+ ASSERT(NT_SUCCESS(st));
+ }
+
+ ASSERT(Peb->Sm.StandardError.Context == (PVOID) FileInformation.IndexNumber);
+ NtTerminateProcess(NtCurrentProcess(),STATUS_SUCCESS);
+ }
+ }
+ }
+}
diff --git a/private/ntos/rtl/up/alpha/sources b/private/ntos/rtl/up/alpha/sources
new file mode 100644
index 000000000..c7016ebc1
--- /dev/null
+++ b/private/ntos/rtl/up/alpha/sources
@@ -0,0 +1,14 @@
+ALPHA_SOURCES=..\alpha\capture.s \
+ ..\alpha\context.c \
+ ..\alpha\debugstb.s \
+ ..\alpha\exdsptch.c \
+ ..\alpha\getcalr.c \
+ ..\alpha\largeint.s \
+ ..\alpha\lzntaxp.s \
+ ..\alpha\ntcurteb.s \
+ ..\alpha\mvmem.s \
+ ..\alpha\trampoln.s \
+ ..\alpha\unwindr.c \
+ ..\alpha\chandler.c \
+ ..\alpha\ghandler.c \
+ ..\alpha\xcptmisc.s
diff --git a/private/ntos/rtl/up/i386/sources b/private/ntos/rtl/up/i386/sources
new file mode 100644
index 000000000..a609b20e6
--- /dev/null
+++ b/private/ntos/rtl/up/i386/sources
@@ -0,0 +1,17 @@
+i386_SOURCES=..\i386\context.c \
+ ..\i386\debug3.c \
+ ..\i386\debug2.asm \
+ ..\i386\divlarge.c \
+ ..\i386\exdsptch.c \
+ ..\i386\stkwalk.asm \
+ ..\i386\stringsp.asm \
+ ..\i386\ioaccess.asm \
+ ..\i386\largeint.asm \
+ ..\i386\lzntx86.asm \
+ ..\i386\movemem.asm \
+ ..\i386\ntcurteb.asm \
+ ..\i386\raise.asm \
+ ..\i386\raisests.c \
+ ..\i386\rtldump.c \
+ ..\i386\xcptmisc.asm \
+ ..\i386\halvprnt.c
diff --git a/private/ntos/rtl/up/makefile b/private/ntos/rtl/up/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/rtl/up/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/rtl/up/makefile.inc b/private/ntos/rtl/up/makefile.inc
new file mode 100644
index 000000000..f64d258e3
--- /dev/null
+++ b/private/ntos/rtl/up/makefile.inc
@@ -0,0 +1 @@
+..\error.c: ..\error.h
diff --git a/private/ntos/rtl/up/mips/sources b/private/ntos/rtl/up/mips/sources
new file mode 100644
index 000000000..c78a3968b
--- /dev/null
+++ b/private/ntos/rtl/up/mips/sources
@@ -0,0 +1,11 @@
+MIPS_SOURCES=..\mips\chandler.c \
+ ..\mips\context.c \
+ ..\mips\debugstb.s \
+ ..\mips\exdsptch.c \
+ ..\mips\getcalr.c \
+ ..\mips\largeint.s \
+ ..\mips\lzntmips.s \
+ ..\mips\trampoln.s \
+ ..\mips\xcptmisc.s \
+ ..\mips\xxcaptur.s \
+ ..\mips\xxmvmem.s
diff --git a/private/ntos/rtl/up/ppc/sources b/private/ntos/rtl/up/ppc/sources
new file mode 100644
index 000000000..933e58555
--- /dev/null
+++ b/private/ntos/rtl/up/ppc/sources
@@ -0,0 +1,10 @@
+PPC_SOURCES=..\ppc\chandler.c \
+ ..\ppc\context.c \
+ ..\ppc\debugstb.s \
+ ..\ppc\exdsptch.c \
+ ..\ppc\getcalr.c \
+ ..\ppc\largeint.s \
+ ..\ppc\lzntppc.s \
+ ..\ppc\movemem.s \
+ ..\ppc\trampoln.s \
+ ..\ppc\xcptmisc.s
diff --git a/private/ntos/rtl/up/sources b/private/ntos/rtl/up/sources
new file mode 100644
index 000000000..43f689833
--- /dev/null
+++ b/private/ntos/rtl/up/sources
@@ -0,0 +1,83 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=krtl
+
+TARGETNAME=ntosrtl
+TARGETPATH=..\..\obj
+TARGETTYPE=LIBRARY
+GPSIZE=32
+
+INCLUDES=..;..\..\inc;..\..\..\inc
+
+NTPROFILEINPUT=yes
+
+C_DEFINES=$(C_DEFINES) -D_NTSYSTEM_ -DNTOS_KERNEL_RUNTIME=1
+
+ASM_DEFINES=-DNTOS_KERNEL_RUNTIME=1
+
+SOURCES=..\acledit.c \
+ ..\assert.c \
+ ..\atom.c \
+ ..\bitmap.c \
+ ..\cnvint.c \
+ ..\compress.c \
+ ..\debug.c \
+ ..\eballoc.c \
+ ..\environ.c \
+ ..\error.c \
+ ..\excptdbg.c \
+ ..\gentable.c \
+ ..\gen8dot3.c \
+ ..\heap.c \
+ ..\imagedir.c \
+ ..\checksum.c \
+ ..\ldrrsrc.c \
+ ..\ldrreloc.c \
+ ..\lznt1.c \
+ ..\message.c \
+ ..\nls.c \
+ ..\pctohdr.c \
+ ..\prefix.c \
+ ..\prodtype.c \
+ ..\random.c \
+ ..\registry.c \
+ ..\regutil.c \
+ ..\rtlassig.c \
+ ..\rtldata.c \
+ ..\rtlexec.c \
+ ..\rxact.c \
+ ..\sertl.c \
+ ..\splay.c \
+ ..\string.c \
+ ..\stktrace.c \
+ ..\time.c \
+ ..\nlsxlat.c \
+ ..\eventlog.c \
+ ..\trace.c
+
+UMTYPE=console
+
+NTTARGETFILE0=..\error.c
diff --git a/private/ntos/rtl/urtl.c b/private/ntos/rtl/urtl.c
new file mode 100644
index 000000000..785955110
--- /dev/null
+++ b/private/ntos/rtl/urtl.c
@@ -0,0 +1,180 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ urtl.c
+
+Abstract:
+
+ Usermode test program for rtl
+
+Author:
+
+ Mark Lucovsky (markl) 22-Aug-1989
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+PVOID MyHeap = NULL;
+
+DumpIt(
+ IN PRTL_USER_PROCESS_PARAMETERS ArgBase
+ )
+{
+ ULONG Base;
+ PSTRING Vector;
+ PCH *ParmVector;
+ ULONG i;
+
+ (VOID) RtlNormalizeProcessParameters( ArgBase );
+ (VOID) RtlDeNormalizeProcessParameters( ArgBase );
+
+ Base = (ULONG) ArgBase;
+
+ DbgPrint("DumpIt: ArgBase %lx\n",ArgBase);
+ DbgPrint("DumpIt: MaximumLength %lx\n",ArgBase->MaximumLength);
+ DbgPrint("DumpIt: Length %lx\n",ArgBase->Length);
+ DbgPrint("DumpIt: ArgumentCount %lx\n",ArgBase->ArgumentCount);
+ DbgPrint("DumpIt: Arguments %lx\n",ArgBase->Arguments );
+ DbgPrint("DumpIt: VariableCount %lx\n",ArgBase->VariableCount);
+ DbgPrint("DumpIt: Variables %lx\n",ArgBase->Variables );
+ DbgPrint("DumpIt: ParameterCount%lx\n",ArgBase->ParameterCount);
+ DbgPrint("DumpIt: Parameters %lx\n",ArgBase->Parameters );
+
+ if ( ArgBase->ArgumentCount ) {
+ Vector = (PSTRING)((PCH)ArgBase->Arguments + Base);
+ i = ArgBase->ArgumentCount;
+ while(i--){
+ DbgPrint("DumpIt: Argument %s\n",Vector->Buffer + Base);
+ Vector++;
+ }
+ }
+
+ if ( ArgBase->VariableCount ) {
+ Vector = (PSTRING)((PCH)ArgBase->Variables + Base);
+ i = ArgBase->VariableCount;
+ while(i--){
+ DbgPrint("DumpIt: Variable %s\n",Vector->Buffer + Base);
+ Vector++;
+ }
+ }
+
+ if ( ArgBase->ParameterCount ) {
+ ParmVector = (PCH *)((PCH)ArgBase->Parameters + Base);
+ i = ArgBase->ParameterCount;
+ while(i--) {
+ DbgPrint("DumpIt: Parameter %s\n",*ParmVector + Base);
+ ParmVector++;
+ }
+ }
+}
+
+BOOLEAN
+VectorTest(
+ IN PCH Arguments[],
+ IN PCH Variables[],
+ IN PCH Parameters[]
+ )
+{
+
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+ NTSTATUS st;
+
+ DbgPrint("VectorTest:++\n");
+
+ ProcessParameters = RtlAllocateHeap(MyHeap, 0, 2048);
+ ProcessParameters->MaximumLength = 2048;
+
+ st = RtlVectorsToProcessParameters(
+ Arguments,
+ Variables,
+ Parameters,
+ ProcessParameters
+ );
+
+ DumpIt(ProcessParameters);
+
+ DbgPrint("VectorTest:--\n");
+
+ return TRUE;
+}
+
+NTSTATUS
+main(
+ IN ULONG argc,
+ IN PCH argv[],
+ IN PCH envp[],
+ IN ULONG DebugParameter OPTIONAL
+ )
+
+{
+ ULONG i;
+ char c, *s;
+ PCH *Arguments;
+ PCH *Variables;
+ PCH Parameters[ RTL_USER_PROC_PARAMS_DEBUGFLAG+2 ];
+
+ ULONG TestVector = 0;
+
+ Arguments = argv;
+ Variables = envp;
+ Parameters[ RTL_USER_PROC_PARAMS_IMAGEFILE ] =
+ "Full Path Specification of Image File goes here";
+
+ Parameters[ RTL_USER_PROC_PARAMS_CMDLINE ] =
+ "Complete Command Line goes here";
+
+ Parameters[ RTL_USER_PROC_PARAMS_DEBUGFLAG ] =
+ "Debugging String goes here";
+
+ Parameters[ RTL_USER_PROC_PARAMS_DEBUGFLAG+1 ] = NULL;
+
+ MyHeap = RtlProcessHeap();
+
+
+#if DBG
+ DbgPrint( "Entering URTL User Mode Test Program\n" );
+ DbgPrint( "argc = %ld\n", argc );
+ for (i=0; i<=argc; i++) {
+ DbgPrint( "argv[ %ld ]: %s\n",
+ i,
+ argv[ i ] ? argv[ i ] : "<NULL>"
+ );
+ }
+ DbgPrint( "\n" );
+ for (i=0; envp[i]; i++) {
+ DbgPrint( "envp[ %ld ]: %s\n", i, envp[ i ] );
+ }
+#endif
+ i = 1;
+ if (argc > 1 ) {
+ while (--argc) {
+ s = *++argv;
+ while ((c = *s++) != '\0') {
+ switch (c) {
+
+ case 'V':
+ case 'v':
+ TestVector = i++;
+ break;
+ default:
+ DbgPrint( "urtl: invalid test code - '%s'", *argv );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( TestVector ) {
+ VectorTest(Arguments,Variables,Parameters);
+ }
+
+ return( STATUS_SUCCESS );
+}
diff --git a/private/ntos/rtl/user/alpha/sources b/private/ntos/rtl/user/alpha/sources
new file mode 100644
index 000000000..c0f256a76
--- /dev/null
+++ b/private/ntos/rtl/user/alpha/sources
@@ -0,0 +1,15 @@
+ALPHA_SOURCES=..\alpha\capture.s \
+ ..\alpha\chkstk.s \
+ ..\alpha\context.c \
+ ..\alpha\debugstb.s \
+ ..\alpha\exdsptch.c \
+ ..\alpha\getcalr.c \
+ ..\alpha\largeint.s \
+ ..\alpha\lzntaxp.s \
+ ..\alpha\longjmp.s \
+ ..\alpha\ntcurteb.s \
+ ..\alpha\mvmem.s \
+ ..\alpha\setjmp.s \
+ ..\alpha\trampoln.s \
+ ..\alpha\unwindr.c \
+ ..\alpha\xcptmisc.s
diff --git a/private/ntos/rtl/user/i386/sources b/private/ntos/rtl/user/i386/sources
new file mode 100644
index 000000000..5a4cd3730
--- /dev/null
+++ b/private/ntos/rtl/user/i386/sources
@@ -0,0 +1,19 @@
+i386_SOURCES=..\i386\context.c \
+ ..\i386\debug3.c \
+ ..\i386\debug2.asm \
+ ..\i386\divlarge.c \
+ ..\i386\exdsptch.c \
+ ..\i386\stkwalk.asm \
+ ..\i386\stringsp.asm \
+ ..\i386\ioaccess.asm \
+ ..\i386\largeint.asm \
+ ..\i386\lzntx86.asm \
+ ..\i386\movemem.asm \
+ ..\i386\ntcurteb.asm \
+ ..\i386\raise.asm \
+ ..\i386\raisests.c \
+ ..\i386\rtldump.c \
+ ..\i386\stkwalk.asm \
+ ..\i386\userdisp.asm \
+ ..\i386\xcptmisc.asm \
+ ..\i386\halvprnt.c
diff --git a/private/ntos/rtl/user/makefile b/private/ntos/rtl/user/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/ntos/rtl/user/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/rtl/user/makefile.inc b/private/ntos/rtl/user/makefile.inc
new file mode 100644
index 000000000..281b22c0b
--- /dev/null
+++ b/private/ntos/rtl/user/makefile.inc
@@ -0,0 +1,25 @@
+theap.c: ..\heap.c ..\heapdbg.c ..\heapdll.c ..\trace.c
+
+t.c: ..\handle.c ..\atom.c
+
+..\error.c: ..\error.h
+
+..\error.h: ..\generr.c
+ set PASS0ONLY=
+ nmake obj\$(TARGET_DIRECTORY)\generr.obj USE_CRTDLL=1
+ -link -out:obj\$(TARGET_DIRECTORY)\generr.exe @<<
+-machine:$(TARGET_DIRECTORY)
+-base:@$(BASEDIR)\PUBLIC\SDK\LIB\coffbase.txt,usermode
+-subsystem:console
+-entry:mainCRTStartup
+-ignore:4001,4037,4039,4065,4070,4078,4087,4089,2001
+-force
+-nodefaultlib
+obj\$(TARGET_DIRECTORY)\generr.obj
+$(BASEDIR)\PUBLIC\SDK\LIB\$(TARGET_DIRECTORY)\crtdll.lib
+<<
+ -obj\$(TARGET_DIRECTORY)\generr.exe $@
+ -erase obj\$(TARGET_DIRECTORY)\error.obj
+ -erase ..\kernel\obj\$(TARGET_DIRECTORY)\error.obj
+ -erase obj\$(TARGET_DIRECTORY)\generr.obj
+ -erase obj\$(TARGET_DIRECTORY)\generr.exe
diff --git a/private/ntos/rtl/user/mips/sources b/private/ntos/rtl/user/mips/sources
new file mode 100644
index 000000000..4b26230d1
--- /dev/null
+++ b/private/ntos/rtl/user/mips/sources
@@ -0,0 +1,11 @@
+MIPS_SOURCES=..\mips\chkstk.s \
+ ..\mips\context.c \
+ ..\mips\debugstb.s \
+ ..\mips\exdsptch.c \
+ ..\mips\getcalr.c \
+ ..\mips\largeint.s \
+ ..\mips\lzntmips.s \
+ ..\mips\trampoln.s \
+ ..\mips\xcptmisc.s \
+ ..\mips\xxcaptur.s \
+ ..\mips\xxmvmem.s
diff --git a/private/ntos/rtl/user/ppc/sources b/private/ntos/rtl/user/ppc/sources
new file mode 100644
index 000000000..c5eaa6ac2
--- /dev/null
+++ b/private/ntos/rtl/user/ppc/sources
@@ -0,0 +1,10 @@
+PPC_SOURCES= ..\ppc\chkstk.s \
+ ..\ppc\context.c \
+ ..\ppc\debugstb.s \
+ ..\ppc\exdsptch.c \
+ ..\ppc\getcalr.c \
+ ..\ppc\largeint.s \
+ ..\ppc\lzntppc.s \
+ ..\ppc\trampoln.s \
+ ..\ppc\xcptmisc.s \
+ ..\ppc\movemem.s
diff --git a/private/ntos/rtl/user/sources b/private/ntos/rtl/user/sources
new file mode 100644
index 000000000..0f72a0bf3
--- /dev/null
+++ b/private/ntos/rtl/user/sources
@@ -0,0 +1,86 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=rtl
+
+TARGETNAME=rtl
+TARGETPATH=..\obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..;..\..\inc;..\..\..\inc
+
+
+
+C_DEFINES=$(C_DEFINES) -D_NTSYSTEM_
+
+SOURCES=..\acledit.c \
+ ..\assert.c \
+ ..\atom.c \
+ ..\bitmap.c \
+ ..\compress.c \
+ ..\cnvint.c \
+ ..\debug.c \
+ ..\eballoc.c \
+ ..\environ.c \
+ ..\error.c \
+ ..\excptdbg.c \
+ ..\gentable.c \
+ ..\gen8dot3.c \
+ ..\handle.c \
+ ..\heap.c \
+ ..\heapdll.c \
+ ..\heapdbg.c \
+ ..\heappage.c \
+ ..\imagedir.c \
+ ..\checksum.c \
+ ..\ldrrsrc.c \
+ ..\ldrreloc.c \
+ ..\lznt1.c \
+ ..\message.c \
+ ..\nls.c \
+ ..\pctohdr.c \
+ ..\prefix.c \
+ ..\prodtype.c \
+ ..\random.c \
+ ..\registry.c \
+ ..\regutil.c \
+ ..\rtlassig.c \
+ ..\rtldata.c \
+ ..\rtlexec.c \
+ ..\rxact.c \
+ ..\sertl.c \
+ ..\splay.c \
+ ..\string.c \
+ ..\stktrace.c \
+ ..\time.c \
+ ..\nlsxlat.c \
+ ..\eventlog.c \
+ ..\trace.c
+
+NTTARGETFILE0=..\error.h
+
+UMTEST=
+UMTYPE=console
+UMLIBS=obj\*\bitmap.obj obj\*\eventlog.obj obj\*\stktrace.obj obj\*\stkwalk.obj \nt\public\sdk\lib\*\ntdll.lib
diff --git a/private/ntos/rtl/user/theap.c b/private/ntos/rtl/user/theap.c
new file mode 100644
index 000000000..38c546d14
--- /dev/null
+++ b/private/ntos/rtl/user/theap.c
@@ -0,0 +1,301 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ theap.c
+
+Abstract:
+
+ Test program for the Heap Procedures
+
+Author:
+
+ Steven R. Wood [stevewo]
+
+Revision History:
+
+--*/
+
+#define THEAP
+#include "..\heap.c"
+#include "..\heapdll.c"
+#include "..\heapdbg.c"
+#include "..\trace.c"
+#include <windows.h>
+
+#include <stdlib.h>
+
+ULONG NtGlobalFlag = FLG_HEAP_ENABLE_TAIL_CHECK |
+ FLG_HEAP_ENABLE_FREE_CHECK |
+ FLG_HEAP_VALIDATE_PARAMETERS |
+ FLG_HEAP_VALIDATE_ALL |
+ FLG_HEAP_ENABLE_TAGGING;
+
+BOOLEAN
+NtdllOkayToLockRoutine(
+ IN PVOID Lock
+ )
+{
+ return TRUE;
+}
+
+PRTL_INITIALIZE_LOCK_ROUTINE RtlInitializeLockRoutine =
+ (PRTL_INITIALIZE_LOCK_ROUTINE)RtlInitializeCriticalSection;
+PRTL_ACQUIRE_LOCK_ROUTINE RtlAcquireLockRoutine =
+ (PRTL_ACQUIRE_LOCK_ROUTINE)RtlEnterCriticalSection;
+PRTL_RELEASE_LOCK_ROUTINE RtlReleaseLockRoutine =
+ (PRTL_RELEASE_LOCK_ROUTINE)RtlLeaveCriticalSection;
+PRTL_DELETE_LOCK_ROUTINE RtlDeleteLockRoutine =
+ (PRTL_DELETE_LOCK_ROUTINE)RtlDeleteCriticalSection;
+PRTL_OKAY_TO_LOCK_ROUTINE RtlOkayToLockRoutine =
+ (PRTL_OKAY_TO_LOCK_ROUTINE)NtdllOkayToLockRoutine;
+
+BOOLEAN
+RtlAreLogging(
+ IN ULONG EventClass
+ )
+{
+ return FALSE;
+}
+
+RTL_HEAP_PARAMETERS HeapParameters;
+
+ULONG RtlpHeapValidateOnCall;
+ULONG RtlpHeapStopOnFree;
+ULONG RtlpHeapStopOnReAlloc;
+
+
+typedef struct _TEST_HEAP_ENTRY {
+ PVOID AllocatedBlock;
+ ULONG Size;
+} TEST_HEAP_ENTRY, *PTEST_HEAP_ENTRY;
+
+ULONG NumberOfHeapEntries;
+PTEST_HEAP_ENTRY HeapEntries;
+
+ULONG Seed = 14623;
+
+#define MAX_HEAP_ALLOC 0x120000
+#define REASONABLE_HEAP_ALLOC 0x200
+
+int
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ PVOID Heap, AllocatedBlock;
+ ULONG i, n;
+ PTEST_HEAP_ENTRY p;
+ BOOLEAN Result;
+ NTSTATUS Status;
+ RTL_HEAP_USAGE Usage;
+ PRTL_HEAP_USAGE_ENTRY pEntries;
+ ULONG TagBaseIndex, Tag;
+
+ RtlInitializeHeapManager();
+ memset( &Usage, 0, sizeof( Usage ) );
+
+#if 0
+ HeapParameters.Length = sizeof( HeapParameters );
+ HeapParameters.DeCommitFreeBlockThreshold = 0x1000;
+ HeapParameters.DeCommitTotalFreeThreshold = 0x4000;
+ Heap = RtlCreateHeap( HEAP_GROWABLE | HEAP_NO_SERIALIZE,
+ NULL,
+ 256 * 4096,
+ 4096,
+ NULL,
+ &HeapParameters
+ );
+#endif
+ Heap = RtlCreateHeap( HEAP_GROWABLE | HEAP_NO_SERIALIZE | HEAP_CLASS_3,
+ NULL,
+ 0x100000,
+ 0x1000,
+ NULL,
+ NULL
+ );
+ if (Heap == NULL) {
+ fprintf( stderr, "THEAP: Unable to create heap.\n" );
+ exit( 1 );
+ }
+ fprintf( stderr, "THEAP: Created heap at %x\n", Heap );
+ DbgBreakPoint();
+ TagBaseIndex = RtlCreateTagHeap( Heap, 0, L"THEAP!",
+ L"!HeapName\0"
+ L"Tag1\0"
+ L"Tag2\0"
+ L"Tag3\0"
+ L"Tag4\0"
+ L"Tag5\0"
+ L"Tag6\0"
+ L"Tag7\0"
+ L"Tag8\0"
+ L"Tag9\0"
+ L"Tag10\0"
+ L"Tag11\0"
+ L"Tag12\0"
+ L"Tag13\0"
+ L"Tag14\0"
+ L"Tag15\0"
+ L"Tag16\0"
+ L"Tag17\0"
+ L"Tag18\0"
+ L"Tag19\0"
+ L"Tag20\0"
+ L"Tag21\0"
+ L"Tag22\0"
+ L"Tag23\0"
+ L"Tag24\0"
+ L"Tag25\0"
+ L"Tag26\0"
+ L"Tag27\0"
+ L"Tag28\0"
+ L"Tag29\0"
+ L"Tag30\0"
+ L"Tag31\0"
+ L"Tag32\0"
+ L"Tag33\0"
+ L"Tag34\0"
+ L"Tag35\0"
+ L"Tag36\0"
+ L"Tag37\0"
+ L"Tag38\0"
+ L"Tag39\0"
+ L"Tag40\0"
+ L"Tag41\0"
+ L"Tag42\0"
+ L"Tag43\0"
+ L"Tag44\0"
+ L"Tag45\0"
+ L"Tag46\0"
+ L"Tag47\0"
+ L"Tag48\0"
+ L"Tag49\0"
+ L"Tag50\0"
+ L"Tag51\0"
+ L"Tag52\0"
+ L"Tag53\0"
+ L"Tag54\0"
+ L"Tag55\0"
+ L"Tag56\0"
+ L"Tag57\0"
+ L"Tag58\0"
+ L"Tag59\0"
+ L"Tag60\0"
+ );
+
+ NumberOfHeapEntries = 1000;
+ HeapEntries = VirtualAlloc( NULL,
+ NumberOfHeapEntries * sizeof( *HeapEntries ),
+ MEM_COMMIT,
+ PAGE_READWRITE
+ );
+ if (HeapEntries == NULL) {
+ fprintf( stderr, "THEAP: Unable to allocate space.\n" );
+ exit( 1 );
+ }
+
+ RtlpHeapValidateOnCall=TRUE;
+ // RtlpHeapStopOnAllocate=0x350f88;
+ // RtlpHeapStopOnReAlloc=0x710040;
+ while (TRUE) {
+ i = RtlUniform( &Seed ) % NumberOfHeapEntries;
+ if (RtlUniform( &Seed ) % 100) {
+ n = RtlUniform( &Seed ) % REASONABLE_HEAP_ALLOC;
+ }
+ else {
+ n = RtlUniform( &Seed ) % MAX_HEAP_ALLOC;
+ }
+
+ Usage.Length = sizeof( Usage );
+ Status = RtlUsageHeap( Heap, HEAP_USAGE_ALLOCATED_BLOCKS , &Usage );
+ if (NT_SUCCESS( Status )) {
+ if (Status == STATUS_MORE_ENTRIES) {
+ pEntries = Usage.AddedEntries;
+ while (pEntries) {
+ fprintf( stderr,
+ "Added: %08x %06x\n",
+ pEntries->Address,
+ pEntries->Size
+ );
+ pEntries = pEntries->Next;
+ }
+
+ pEntries = Usage.RemovedEntries;
+ while (pEntries) {
+ fprintf( stderr,
+ "Freed: %08x %06x\n",
+ pEntries->Address,
+ pEntries->Size
+ );
+ pEntries = pEntries->Next;
+ }
+ }
+
+ fprintf( stderr, "%08x %08x %08x %08x ",
+ Usage.BytesAllocated, Usage.BytesCommitted,
+ Usage.BytesReserved, Usage.BytesReservedMaximum
+ );
+ }
+ else {
+ fprintf( stderr, "RtlUsageHeap failed with status %x\n", Status );
+ DebugBreak();
+ }
+
+ if (i < 60) {
+ Tag = (TagBaseIndex + i + 1) << 16;
+ }
+ else {
+ Tag = 0;
+ }
+ p = &HeapEntries[ i ];
+ if (p->AllocatedBlock == NULL) {
+ p->AllocatedBlock = RtlAllocateHeap( Heap, Tag, n );
+ fprintf( stderr, "Allocated %06x bytes at %08x\n", n, p->AllocatedBlock );
+ if (p->AllocatedBlock != NULL) {
+ p->Size = n;
+ }
+ else {
+ DebugBreak();
+ }
+ }
+ else
+ if (RtlUniform( &Seed ) & 1) {
+ AllocatedBlock = RtlReAllocateHeap( Heap, Tag, p->AllocatedBlock, n );
+ fprintf( stderr, "ReAlloced %06x bytes at %08x to %06x bytes at %08x\n",
+ p->Size,
+ p->AllocatedBlock,
+ n,
+ AllocatedBlock
+ );
+ if (AllocatedBlock != NULL) {
+ p->AllocatedBlock = AllocatedBlock;
+ p->Size = n;
+ }
+ else {
+ DebugBreak();
+ }
+ }
+ else {
+ Result = RtlFreeHeap( Heap, 0, p->AllocatedBlock );
+ fprintf( stderr, "Freed %06x bytes at %08x\n",
+ p->Size,
+ p->AllocatedBlock
+ );
+ if (Result) {
+ p->AllocatedBlock = NULL;
+ p->Size = 0;
+ }
+ else {
+ DebugBreak();
+ }
+ }
+
+ }
+
+ return 0;
+}
diff --git a/private/ntos/rtl/utxcpt1.c b/private/ntos/rtl/utxcpt1.c
new file mode 100644
index 000000000..b40e7f9e5
--- /dev/null
+++ b/private/ntos/rtl/utxcpt1.c
@@ -0,0 +1,83 @@
+// utxcpt1.c - user mode structured exception handling test 1
+
+#include <ntos.h>
+
+main()
+{
+ LONG i, j;
+ PULONG p4, p3, p2, p1;
+ ULONG Size1, Size2, Size3;
+ NTSTATUS status;
+ HANDLE CurrentProcessHandle;
+ MEMORY_BASIC_INFORMATION MemInfo;
+ ULONG OldProtect;
+ STRING Name3;
+ HANDLE Section1;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG ViewSize, Offset;
+
+ CurrentProcessHandle = NtCurrentProcess();
+
+ for(i=0;i<3;i++){
+ DbgPrint("Hello World...\n\n");
+ }
+
+ DbgPrint("allocating virtual memory\n");
+
+ p1 = (PULONG)NULL;
+ Size1 = 5*4096;
+
+ status = NtAllocateVirtualMemory (CurrentProcessHandle, (PVOID)&p1,
+ 0, &Size1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+
+ DbgPrint("created vm status %X start %lx size %lx\n",
+ status, (ULONG)p1, Size1);
+
+ p2 = p1;
+
+ *p2 = 99;
+ Size2 = 4;
+
+ status = NtProtectVirtualMemory (CurrentProcessHandle, (PVOID)&p2,
+ &Size2, PAGE_GUARD | PAGE_READONLY, &OldProtect);
+
+ DbgPrint("protected VM status %X, base %lx, size %lx, old protect %lx\n",
+ status, p2, Size2, OldProtect);
+
+ p3 = p1 + 1024;
+
+ *p3 =91;
+ Size2 = 4;
+
+ status = NtProtectVirtualMemory (CurrentProcessHandle, (PVOID)&p3,
+ &Size2, PAGE_NOACCESS, &OldProtect);
+
+ DbgPrint("protected VM status %X, base %lx, size %lx, old protect %lx\n",
+ status, p3, Size2, OldProtect);
+ try {
+ *p2 = 94;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+ DbgPrint("got an exception of %X\n",status);
+ }
+
+ try {
+ i = *p2;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+ DbgPrint("got an exception of %X\n",status);
+ }
+
+ DbgPrint("value of p2 should be 94 is %ld\n",*p2);
+
+ try {
+ *p3 = 94;
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+ DbgPrint("got an exception of %X\n",status);
+ }
+
+ return 0;
+}
diff --git a/private/ntos/rtl/utxcpt2.c b/private/ntos/rtl/utxcpt2.c
new file mode 100644
index 000000000..3b8037320
--- /dev/null
+++ b/private/ntos/rtl/utxcpt2.c
@@ -0,0 +1,398 @@
+// utxcpt2.c - user mode structured exception handling test 2
+//
+// Exception test from Markl.
+//
+
+#include <ntos.h>
+
+VOID
+ExceptionTest (
+ )
+
+//
+// This routine tests the structured exception handling capabilities of the
+// MS C compiler and the NT exception handling facilities.
+//
+
+{
+
+ EXCEPTION_RECORD ExceptionRecord;
+ LONG Counter;
+ ULONG rv;
+
+ //
+ // Announce start of exception test.
+ //
+
+ DbgPrint("Start of exception test\n");
+
+ //
+ // Initialize exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = (NTSTATUS)49;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 1;
+ ExceptionRecord.ExceptionInformation[0] = 9;
+
+ //
+ // Simply try statement with a finally clause that is entered sequentially.
+ //
+ DbgPrint("t1...");
+ Counter = 0;
+ try {
+ Counter += 1;
+ } finally {
+ if (abnormal_termination() == 0) {
+ Counter += 1;
+ }
+ }
+ if (Counter != 2) {
+ DbgPrint("BUG Finally clause executed as result of unwind\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // Simple try statement with an exception clause that is never executed
+ // because there is no exception raised in the try clause.
+ //
+// goto a;
+ DbgPrint("t2...");
+ Counter = 0;
+ try {
+//a: Counter += 1;
+ Counter += 1;
+ } except (Counter) {
+ Counter += 1;
+ }
+ if (Counter != 1) {
+ DbgPrint("BUG Exception clause executed when it shouldn't be\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // Simple try statement with an exception handler that is never executed
+ // because the exception expression continues execution.
+ //
+ DbgPrint("t3...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = 0;
+ try {
+ Counter -= 1;
+ RtlRaiseException(&ExceptionRecord);
+ } except (Counter) {
+ Counter -= 1;
+ }
+ if (Counter != - 1) {
+ DbgPrint("BUG Exception clause executed when it shouldn't be\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // Simple try statement with an exception clause that is always executed.
+ //
+ DbgPrint("t4...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ try {
+ Counter += 1;
+ RtlRaiseException(&ExceptionRecord);
+ } except (Counter) {
+ Counter += 1;
+ }
+ if (Counter != 2) {
+ DbgPrint("BUG Exception clause not executed when it should be\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // Simply try statement with a finally clause that is entered as the
+ // result of an exception.
+ //
+
+ DbgPrint("t5...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = 0;
+ try {
+ try {
+ Counter += 1;
+ RtlRaiseException(&ExceptionRecord);
+ } finally {
+ if (abnormal_termination() != 0) {
+ Counter += 1;
+ }
+ }
+ } except (Counter) {
+ if (Counter == 2) {
+ Counter += 1;
+ }
+ }
+ if (Counter != 3) {
+ DbgPrint("BUG Finally clause executed as result of sequential exit\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // Simple try that calls a function which raises an exception.
+ //
+ DbgPrint("t6...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ try {
+ VOID foo(IN NTSTATUS Status);
+
+ Counter += 1;
+ foo(STATUS_ACCESS_VIOLATION);
+ } except (exception_code() == STATUS_ACCESS_VIOLATION) {
+ Counter += 1;
+ }
+ if (Counter != 2) {
+ DbgPrint("BUG Exception clause not executed when it should be\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // Simple try that calls a function which calls a function that
+ // raises an exception. The first function has a finally clause
+ // that must be executed for this test to work.
+ //
+ DbgPrint("t7...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ try {
+ VOID bar(IN NTSTATUS Status, IN PULONG Counter);
+
+ bar(STATUS_ACCESS_VIOLATION, &Counter);
+
+ } except (exception_code() == STATUS_ACCESS_VIOLATION) {
+ if (Counter != 99) {
+ DbgPrint("BUG finally in called procedure not executed\n");
+ }
+ }
+ DbgPrint("done\n");
+
+ //
+ // A try within an except
+ //
+ DbgPrint("t8...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ try {
+
+ foo(STATUS_ACCESS_VIOLATION);
+
+ } except (exception_code() == STATUS_ACCESS_VIOLATION) {
+
+ Counter++;
+
+ try {
+
+ foo(STATUS_SUCCESS);
+
+ } except (exception_code() == STATUS_SUCCESS) {
+ if ( Counter != 1 ) {
+ DbgPrint("BUG Previous Handler not Entered\n");
+ }
+ Counter++;
+
+ }
+ }
+ if (Counter != 2) {
+ DbgPrint("BUG Both Handlers not entered\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // A goto from an exception clause that needs to pass
+ // through a finally
+ //
+ DbgPrint("t9...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ try {
+ try {
+ foo(STATUS_ACCESS_VIOLATION);
+ } except (exception_code() == STATUS_ACCESS_VIOLATION) {
+ Counter++;
+ goto t9;
+ }
+ } finally {
+ Counter++;
+ }
+t9:
+ if (Counter != 2) {
+ DbgPrint("BUG Finally and Exception Handlers not entered\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // A goto from an exception clause that needs to pass
+ // through a finally
+ //
+ DbgPrint("t10...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+ try {
+ try {
+ Counter++;
+ } finally {
+ Counter++;
+ goto t10;
+ }
+ } finally {
+ Counter++;
+ }
+t10:
+ if (Counter != 3) {
+ DbgPrint("BUG Both Finally Handlers not entered\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // A return from an except clause
+ //
+ DbgPrint("t11...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+
+ try {
+ ULONG eret(IN NTSTATUS Status, IN PULONG Counter);
+
+ Counter++;
+ rv = eret(STATUS_ACCESS_VIOLATION, &Counter);
+ } finally {
+ Counter++;
+ }
+
+ if (Counter != 4) {
+ DbgPrint("BUG Both Finally Handlers and Exception Handler not entered\n");
+ }
+ if (rv != 0xDEADBEEF) {
+ DbgPrint("BUG rv is wrong\n");
+ }
+ DbgPrint("done\n");
+
+ //
+ // A return from a finally clause
+ //
+ DbgPrint("t12...");
+ Counter = 0;
+ ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
+
+ try {
+ VOID fret(IN PULONG Counter);
+
+ Counter++;
+ fret(&Counter);
+ } finally {
+ Counter++;
+ }
+
+ if (Counter != 5) {
+ DbgPrint("BUG All three Finally Handlers not entered\n");
+ }
+ DbgPrint("done\n");
+ //
+ // Announce end of exception test.
+ //
+
+ DbgPrint("End of exception test\n");
+
+ return;
+}
+
+main()
+{
+ ExceptionTest ();
+}
+
+
+NTSTATUS
+ZwLastChance (
+ IN PEXCEPTION_RECORD ExceptionRecord,
+ IN PCONTEXT ContextRecord
+ )
+{
+ DbgPrint("ZwLastChance Entered\n");;
+}
+
+
+VOID
+fret(
+ IN PULONG Counter
+ )
+{
+
+ try {
+
+ try {
+ *Counter += 1;
+ } finally {
+ *Counter += 1;
+ return;
+ }
+ } finally {
+ *Counter += 1;
+ }
+}
+ULONG
+eret(
+ IN NTSTATUS Status,
+ IN PULONG Counter
+ )
+{
+
+ EXCEPTION_RECORD ExceptionRecord;
+
+ try {
+
+ try {
+ foo(Status);
+ } except (exception_code() == Status) {
+ *Counter += 1;
+ return 0xDEADBEEF;
+ }
+ } finally {
+ *Counter += 1;
+ }
+}
+VOID
+bar(
+ IN NTSTATUS Status,
+ IN PULONG Counter
+ )
+{
+
+ EXCEPTION_RECORD ExceptionRecord;
+
+ try {
+ foo(Status);
+ }
+
+ finally {
+ if (abnormal_termination() != 0) {
+ *Counter = 99;
+ } else {
+ *Counter = 100;
+ }
+ }
+}
+
+VOID
+foo(
+ IN NTSTATUS Status
+ )
+{
+ EXCEPTION_RECORD ExceptionRecord;
+ LONG Counter;
+
+ //
+ // Initialize exception record.
+ //
+
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.ExceptionCode = Status;
+ ExceptionRecord.ExceptionRecord = (PEXCEPTION_RECORD)NULL;
+ ExceptionRecord.NumberParameters = 0;
+ RtlRaiseException(&ExceptionRecord);
+}
diff --git a/private/ntos/rtl/utxcpt3.c b/private/ntos/rtl/utxcpt3.c
new file mode 100644
index 000000000..13ab5818f
--- /dev/null
+++ b/private/ntos/rtl/utxcpt3.c
@@ -0,0 +1,26 @@
+// utxcpt3.c - user mode seh test #3.
+
+#include <ntos.h>
+
+BOOLEAN EndTest = FALSE;
+
+main()
+{
+
+ while (EndTest == FALSE) {
+ try {
+ try {
+ DbgPrint("doing return\n");
+ return;
+ }
+ finally {
+ DbgPrint("in inner finally\n");
+ break;
+ }
+ }
+ finally {
+ DbgPrint("in last finally\n");
+ continue;
+ }
+ }
+}
diff --git a/private/ntos/rtl/utxcpt4.c b/private/ntos/rtl/utxcpt4.c
new file mode 100644
index 000000000..dda8aa4a3
--- /dev/null
+++ b/private/ntos/rtl/utxcpt4.c
@@ -0,0 +1,514 @@
+// utxcpt4.c - user mode seh test #3.
+
+#include <ntos.h>
+//
+// Define function prototypes.
+//
+
+VOID
+bar (
+ IN NTSTATUS Status,
+ IN PULONG Counter
+ );
+
+VOID
+eret (
+ IN NTSTATUS Status,
+ IN PULONG Counter
+ );
+
+VOID
+foo (
+ IN NTSTATUS Status
+ );
+
+VOID
+fret (
+ IN PULONG Counter
+ );
+
+BOOLEAN
+Tkm (
+ VOID
+ );
+
+//
+// Define static storage.
+//
+
+PTESTFCN TestFunction = Tkm;
+
+main()
+{
+ Tkm();
+}
+
+BOOLEAN
+Tkm (
+ )
+
+{
+
+ EXCEPTION_RECORD ExceptionRecord;
+ LONG Counter;
+
+ //
+ // Announce start of exception test.
+ //
+
+ DbgPrint("Start of exception test\n");
+
+ //
+ // Initialize exception record.
+ //
+
+ ExceptionRecord.ExceptionCode = STATUS_INTEGER_OVERFLOW;
+ ExceptionRecord.ExceptionFlags = 0;
+ ExceptionRecord.ExceptionRecord = NULL;
+ ExceptionRecord.NumberParameters = 0;
+
+ //
+ // Simply try statement with a finally clause that is entered sequentially.
+ //
+
+ DbgPrint(" test1...");
+ Counter = 0;
+ try {
+ Counter += 1;
+
+ } finally {
+ if (abnormal_termination() == FALSE) {
+ Counter += 1;
+ }
+ }
+
+ if (Counter != 2) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Simple try statement with an exception clause that is never executed
+ // because there is no exception raised in the try clause.
+ //
+
+ DbgPrint(" test2...");
+ Counter = 0;
+ try {
+ Counter += 1;
+
+ } except (Counter) {
+ Counter += 1;
+ }
+
+ if (Counter != 1) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Simple try statement with an exception handler that is never executed
+ // because the exception expression continues execution.
+ //
+
+ DbgPrint(" test3...");
+ Counter = 0;
+ try {
+ Counter -= 1;
+ RtlRaiseException(&ExceptionRecord);
+
+ } except (Counter) {
+ Counter -= 1;
+ }
+
+ if (Counter != - 1) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Simple try statement with an exception clause that is always executed.
+ //
+
+ DbgPrint(" test4...");
+ Counter = 0;
+ try {
+ Counter += 1;
+ RtlRaiseStatus(STATUS_INTEGER_OVERFLOW);
+
+ } except (Counter) {
+ Counter += 1;
+ }
+
+ if (Counter != 2) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Simply try statement with a finally clause that is entered as the
+ // result of an exception.
+ //
+
+ DbgPrint(" test5...");
+ Counter = 0;
+ try {
+ try {
+ Counter += 1;
+ RtlRaiseException(&ExceptionRecord);
+
+ } finally {
+ if (abnormal_termination() != FALSE) {
+ Counter += 1;
+ }
+ }
+
+ } except (Counter) {
+ if (Counter == 2) {
+ Counter += 1;
+ }
+ }
+
+ if (Counter != 3) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Simple try that calls a function which raises an exception.
+ //
+
+ DbgPrint(" test6...");
+ Counter = 0;
+ try {
+ Counter += 1;
+ foo(STATUS_ACCESS_VIOLATION);
+
+ } except ((GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ Counter += 1;
+ }
+
+ if (Counter != 2) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Simple try that calls a function which calls a function that
+ // raises an exception. The first function has a finally clause
+ // that must be executed for this test to work.
+ //
+
+ DbgPrint(" test7...");
+ Counter = 0;
+ try {
+ bar(STATUS_ACCESS_VIOLATION, (PULONG)&Counter);
+
+ } except ((GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ Counter -= 1;
+ }
+
+ if (Counter != 98) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A try within an except
+ //
+
+ DbgPrint(" test8...");
+ Counter = 0;
+ try {
+ foo(STATUS_ACCESS_VIOLATION);
+
+ } except ((GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ Counter += 1;
+ try {
+ foo(STATUS_SUCCESS);
+
+ } except ((GetExceptionCode() == STATUS_SUCCESS) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ if (Counter != 1) {
+ DbgPrint("failed...");
+
+ } else {
+ DbgPrint("succeeded...");
+ }
+
+ Counter += 1;
+ }
+ }
+
+ if (Counter != 2) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A goto from an exception clause that needs to pass
+ // through a finally
+ //
+
+ DbgPrint(" test9...");
+ Counter = 0;
+ try {
+ try {
+ foo(STATUS_ACCESS_VIOLATION);
+
+ } except ((GetExceptionCode() == STATUS_ACCESS_VIOLATION) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ Counter += 1;
+ goto t9;
+ }
+
+ } finally {
+ Counter += 1;
+ }
+
+t9:;
+ if (Counter != 2) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A goto from an finally clause that needs to pass
+ // through a finally
+ //
+
+ DbgPrint(" test10...");
+ Counter = 0;
+ try {
+ try {
+ Counter += 1;
+
+ } finally {
+ Counter += 1;
+ goto t10;
+ }
+
+ } finally {
+ Counter += 1;
+ }
+
+t10:;
+ if (Counter != 3) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A goto from an exception clause that needs to pass
+ // through a finally into the outer finally clause.
+ //
+
+ DbgPrint(" test11...");
+ Counter = 0;
+ try {
+ try {
+ try {
+ Counter += 1;
+ foo(STATUS_INTEGER_OVERFLOW);
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Counter += 1;
+ goto t11;
+ }
+
+ } finally {
+ Counter += 1;
+ }
+t11:;
+ } finally {
+ Counter += 1;
+ }
+
+ if (Counter != 4) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A goto from an finally clause that needs to pass
+ // through a finally into the outer finally clause.
+ //
+
+ DbgPrint(" test12...");
+ Counter = 0;
+ try {
+ try {
+ Counter += 1;
+
+ } finally {
+ Counter += 1;
+ goto t12;
+ }
+t12:;
+ } finally {
+ Counter += 1;
+ }
+
+ if (Counter != 3) {
+ DbgPrint("failed\n");
+
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A return from an except clause
+ //
+
+ DbgPrint(" test13...");
+ Counter = 0;
+ try {
+ Counter += 1;
+ eret(STATUS_ACCESS_VIOLATION, (PULONG)&Counter);
+
+ } finally {
+ Counter += 1;
+ }
+
+ if (Counter != 4) {
+ DbgPrint("failed\n");
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // A return from a finally clause
+ //
+
+ DbgPrint(" test14...");
+ Counter = 0;
+ try {
+ Counter += 1;
+ fret((PULONG)&Counter);
+
+ } finally {
+ Counter += 1;
+ }
+
+ if (Counter != 5) {
+ DbgPrint("failed\n");
+ } else {
+ DbgPrint("succeeded\n");
+ }
+
+ //
+ // Announce end of exception test.
+ //
+
+ DbgPrint("End of exception test\n");
+ return TRUE;
+}
+
+VOID
+fret(
+ IN PULONG Counter
+ )
+
+{
+
+ try {
+ try {
+ *Counter += 1;
+
+ } finally {
+ *Counter += 1;
+ return;
+ }
+ } finally {
+ *Counter += 1;
+ }
+
+ return;
+}
+
+VOID
+eret(
+ IN NTSTATUS Status,
+ IN PULONG Counter
+ )
+
+{
+
+ try {
+ try {
+ foo(Status);
+
+ } except ((GetExceptionCode() == Status) ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
+ *Counter += 1;
+ return;
+ }
+
+ } finally {
+ *Counter += 1;
+ }
+
+ return;
+}
+
+VOID
+bar (
+ IN NTSTATUS Status,
+ IN PULONG Counter
+ )
+{
+
+ try {
+ foo(Status);
+
+ } finally {
+ if (abnormal_termination() != FALSE) {
+ *Counter = 99;
+
+ } else {
+ *Counter = 100;
+ }
+ }
+
+ return;
+}
+
+VOID
+foo(
+ IN NTSTATUS Status
+ )
+
+{
+
+ //
+ // Raise exception.
+ //
+
+ RtlRaiseStatus(Status);
+ return;
+}