diff --git a/reactos/boot/freeldr/freeldr/windows/conversion.c b/reactos/boot/freeldr/freeldr/windows/conversion.c index f99810c4954..87ba43ee331 100644 --- a/reactos/boot/freeldr/freeldr/windows/conversion.c +++ b/reactos/boot/freeldr/freeldr/windows/conversion.c @@ -1,128 +1,128 @@ -/* - * PROJECT: EFI Windows Loader - * LICENSE: GPL - See COPYING in the top level directory - * FILE: freeldr/winldr/conversion.c - * PURPOSE: Physical <-> Virtual addressing mode conversions - * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) - */ - -/* INCLUDES ***************************************************************/ - -#include - -//#include -#include - -/* FUNCTIONS **************************************************************/ - -/* Arch-specific addresses translation implementation */ -PVOID -VaToPa(PVOID Va) -{ - return (PVOID)((ULONG_PTR)Va & ~KSEG0_BASE); -} - -PVOID -PaToVa(PVOID Pa) -{ - return (PVOID)((ULONG_PTR)Pa | KSEG0_BASE); -} - -VOID -List_PaToVa(LIST_ENTRY *ListEntry) -{ - LIST_ENTRY *ListHead = ListEntry; - LIST_ENTRY *Next = ListEntry->Flink; - LIST_ENTRY *NextPA; - - //Print(L"\n\nList_Entry: %X, First Next: %X\n", ListEntry, Next); - // - // Walk through the whole list - // - if (Next != NULL) - { - while (Next != PaToVa(ListHead)) - { - NextPA = VaToPa(Next); - //Print(L"Current: %X, Flink: %X, Blink: %X\n", Next, NextPA->Flink, NextPA->Blink); - - NextPA->Flink = PaToVa((PVOID)NextPA->Flink); - NextPA->Blink = PaToVa((PVOID)NextPA->Blink); - - //Print(L"After converting Flink: %X, Blink: %X\n", NextPA->Flink, NextPA->Blink); - - Next = NextPA->Flink; - } - - // - // Finally convert first Flink/Blink - // - ListEntry->Flink = PaToVa((PVOID)ListEntry->Flink); - if (ListEntry->Blink) - ListEntry->Blink = PaToVa((PVOID)ListEntry->Blink); - } -} - -// This function converts only Child->Child, and calls itself for each Sibling -VOID -ConvertConfigToVA(PCONFIGURATION_COMPONENT_DATA Start) -{ - PCONFIGURATION_COMPONENT_DATA Child; - PCONFIGURATION_COMPONENT_DATA Sibling; - - DbgPrint((DPRINT_WINDOWS, "ConvertConfigToVA(Start 0x%X)\n", Start)); - Child = Start; - - while (Child != NULL) - { - if (Child->ConfigurationData) - Child->ConfigurationData = PaToVa(Child->ConfigurationData); - - if (Child->Child) - Child->Child = PaToVa(Child->Child); - - if (Child->Parent) - Child->Parent = PaToVa(Child->Parent); - - if (Child->Sibling) - Child->Sibling = PaToVa(Child->Sibling); - - if (Child->ComponentEntry.Identifier) - Child->ComponentEntry.Identifier = PaToVa(Child->ComponentEntry.Identifier); - - DbgPrint((DPRINT_WINDOWS, "Device 0x%X class %d type %d id '%s', parent %p\n", Child, - Child->ComponentEntry.Class, Child->ComponentEntry.Type, VaToPa(Child->ComponentEntry.Identifier), Child->Parent)); - - // Go through siblings list - Sibling = VaToPa(Child->Sibling); - while (Sibling != NULL) - { - if (Sibling->ConfigurationData) - Sibling->ConfigurationData = PaToVa(Sibling->ConfigurationData); - - if (Sibling->Child) - Sibling->Child = PaToVa(Sibling->Child); - - if (Sibling->Parent) - Sibling->Parent = PaToVa(Sibling->Parent); - - if (Sibling->Sibling) - Sibling->Sibling = PaToVa(Sibling->Sibling); - - if (Sibling->ComponentEntry.Identifier) - Sibling->ComponentEntry.Identifier = PaToVa(Sibling->ComponentEntry.Identifier); - - DbgPrint((DPRINT_WINDOWS, "Device 0x%X class %d type %d id '%s', parent %p\n", Sibling, - Sibling->ComponentEntry.Class, Sibling->ComponentEntry.Type, VaToPa(Sibling->ComponentEntry.Identifier), Sibling->Parent)); - - // Recurse into the Child tree - if (VaToPa(Sibling->Child) != NULL) - ConvertConfigToVA(VaToPa(Sibling->Child)); - - Sibling = VaToPa(Sibling->Sibling); - } - - // Go to the next child - Child = VaToPa(Child->Child); - } -} +/* + * PROJECT: EFI Windows Loader + * LICENSE: GPL - See COPYING in the top level directory + * FILE: freeldr/winldr/conversion.c + * PURPOSE: Physical <-> Virtual addressing mode conversions + * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) + */ + +/* INCLUDES ***************************************************************/ + +#include + +//#include +#include + +/* FUNCTIONS **************************************************************/ + +/* Arch-specific addresses translation implementation */ +PVOID +VaToPa(PVOID Va) +{ + return (PVOID)((ULONG_PTR)Va & ~KSEG0_BASE); +} + +PVOID +PaToVa(PVOID Pa) +{ + return (PVOID)((ULONG_PTR)Pa | KSEG0_BASE); +} + +VOID +List_PaToVa(LIST_ENTRY *ListEntry) +{ + LIST_ENTRY *ListHead = ListEntry; + LIST_ENTRY *Next = ListEntry->Flink; + LIST_ENTRY *NextPA; + + //Print(L"\n\nList_Entry: %X, First Next: %X\n", ListEntry, Next); + // + // Walk through the whole list + // + if (Next != NULL) + { + while (Next != PaToVa(ListHead)) + { + NextPA = VaToPa(Next); + //Print(L"Current: %X, Flink: %X, Blink: %X\n", Next, NextPA->Flink, NextPA->Blink); + + NextPA->Flink = PaToVa((PVOID)NextPA->Flink); + NextPA->Blink = PaToVa((PVOID)NextPA->Blink); + + //Print(L"After converting Flink: %X, Blink: %X\n", NextPA->Flink, NextPA->Blink); + + Next = NextPA->Flink; + } + + // + // Finally convert first Flink/Blink + // + ListEntry->Flink = PaToVa((PVOID)ListEntry->Flink); + if (ListEntry->Blink) + ListEntry->Blink = PaToVa((PVOID)ListEntry->Blink); + } +} + +// This function converts only Child->Child, and calls itself for each Sibling +VOID +ConvertConfigToVA(PCONFIGURATION_COMPONENT_DATA Start) +{ + PCONFIGURATION_COMPONENT_DATA Child; + PCONFIGURATION_COMPONENT_DATA Sibling; + + DbgPrint((DPRINT_WINDOWS, "ConvertConfigToVA(Start 0x%X)\n", Start)); + Child = Start; + + while (Child != NULL) + { + if (Child->ConfigurationData) + Child->ConfigurationData = PaToVa(Child->ConfigurationData); + + if (Child->Child) + Child->Child = PaToVa(Child->Child); + + if (Child->Parent) + Child->Parent = PaToVa(Child->Parent); + + if (Child->Sibling) + Child->Sibling = PaToVa(Child->Sibling); + + if (Child->ComponentEntry.Identifier) + Child->ComponentEntry.Identifier = PaToVa(Child->ComponentEntry.Identifier); + + DbgPrint((DPRINT_WINDOWS, "Device 0x%X class %d type %d id '%s', parent %p\n", Child, + Child->ComponentEntry.Class, Child->ComponentEntry.Type, VaToPa(Child->ComponentEntry.Identifier), Child->Parent)); + + // Go through siblings list + Sibling = VaToPa(Child->Sibling); + while (Sibling != NULL) + { + if (Sibling->ConfigurationData) + Sibling->ConfigurationData = PaToVa(Sibling->ConfigurationData); + + if (Sibling->Child) + Sibling->Child = PaToVa(Sibling->Child); + + if (Sibling->Parent) + Sibling->Parent = PaToVa(Sibling->Parent); + + if (Sibling->Sibling) + Sibling->Sibling = PaToVa(Sibling->Sibling); + + if (Sibling->ComponentEntry.Identifier) + Sibling->ComponentEntry.Identifier = PaToVa(Sibling->ComponentEntry.Identifier); + + DbgPrint((DPRINT_WINDOWS, "Device 0x%X class %d type %d id '%s', parent %p\n", Sibling, + Sibling->ComponentEntry.Class, Sibling->ComponentEntry.Type, VaToPa(Sibling->ComponentEntry.Identifier), Sibling->Parent)); + + // Recurse into the Child tree + if (VaToPa(Sibling->Child) != NULL) + ConvertConfigToVA(VaToPa(Sibling->Child)); + + Sibling = VaToPa(Sibling->Sibling); + } + + // Go to the next child + Child = VaToPa(Child->Child); + } +} diff --git a/reactos/boot/freeldr/freeldr/windows/peloader.c b/reactos/boot/freeldr/freeldr/windows/peloader.c index a3792388669..aa5e3ad9b80 100644 --- a/reactos/boot/freeldr/freeldr/windows/peloader.c +++ b/reactos/boot/freeldr/freeldr/windows/peloader.c @@ -1,830 +1,830 @@ -/* - * PROJECT: FreeLoader - * LICENSE: GPL - See COPYING in the top level directory - * FILE: freeldr/winldr/peloader.c - * PURPOSE: Provides routines for loading PE files. To be merged with - * arch/i386/loader.c in future - * This article was very handy during development: - * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/ - * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) - * The source code in this file is based on the work of respective - * authors of PE loading code in ReactOS and Brian Palmer and - * Alex Ionescu's arch/i386/loader.c, and my research project - * (creating a native EFI loader for Windows) - */ - -/* INCLUDES ***************************************************************/ -#include -#include - - -BOOLEAN -WinLdrpCompareDllName(IN PCH DllName, - IN PUNICODE_STRING UnicodeName); - -BOOLEAN -WinLdrpBindImportName(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData, - IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, - IN ULONG ExportSize, - IN BOOLEAN ProcessForwards); - -BOOLEAN -WinLdrpLoadAndScanReferencedDll(PLOADER_PARAMETER_BLOCK WinLdrBlock, - PCCH DirectoryPath, - PCH ImportName, - PLDR_DATA_TABLE_ENTRY *DataTableEntry); - -BOOLEAN -WinLdrpScanImportAddressTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData); - - - -/* FUNCTIONS **************************************************************/ - -/* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */ -BOOLEAN -WinLdrCheckForLoadedDll(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PCH DllName, - OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry) -{ - PLDR_DATA_TABLE_ENTRY DataTableEntry; - LIST_ENTRY *ModuleEntry; - - DbgPrint((DPRINT_WINDOWS, "WinLdrCheckForLoadedDll: DllName %X, LoadedEntry: %X\n", - DllName, LoadedEntry)); - - /* Just go through each entry in the LoadOrderList and compare loaded module's - name with a given name */ - ModuleEntry = WinLdrBlock->LoadOrderListHead.Flink; - while (ModuleEntry != &WinLdrBlock->LoadOrderListHead) - { - /* Get pointer to the current DTE */ - DataTableEntry = CONTAINING_RECORD(ModuleEntry, - LDR_DATA_TABLE_ENTRY, - InLoadOrderLinks); - - DbgPrint((DPRINT_WINDOWS, "WinLdrCheckForLoadedDll: DTE %p, EP %p\n", - DataTableEntry, DataTableEntry->EntryPoint)); - - /* Compare names */ - if (WinLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName)) - { - /* Yes, found it, report pointer to the loaded module's DTE - to the caller and increase load count for it */ - *LoadedEntry = DataTableEntry; - DataTableEntry->LoadCount++; - DbgPrint((DPRINT_WINDOWS, "WinLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry)); - return TRUE; - } - - /* Go to the next entry */ - ModuleEntry = ModuleEntry->Flink; - } - - /* Nothing found */ - return FALSE; -} - -BOOLEAN -WinLdrScanImportDescriptorTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PCCH DirectoryPath, - IN PLDR_DATA_TABLE_ENTRY ScanDTE) -{ - PLDR_DATA_TABLE_ENTRY DataTableEntry; - PIMAGE_IMPORT_DESCRIPTOR ImportTable; - ULONG ImportTableSize; - PCH ImportName; - BOOLEAN Status; - - /* Get a pointer to the import table of this image */ - ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(VaToPa(ScanDTE->DllBase), - TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ImportTableSize); - - { - UNICODE_STRING BaseName; - BaseName.Buffer = VaToPa(ScanDTE->BaseDllName.Buffer); - BaseName.MaximumLength = ScanDTE->BaseDllName.MaximumLength; - BaseName.Length = ScanDTE->BaseDllName.Length; - DbgPrint((DPRINT_WINDOWS, "WinLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n", - &BaseName, ImportTable)); - } - - /* If image doesn't have any import directory - just return success */ - if (ImportTable == NULL) - return TRUE; - - /* Loop through all entries */ - for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++) - { - /* Get pointer to the name */ - ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name)); - DbgPrint((DPRINT_WINDOWS, "WinLdrScanImportDescriptorTable(): Looking at %s\n", ImportName)); - - /* In case we get a reference to ourselves - just skip it */ - if (WinLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName)) - continue; - - /* Load the DLL if it is not already loaded */ - if (!WinLdrCheckForLoadedDll(WinLdrBlock, ImportName, &DataTableEntry)) - { - Status = WinLdrpLoadAndScanReferencedDll(WinLdrBlock, - DirectoryPath, - ImportName, - &DataTableEntry); - - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, "WinLdrpLoadAndScanReferencedDll() failed\n")); - return Status; - } - } - - /* Scan its import address table */ - Status = WinLdrpScanImportAddressTable( - WinLdrBlock, - DataTableEntry->DllBase, - ScanDTE->DllBase, - (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk)); - - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, "WinLdrpScanImportAddressTable() failed\n")); - return Status; - } - } - - return TRUE; -} - -BOOLEAN -WinLdrAllocateDataTableEntry(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PCCH BaseDllName, - IN PCCH FullDllName, - IN PVOID BasePA, - OUT PLDR_DATA_TABLE_ENTRY *NewEntry) -{ - PVOID BaseVA = PaToVa(BasePA); - PWSTR Buffer; - PLDR_DATA_TABLE_ENTRY DataTableEntry; - PIMAGE_NT_HEADERS NtHeaders; - USHORT Length; - - /* Allocate memory for a data table entry, zero-initialize it */ - DataTableEntry = (PLDR_DATA_TABLE_ENTRY)MmHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY)); - if (DataTableEntry == NULL) - return FALSE; - RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY)); - - /* Get NT headers from the image */ - NtHeaders = RtlImageNtHeader(BasePA); - - /* Initialize corresponding fields of DTE based on NT headers value */ - DataTableEntry->DllBase = BaseVA; - DataTableEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage; - DataTableEntry->EntryPoint = RVA(BaseVA, NtHeaders->OptionalHeader.AddressOfEntryPoint); - DataTableEntry->SectionPointer = 0; - DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum; - - /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName - by simple conversion - copying each character */ - Length = (USHORT)(strlen(BaseDllName) * sizeof(WCHAR)); - Buffer = (PWSTR)MmHeapAlloc(Length); - if (Buffer == NULL) - { - MmHeapFree(DataTableEntry); - return FALSE; - } - RtlZeroMemory(Buffer, Length); - - DataTableEntry->BaseDllName.Length = Length; - DataTableEntry->BaseDllName.MaximumLength = Length; - DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer); - while (*BaseDllName != 0) - { - *Buffer++ = *BaseDllName++; - } - - /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName - using the same method */ - Length = (USHORT)(strlen(FullDllName) * sizeof(WCHAR)); - Buffer = (PWSTR)MmHeapAlloc(Length); - if (Buffer == NULL) - { - MmHeapFree(DataTableEntry); - return FALSE; - } - RtlZeroMemory(Buffer, Length); - - DataTableEntry->FullDllName.Length = Length; - DataTableEntry->FullDllName.MaximumLength = Length; - DataTableEntry->FullDllName.Buffer = PaToVa(Buffer); - while (*FullDllName != 0) - { - *Buffer++ = *FullDllName++; - } - - /* Initialize what's left - LoadCount which is 1, and set Flags so that - we know this entry is processed */ - DataTableEntry->Flags = LDRP_ENTRY_PROCESSED; - DataTableEntry->LoadCount = 1; - - /* Insert this DTE to a list in the LPB */ - InsertTailList(&WinLdrBlock->LoadOrderListHead, &DataTableEntry->InLoadOrderLinks); - - /* Save pointer to a newly allocated and initialized entry */ - *NewEntry = DataTableEntry; - - /* Return success */ - return TRUE; -} - -/* WinLdrLoadImage loads the specified image from the file (it doesn't - perform any additional operations on the filename, just directly - calls the file I/O routines), and relocates it so that it's ready - to be used when paging is enabled. - Addressing mode: physical - */ -BOOLEAN -WinLdrLoadImage(IN PCHAR FileName, - TYPE_OF_MEMORY MemoryType, - OUT PVOID *ImageBasePA) -{ - PFILE FileHandle; - PVOID PhysicalBase; - PVOID VirtualBase = NULL; - UCHAR HeadersBuffer[SECTOR_SIZE * 2]; - PIMAGE_NT_HEADERS NtHeaders; - PIMAGE_SECTION_HEADER SectionHeader; - ULONG VirtualSize, SizeOfRawData, NumberOfSections; - BOOLEAN Status; - ULONG i, BytesRead; - - CHAR ProgressString[256]; - - /* Inform user we are loading files */ - sprintf(ProgressString, "Loading %s...", FileName); - UiDrawProgressBarCenter(1, 100, ProgressString); - - /* Open the image file */ - FileHandle = FsOpenFile(FileName); - - if (FileHandle == NULL) - { - //Print(L"Can not open the file %s\n",FileName); - UiMessageBox("Can not open the file"); - return FALSE; - } - - /* Load the first 2 sectors of the image so we can read the PE header */ - Status = FsReadFile(FileHandle, SECTOR_SIZE * 2, NULL, HeadersBuffer); - if (!Status) - { - //Print(L"Error reading from file %s\n", FileName); - UiMessageBox("Error reading from file"); - FsCloseFile(FileHandle); - return FALSE; - } - - /* Now read the MZ header to get the offset to the PE Header */ - NtHeaders = RtlImageNtHeader(HeadersBuffer); - - if (!NtHeaders) - { - //Print(L"Error - no NT header found in %s\n", FileName); - UiMessageBox("Error - no NT header found"); - FsCloseFile(FileHandle); - return FALSE; - } - - /* Ensure this is executable image */ - if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) - { - //Print(L"Not an executable image %s\n", FileName); - UiMessageBox("Not an executable image"); - FsCloseFile(FileHandle); - return FALSE; - } - - /* Store number of sections to read and a pointer to the first section */ - NumberOfSections = NtHeaders->FileHeader.NumberOfSections; - SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); - - /* Try to allocate this memory, if fails - allocate somewhere else */ - PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage, - (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)), - MemoryType); - - if (PhysicalBase == NULL) - { - /* It's ok, we don't panic - let's allocate again at any other "low" place */ - PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType); - - if (PhysicalBase == NULL) - { - //Print(L"Failed to alloc pages for image %s\n", FileName); - UiMessageBox("Failed to alloc pages for image"); - FsCloseFile(FileHandle); - return FALSE; - } - } - - /* This is the real image base - in form of a virtual address */ - VirtualBase = PaToVa(PhysicalBase); - - DbgPrint((DPRINT_WINDOWS, "Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase)); - - /* Set to 0 position and fully load the file image */ - FsSetFilePointer(FileHandle, 0); - - Status = FsReadFile(FileHandle, NtHeaders->OptionalHeader.SizeOfHeaders, NULL, PhysicalBase); - - if (!Status) - { - //Print(L"Error reading headers %s\n", FileName); - UiMessageBox("Error reading headers"); - FsCloseFile(FileHandle); - return FALSE; - } - - /* Reload the NT Header */ - NtHeaders = RtlImageNtHeader(PhysicalBase); - - /* Load the first section */ - SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); - - /* Fill output parameters */ - *ImageBasePA = PhysicalBase; - - /* Walk through each section and read it (check/fix any possible - bad situations, if they arise) */ - for (i = 0; i < NumberOfSections; i++) - { - VirtualSize = SectionHeader->Misc.VirtualSize; - SizeOfRawData = SectionHeader->SizeOfRawData; - - /* Handle a case when VirtualSize equals 0 */ - if (VirtualSize == 0) - VirtualSize = SizeOfRawData; - - /* If PointerToRawData is 0, then force its size to be also 0 */ - if (SectionHeader->PointerToRawData == 0) - { - SizeOfRawData = 0; - } - else - { - /* Cut the loaded size to the VirtualSize extents */ - if (SizeOfRawData > VirtualSize) - SizeOfRawData = VirtualSize; - } - - /* Actually read the section (if its size is not 0) */ - if (SizeOfRawData != 0) - { - /* Seek to the correct position */ - FsSetFilePointer(FileHandle, SectionHeader->PointerToRawData); - - DbgPrint((DPRINT_WINDOWS, "SH->VA: 0x%X\n", SectionHeader->VirtualAddress)); - - /* Read this section from the file, size = SizeOfRawData */ - Status = FsReadFile(FileHandle, SizeOfRawData, &BytesRead, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress); - - if (!Status && (BytesRead == 0)) - { - DbgPrint((DPRINT_WINDOWS, "WinLdrLoadImage(): Error reading section from file!\n")); - break; - } - } - - /* Size of data is less than the virtual size - fill up the remainder with zeroes */ - if (SizeOfRawData < VirtualSize) - { - DbgPrint((DPRINT_WINDOWS, "WinLdrLoadImage(): SORD %d < VS %d", SizeOfRawData, VirtualSize)); - RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData); - } - - SectionHeader++; - } - - /* We are done with the file - close it */ - FsCloseFile(FileHandle); - - /* If loading failed - return right now */ - if (!Status) - return FALSE; - - - /* Relocate the image, if it needs it */ - if (NtHeaders->OptionalHeader.ImageBase != (ULONG)VirtualBase) - { - DbgPrint((DPRINT_WINDOWS, "Relocating %p -> %p\n", - NtHeaders->OptionalHeader.ImageBase, VirtualBase)); - Status = (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase, - (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase, - "FreeLdr", - TRUE, - TRUE, /* in case of conflict still return success */ - FALSE); - } - - return Status; -} - -/* PRIVATE FUNCTIONS *******************************************************/ - -/* DllName - physical, UnicodeString->Buffer - virtual */ -BOOLEAN -WinLdrpCompareDllName(IN PCH DllName, - IN PUNICODE_STRING UnicodeName) -{ - PWSTR Buffer; - UNICODE_STRING UnicodeNamePA; - ULONG i, Length; - - /* First obvious check: for length of two names */ - Length = strlen(DllName); - - UnicodeNamePA.Length = UnicodeName->Length; - UnicodeNamePA.MaximumLength = UnicodeName->MaximumLength; - UnicodeNamePA.Buffer = VaToPa(UnicodeName->Buffer); - DbgPrint((DPRINT_WINDOWS, "WinLdrpCompareDllName: %s and %wZ, Length = %d " - "UN->Length %d\n", DllName, &UnicodeNamePA, Length, UnicodeName->Length)); - - if ((Length * sizeof(WCHAR)) > UnicodeName->Length) - return FALSE; - - /* Store pointer to unicode string's buffer */ - Buffer = VaToPa(UnicodeName->Buffer); - - /* Loop character by character */ - for (i = 0; i < Length; i++) - { - /* Compare two characters, uppercasing them */ - if (toupper(*DllName) != toupper((CHAR)*Buffer)) - return FALSE; - - /* Move to the next character */ - DllName++; - Buffer++; - } - - /* Check, if strings either fully match, or match till the "." (w/o extension) */ - if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.')) - { - /* Yes they do */ - return TRUE; - } - - /* Strings don't match, return FALSE */ - return FALSE; -} - -BOOLEAN -WinLdrpBindImportName(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData, - IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, - IN ULONG ExportSize, - IN BOOLEAN ProcessForwards) -{ - ULONG Ordinal; - PULONG NameTable, FunctionTable; - PUSHORT OrdinalTable; - LONG High, Low, Middle, Result; - ULONG Hint; - - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n", - // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards)); - - /* Check passed DllBase param */ - if(DllBase == NULL) - { - DbgPrint((DPRINT_WINDOWS, "WARNING: DllBase == NULL!\n")); - return FALSE; - } - - /* Convert all non-critical pointers to PA from VA */ - ThunkData = VaToPa(ThunkData); - - /* Is the reference by ordinal? */ - if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards) - { - /* Yes, calculate the ordinal */ - Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base); - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): Ordinal %d\n", Ordinal)); - } - else - { - /* It's reference by name, we have to look it up in the export directory */ - if (!ProcessForwards) - { - /* AddressOfData in thunk entry will become a virtual address (from relative) */ - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData)); - ThunkData->u1.AddressOfData = - (ULONG)RVA(ImageBase, ThunkData->u1.AddressOfData); - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData)); - } - - /* Get pointers to Name and Ordinal tables (RVA -> VA) */ - NameTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames)); - OrdinalTable = (PUSHORT)VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals)); - - //DbgPrint((DPRINT_WINDOWS, "NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n", - // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals)); - - /* Get the hint, convert it to a physical pointer */ - Hint = ((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Hint; - //DbgPrint((DPRINT_WINDOWS, "HintIndex %d\n", Hint)); - - /* If Hint is less than total number of entries in the export directory, - and import name == export name, then we can just get it from the OrdinalTable */ - if ( - (Hint < ExportDirectory->NumberOfNames) && - ( - strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]), - (PCHAR)VaToPa( RVA(DllBase, NameTable[Hint])) ) == 0 - ) - ) - { - Ordinal = OrdinalTable[Hint]; - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): Ordinal %d\n", Ordinal)); - } - else - { - /* It's not the easy way, we have to lookup import name in the name table. - Let's use a binary search for this task. */ - - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName() looking up the import name using binary search...\n")); - - /* Low boundary is set to 0, and high boundary to the maximum index */ - Low = 0; - High = ExportDirectory->NumberOfNames - 1; - - /* Perform a binary-search loop */ - while (High >= Low) - { - /* Divide by 2 by shifting to the right once */ - Middle = (Low + High) >> 1; - - /* Compare the names */ - Result = strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]), - (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle]))); - - /*DbgPrint((DPRINT_WINDOWS, "Binary search: comparing Import '__', Export '%s'\n",*/ - /*VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),*/ - /*(PCHAR)VaToPa(RVA(DllBase, NameTable[Middle]))));*/ - - /*DbgPrint((DPRINT_WINDOWS, "TE->u1.AOD %p, fulladdr %p\n", - ThunkData->u1.AddressOfData, - ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name ));*/ - - - /* Depending on result of strcmp, perform different actions */ - if (Result < 0) - { - /* Adjust top boundary */ - High = Middle - 1; - } - else if (Result > 0) - { - /* Adjust bottom boundary */ - Low = Middle + 1; - } - else - { - /* Yay, found it! */ - break; - } - } - - /* If high boundary is less than low boundary, then no result found */ - if (High < Low) - { - //Print(L"Error in binary search\n"); - DbgPrint((DPRINT_WINDOWS, "Error in binary search!\n")); - return FALSE; - } - - /* Everything allright, get the ordinal */ - Ordinal = OrdinalTable[Middle]; - - //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName() found Ordinal %d\n", Ordinal)); - } - } - - /* Check ordinal number for validity! */ - if (Ordinal >= ExportDirectory->NumberOfFunctions) - { - DbgPrint((DPRINT_WINDOWS, "Ordinal number is invalid!\n")); - return FALSE; - } - - /* Get a pointer to the function table */ - FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions)); - - /* Save a pointer to the function */ - ThunkData->u1.Function = (ULONG)RVA(DllBase, FunctionTable[Ordinal]); - - /* Is it a forwarder? (function pointer isn't within the export directory) */ - if (((ULONG)VaToPa((PVOID)ThunkData->u1.Function) > (ULONG)ExportDirectory) && - ((ULONG)VaToPa((PVOID)ThunkData->u1.Function) < ((ULONG)ExportDirectory + ExportSize))) - { - PLDR_DATA_TABLE_ENTRY DataTableEntry; - CHAR ForwardDllName[255]; - PIMAGE_EXPORT_DIRECTORY RefExportDirectory; - ULONG RefExportSize; - - /* Save the name of the forward dll */ - RtlCopyMemory(ForwardDllName, (PCHAR)VaToPa((PVOID)ThunkData->u1.Function), sizeof(ForwardDllName)); - - /* Strip out its extension */ - *strchr(ForwardDllName,'.') = '\0'; - - DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): ForwardDllName %s\n", ForwardDllName)); - if (!WinLdrCheckForLoadedDll(WinLdrBlock, ForwardDllName, &DataTableEntry)) - { - /* We can't continue if DLL couldn't be loaded, so bomb out with an error */ - //Print(L"Error loading DLL!\n"); - DbgPrint((DPRINT_WINDOWS, "Error loading DLL!\n")); - return FALSE; - } - - /* Get pointer to the export directory of loaded DLL */ - RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY) - RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase), - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &RefExportSize); - - /* Fail if it's NULL */ - if (RefExportDirectory) - { - UCHAR Buffer[128]; - IMAGE_THUNK_DATA RefThunkData; - PIMAGE_IMPORT_BY_NAME ImportByName; - PCHAR ImportName; - BOOLEAN Status; - - /* Get pointer to the import name */ - ImportName = strchr((PCHAR)VaToPa((PVOID)ThunkData->u1.Function), '.') + 1; - - /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */ - ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer; - - /* Fill the name with the import name */ - RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1); - - /* Set Hint to 0 */ - ImportByName->Hint = 0; - - /* And finally point ThunkData's AddressOfData to that structure */ - RefThunkData.u1.AddressOfData = (ULONG)ImportByName; - - /* And recursively call ourselves */ - Status = WinLdrpBindImportName( - WinLdrBlock, - DataTableEntry->DllBase, - ImageBase, - &RefThunkData, - RefExportDirectory, - RefExportSize, - TRUE); - - /* Fill out the ThunkData with data from RefThunkData */ - ThunkData->u1 = RefThunkData.u1; - - /* Return what we got from the recursive call */ - return Status; - } - else - { - /* Fail if ExportDirectory is NULL */ - return FALSE; - } - } - - /* Success! */ - return TRUE; -} - -BOOLEAN -WinLdrpLoadAndScanReferencedDll(PLOADER_PARAMETER_BLOCK WinLdrBlock, - PCCH DirectoryPath, - PCH ImportName, - PLDR_DATA_TABLE_ENTRY *DataTableEntry) -{ - CHAR FullDllName[256]; - BOOLEAN Status; - PVOID BasePA; - - /* Prepare the full path to the file to be loaded */ - strcpy(FullDllName, DirectoryPath); - strcat(FullDllName, ImportName); - - DbgPrint((DPRINT_WINDOWS, "Loading referenced DLL: %s\n", FullDllName)); - //Print(L"Loading referenced DLL: %s\n", FullDllName); - - /* Load the image */ - Status = WinLdrLoadImage(FullDllName, LoaderHalCode, &BasePA); - - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, "WinLdrLoadImage() failed\n")); - return Status; - } - - /* Allocate DTE for newly loaded DLL */ - Status = WinLdrAllocateDataTableEntry(WinLdrBlock, - ImportName, - FullDllName, - BasePA, - DataTableEntry); - - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, - "WinLdrAllocateDataTableEntry() failed with Status=0x%X\n", Status)); - return Status; - } - - /* Scan its dependencies too */ - DbgPrint((DPRINT_WINDOWS, - "WinLdrScanImportDescriptorTable() calling ourselves for %S\n", - VaToPa((*DataTableEntry)->BaseDllName.Buffer))); - Status = WinLdrScanImportDescriptorTable(WinLdrBlock, DirectoryPath, *DataTableEntry); - - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, - "WinLdrScanImportDescriptorTable() failed with Status=0x%X\n", Status)); - return Status; - } - - return TRUE; -} - -BOOLEAN -WinLdrpScanImportAddressTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData) -{ - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - BOOLEAN Status; - ULONG ExportSize; - - DbgPrint((DPRINT_WINDOWS, "WinLdrpScanImportAddressTable(): DllBase 0x%X, " - "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData)); - - /* Obtain the export table from the DLL's base */ - if (DllBase == NULL) - { - //Print(L"Error, DllBase == NULL!\n"); - return FALSE; - } - else - { - ExportDirectory = - (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase), - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &ExportSize); - } - - DbgPrint((DPRINT_WINDOWS, "WinLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory)); - - /* If pointer to Export Directory is */ - if (ExportDirectory == NULL) - return FALSE; - - /* Go through each entry in the thunk table and bind it */ - while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0) - { - /* Bind it */ - Status = WinLdrpBindImportName( - WinLdrBlock, - DllBase, - ImageBase, - ThunkData, - ExportDirectory, - ExportSize, - FALSE); - - /* Move to the next entry */ - ThunkData++; - - /* Return error if binding was unsuccessful */ - if (!Status) - return Status; - } - - /* Return success */ - return TRUE; -} +/* + * PROJECT: FreeLoader + * LICENSE: GPL - See COPYING in the top level directory + * FILE: freeldr/winldr/peloader.c + * PURPOSE: Provides routines for loading PE files. To be merged with + * arch/i386/loader.c in future + * This article was very handy during development: + * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/ + * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) + * The source code in this file is based on the work of respective + * authors of PE loading code in ReactOS and Brian Palmer and + * Alex Ionescu's arch/i386/loader.c, and my research project + * (creating a native EFI loader for Windows) + */ + +/* INCLUDES ***************************************************************/ +#include +#include + + +BOOLEAN +WinLdrpCompareDllName(IN PCH DllName, + IN PUNICODE_STRING UnicodeName); + +BOOLEAN +WinLdrpBindImportName(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PVOID DllBase, + IN PVOID ImageBase, + IN PIMAGE_THUNK_DATA ThunkData, + IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, + IN ULONG ExportSize, + IN BOOLEAN ProcessForwards); + +BOOLEAN +WinLdrpLoadAndScanReferencedDll(PLOADER_PARAMETER_BLOCK WinLdrBlock, + PCCH DirectoryPath, + PCH ImportName, + PLDR_DATA_TABLE_ENTRY *DataTableEntry); + +BOOLEAN +WinLdrpScanImportAddressTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PVOID DllBase, + IN PVOID ImageBase, + IN PIMAGE_THUNK_DATA ThunkData); + + + +/* FUNCTIONS **************************************************************/ + +/* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */ +BOOLEAN +WinLdrCheckForLoadedDll(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PCH DllName, + OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry) +{ + PLDR_DATA_TABLE_ENTRY DataTableEntry; + LIST_ENTRY *ModuleEntry; + + DbgPrint((DPRINT_WINDOWS, "WinLdrCheckForLoadedDll: DllName %X, LoadedEntry: %X\n", + DllName, LoadedEntry)); + + /* Just go through each entry in the LoadOrderList and compare loaded module's + name with a given name */ + ModuleEntry = WinLdrBlock->LoadOrderListHead.Flink; + while (ModuleEntry != &WinLdrBlock->LoadOrderListHead) + { + /* Get pointer to the current DTE */ + DataTableEntry = CONTAINING_RECORD(ModuleEntry, + LDR_DATA_TABLE_ENTRY, + InLoadOrderLinks); + + DbgPrint((DPRINT_WINDOWS, "WinLdrCheckForLoadedDll: DTE %p, EP %p\n", + DataTableEntry, DataTableEntry->EntryPoint)); + + /* Compare names */ + if (WinLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName)) + { + /* Yes, found it, report pointer to the loaded module's DTE + to the caller and increase load count for it */ + *LoadedEntry = DataTableEntry; + DataTableEntry->LoadCount++; + DbgPrint((DPRINT_WINDOWS, "WinLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry)); + return TRUE; + } + + /* Go to the next entry */ + ModuleEntry = ModuleEntry->Flink; + } + + /* Nothing found */ + return FALSE; +} + +BOOLEAN +WinLdrScanImportDescriptorTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PCCH DirectoryPath, + IN PLDR_DATA_TABLE_ENTRY ScanDTE) +{ + PLDR_DATA_TABLE_ENTRY DataTableEntry; + PIMAGE_IMPORT_DESCRIPTOR ImportTable; + ULONG ImportTableSize; + PCH ImportName; + BOOLEAN Status; + + /* Get a pointer to the import table of this image */ + ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(VaToPa(ScanDTE->DllBase), + TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ImportTableSize); + + { + UNICODE_STRING BaseName; + BaseName.Buffer = VaToPa(ScanDTE->BaseDllName.Buffer); + BaseName.MaximumLength = ScanDTE->BaseDllName.MaximumLength; + BaseName.Length = ScanDTE->BaseDllName.Length; + DbgPrint((DPRINT_WINDOWS, "WinLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n", + &BaseName, ImportTable)); + } + + /* If image doesn't have any import directory - just return success */ + if (ImportTable == NULL) + return TRUE; + + /* Loop through all entries */ + for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++) + { + /* Get pointer to the name */ + ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name)); + DbgPrint((DPRINT_WINDOWS, "WinLdrScanImportDescriptorTable(): Looking at %s\n", ImportName)); + + /* In case we get a reference to ourselves - just skip it */ + if (WinLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName)) + continue; + + /* Load the DLL if it is not already loaded */ + if (!WinLdrCheckForLoadedDll(WinLdrBlock, ImportName, &DataTableEntry)) + { + Status = WinLdrpLoadAndScanReferencedDll(WinLdrBlock, + DirectoryPath, + ImportName, + &DataTableEntry); + + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, "WinLdrpLoadAndScanReferencedDll() failed\n")); + return Status; + } + } + + /* Scan its import address table */ + Status = WinLdrpScanImportAddressTable( + WinLdrBlock, + DataTableEntry->DllBase, + ScanDTE->DllBase, + (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk)); + + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, "WinLdrpScanImportAddressTable() failed\n")); + return Status; + } + } + + return TRUE; +} + +BOOLEAN +WinLdrAllocateDataTableEntry(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PCCH BaseDllName, + IN PCCH FullDllName, + IN PVOID BasePA, + OUT PLDR_DATA_TABLE_ENTRY *NewEntry) +{ + PVOID BaseVA = PaToVa(BasePA); + PWSTR Buffer; + PLDR_DATA_TABLE_ENTRY DataTableEntry; + PIMAGE_NT_HEADERS NtHeaders; + USHORT Length; + + /* Allocate memory for a data table entry, zero-initialize it */ + DataTableEntry = (PLDR_DATA_TABLE_ENTRY)MmHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY)); + if (DataTableEntry == NULL) + return FALSE; + RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY)); + + /* Get NT headers from the image */ + NtHeaders = RtlImageNtHeader(BasePA); + + /* Initialize corresponding fields of DTE based on NT headers value */ + DataTableEntry->DllBase = BaseVA; + DataTableEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage; + DataTableEntry->EntryPoint = RVA(BaseVA, NtHeaders->OptionalHeader.AddressOfEntryPoint); + DataTableEntry->SectionPointer = 0; + DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum; + + /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName + by simple conversion - copying each character */ + Length = (USHORT)(strlen(BaseDllName) * sizeof(WCHAR)); + Buffer = (PWSTR)MmHeapAlloc(Length); + if (Buffer == NULL) + { + MmHeapFree(DataTableEntry); + return FALSE; + } + RtlZeroMemory(Buffer, Length); + + DataTableEntry->BaseDllName.Length = Length; + DataTableEntry->BaseDllName.MaximumLength = Length; + DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer); + while (*BaseDllName != 0) + { + *Buffer++ = *BaseDllName++; + } + + /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName + using the same method */ + Length = (USHORT)(strlen(FullDllName) * sizeof(WCHAR)); + Buffer = (PWSTR)MmHeapAlloc(Length); + if (Buffer == NULL) + { + MmHeapFree(DataTableEntry); + return FALSE; + } + RtlZeroMemory(Buffer, Length); + + DataTableEntry->FullDllName.Length = Length; + DataTableEntry->FullDllName.MaximumLength = Length; + DataTableEntry->FullDllName.Buffer = PaToVa(Buffer); + while (*FullDllName != 0) + { + *Buffer++ = *FullDllName++; + } + + /* Initialize what's left - LoadCount which is 1, and set Flags so that + we know this entry is processed */ + DataTableEntry->Flags = LDRP_ENTRY_PROCESSED; + DataTableEntry->LoadCount = 1; + + /* Insert this DTE to a list in the LPB */ + InsertTailList(&WinLdrBlock->LoadOrderListHead, &DataTableEntry->InLoadOrderLinks); + + /* Save pointer to a newly allocated and initialized entry */ + *NewEntry = DataTableEntry; + + /* Return success */ + return TRUE; +} + +/* WinLdrLoadImage loads the specified image from the file (it doesn't + perform any additional operations on the filename, just directly + calls the file I/O routines), and relocates it so that it's ready + to be used when paging is enabled. + Addressing mode: physical + */ +BOOLEAN +WinLdrLoadImage(IN PCHAR FileName, + TYPE_OF_MEMORY MemoryType, + OUT PVOID *ImageBasePA) +{ + PFILE FileHandle; + PVOID PhysicalBase; + PVOID VirtualBase = NULL; + UCHAR HeadersBuffer[SECTOR_SIZE * 2]; + PIMAGE_NT_HEADERS NtHeaders; + PIMAGE_SECTION_HEADER SectionHeader; + ULONG VirtualSize, SizeOfRawData, NumberOfSections; + BOOLEAN Status; + ULONG i, BytesRead; + + CHAR ProgressString[256]; + + /* Inform user we are loading files */ + sprintf(ProgressString, "Loading %s...", FileName); + UiDrawProgressBarCenter(1, 100, ProgressString); + + /* Open the image file */ + FileHandle = FsOpenFile(FileName); + + if (FileHandle == NULL) + { + //Print(L"Can not open the file %s\n",FileName); + UiMessageBox("Can not open the file"); + return FALSE; + } + + /* Load the first 2 sectors of the image so we can read the PE header */ + Status = FsReadFile(FileHandle, SECTOR_SIZE * 2, NULL, HeadersBuffer); + if (!Status) + { + //Print(L"Error reading from file %s\n", FileName); + UiMessageBox("Error reading from file"); + FsCloseFile(FileHandle); + return FALSE; + } + + /* Now read the MZ header to get the offset to the PE Header */ + NtHeaders = RtlImageNtHeader(HeadersBuffer); + + if (!NtHeaders) + { + //Print(L"Error - no NT header found in %s\n", FileName); + UiMessageBox("Error - no NT header found"); + FsCloseFile(FileHandle); + return FALSE; + } + + /* Ensure this is executable image */ + if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) + { + //Print(L"Not an executable image %s\n", FileName); + UiMessageBox("Not an executable image"); + FsCloseFile(FileHandle); + return FALSE; + } + + /* Store number of sections to read and a pointer to the first section */ + NumberOfSections = NtHeaders->FileHeader.NumberOfSections; + SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); + + /* Try to allocate this memory, if fails - allocate somewhere else */ + PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage, + (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)), + MemoryType); + + if (PhysicalBase == NULL) + { + /* It's ok, we don't panic - let's allocate again at any other "low" place */ + PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType); + + if (PhysicalBase == NULL) + { + //Print(L"Failed to alloc pages for image %s\n", FileName); + UiMessageBox("Failed to alloc pages for image"); + FsCloseFile(FileHandle); + return FALSE; + } + } + + /* This is the real image base - in form of a virtual address */ + VirtualBase = PaToVa(PhysicalBase); + + DbgPrint((DPRINT_WINDOWS, "Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase)); + + /* Set to 0 position and fully load the file image */ + FsSetFilePointer(FileHandle, 0); + + Status = FsReadFile(FileHandle, NtHeaders->OptionalHeader.SizeOfHeaders, NULL, PhysicalBase); + + if (!Status) + { + //Print(L"Error reading headers %s\n", FileName); + UiMessageBox("Error reading headers"); + FsCloseFile(FileHandle); + return FALSE; + } + + /* Reload the NT Header */ + NtHeaders = RtlImageNtHeader(PhysicalBase); + + /* Load the first section */ + SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); + + /* Fill output parameters */ + *ImageBasePA = PhysicalBase; + + /* Walk through each section and read it (check/fix any possible + bad situations, if they arise) */ + for (i = 0; i < NumberOfSections; i++) + { + VirtualSize = SectionHeader->Misc.VirtualSize; + SizeOfRawData = SectionHeader->SizeOfRawData; + + /* Handle a case when VirtualSize equals 0 */ + if (VirtualSize == 0) + VirtualSize = SizeOfRawData; + + /* If PointerToRawData is 0, then force its size to be also 0 */ + if (SectionHeader->PointerToRawData == 0) + { + SizeOfRawData = 0; + } + else + { + /* Cut the loaded size to the VirtualSize extents */ + if (SizeOfRawData > VirtualSize) + SizeOfRawData = VirtualSize; + } + + /* Actually read the section (if its size is not 0) */ + if (SizeOfRawData != 0) + { + /* Seek to the correct position */ + FsSetFilePointer(FileHandle, SectionHeader->PointerToRawData); + + DbgPrint((DPRINT_WINDOWS, "SH->VA: 0x%X\n", SectionHeader->VirtualAddress)); + + /* Read this section from the file, size = SizeOfRawData */ + Status = FsReadFile(FileHandle, SizeOfRawData, &BytesRead, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress); + + if (!Status && (BytesRead == 0)) + { + DbgPrint((DPRINT_WINDOWS, "WinLdrLoadImage(): Error reading section from file!\n")); + break; + } + } + + /* Size of data is less than the virtual size - fill up the remainder with zeroes */ + if (SizeOfRawData < VirtualSize) + { + DbgPrint((DPRINT_WINDOWS, "WinLdrLoadImage(): SORD %d < VS %d", SizeOfRawData, VirtualSize)); + RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData); + } + + SectionHeader++; + } + + /* We are done with the file - close it */ + FsCloseFile(FileHandle); + + /* If loading failed - return right now */ + if (!Status) + return FALSE; + + + /* Relocate the image, if it needs it */ + if (NtHeaders->OptionalHeader.ImageBase != (ULONG)VirtualBase) + { + DbgPrint((DPRINT_WINDOWS, "Relocating %p -> %p\n", + NtHeaders->OptionalHeader.ImageBase, VirtualBase)); + Status = (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase, + (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase, + "FreeLdr", + TRUE, + TRUE, /* in case of conflict still return success */ + FALSE); + } + + return Status; +} + +/* PRIVATE FUNCTIONS *******************************************************/ + +/* DllName - physical, UnicodeString->Buffer - virtual */ +BOOLEAN +WinLdrpCompareDllName(IN PCH DllName, + IN PUNICODE_STRING UnicodeName) +{ + PWSTR Buffer; + UNICODE_STRING UnicodeNamePA; + ULONG i, Length; + + /* First obvious check: for length of two names */ + Length = strlen(DllName); + + UnicodeNamePA.Length = UnicodeName->Length; + UnicodeNamePA.MaximumLength = UnicodeName->MaximumLength; + UnicodeNamePA.Buffer = VaToPa(UnicodeName->Buffer); + DbgPrint((DPRINT_WINDOWS, "WinLdrpCompareDllName: %s and %wZ, Length = %d " + "UN->Length %d\n", DllName, &UnicodeNamePA, Length, UnicodeName->Length)); + + if ((Length * sizeof(WCHAR)) > UnicodeName->Length) + return FALSE; + + /* Store pointer to unicode string's buffer */ + Buffer = VaToPa(UnicodeName->Buffer); + + /* Loop character by character */ + for (i = 0; i < Length; i++) + { + /* Compare two characters, uppercasing them */ + if (toupper(*DllName) != toupper((CHAR)*Buffer)) + return FALSE; + + /* Move to the next character */ + DllName++; + Buffer++; + } + + /* Check, if strings either fully match, or match till the "." (w/o extension) */ + if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.')) + { + /* Yes they do */ + return TRUE; + } + + /* Strings don't match, return FALSE */ + return FALSE; +} + +BOOLEAN +WinLdrpBindImportName(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PVOID DllBase, + IN PVOID ImageBase, + IN PIMAGE_THUNK_DATA ThunkData, + IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, + IN ULONG ExportSize, + IN BOOLEAN ProcessForwards) +{ + ULONG Ordinal; + PULONG NameTable, FunctionTable; + PUSHORT OrdinalTable; + LONG High, Low, Middle, Result; + ULONG Hint; + + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n", + // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards)); + + /* Check passed DllBase param */ + if(DllBase == NULL) + { + DbgPrint((DPRINT_WINDOWS, "WARNING: DllBase == NULL!\n")); + return FALSE; + } + + /* Convert all non-critical pointers to PA from VA */ + ThunkData = VaToPa(ThunkData); + + /* Is the reference by ordinal? */ + if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards) + { + /* Yes, calculate the ordinal */ + Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base); + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): Ordinal %d\n", Ordinal)); + } + else + { + /* It's reference by name, we have to look it up in the export directory */ + if (!ProcessForwards) + { + /* AddressOfData in thunk entry will become a virtual address (from relative) */ + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData)); + ThunkData->u1.AddressOfData = + (ULONG)RVA(ImageBase, ThunkData->u1.AddressOfData); + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData)); + } + + /* Get pointers to Name and Ordinal tables (RVA -> VA) */ + NameTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames)); + OrdinalTable = (PUSHORT)VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals)); + + //DbgPrint((DPRINT_WINDOWS, "NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n", + // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals)); + + /* Get the hint, convert it to a physical pointer */ + Hint = ((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Hint; + //DbgPrint((DPRINT_WINDOWS, "HintIndex %d\n", Hint)); + + /* If Hint is less than total number of entries in the export directory, + and import name == export name, then we can just get it from the OrdinalTable */ + if ( + (Hint < ExportDirectory->NumberOfNames) && + ( + strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]), + (PCHAR)VaToPa( RVA(DllBase, NameTable[Hint])) ) == 0 + ) + ) + { + Ordinal = OrdinalTable[Hint]; + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): Ordinal %d\n", Ordinal)); + } + else + { + /* It's not the easy way, we have to lookup import name in the name table. + Let's use a binary search for this task. */ + + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName() looking up the import name using binary search...\n")); + + /* Low boundary is set to 0, and high boundary to the maximum index */ + Low = 0; + High = ExportDirectory->NumberOfNames - 1; + + /* Perform a binary-search loop */ + while (High >= Low) + { + /* Divide by 2 by shifting to the right once */ + Middle = (Low + High) >> 1; + + /* Compare the names */ + Result = strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]), + (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle]))); + + /*DbgPrint((DPRINT_WINDOWS, "Binary search: comparing Import '__', Export '%s'\n",*/ + /*VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),*/ + /*(PCHAR)VaToPa(RVA(DllBase, NameTable[Middle]))));*/ + + /*DbgPrint((DPRINT_WINDOWS, "TE->u1.AOD %p, fulladdr %p\n", + ThunkData->u1.AddressOfData, + ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name ));*/ + + + /* Depending on result of strcmp, perform different actions */ + if (Result < 0) + { + /* Adjust top boundary */ + High = Middle - 1; + } + else if (Result > 0) + { + /* Adjust bottom boundary */ + Low = Middle + 1; + } + else + { + /* Yay, found it! */ + break; + } + } + + /* If high boundary is less than low boundary, then no result found */ + if (High < Low) + { + //Print(L"Error in binary search\n"); + DbgPrint((DPRINT_WINDOWS, "Error in binary search!\n")); + return FALSE; + } + + /* Everything allright, get the ordinal */ + Ordinal = OrdinalTable[Middle]; + + //DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName() found Ordinal %d\n", Ordinal)); + } + } + + /* Check ordinal number for validity! */ + if (Ordinal >= ExportDirectory->NumberOfFunctions) + { + DbgPrint((DPRINT_WINDOWS, "Ordinal number is invalid!\n")); + return FALSE; + } + + /* Get a pointer to the function table */ + FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions)); + + /* Save a pointer to the function */ + ThunkData->u1.Function = (ULONG)RVA(DllBase, FunctionTable[Ordinal]); + + /* Is it a forwarder? (function pointer isn't within the export directory) */ + if (((ULONG)VaToPa((PVOID)ThunkData->u1.Function) > (ULONG)ExportDirectory) && + ((ULONG)VaToPa((PVOID)ThunkData->u1.Function) < ((ULONG)ExportDirectory + ExportSize))) + { + PLDR_DATA_TABLE_ENTRY DataTableEntry; + CHAR ForwardDllName[255]; + PIMAGE_EXPORT_DIRECTORY RefExportDirectory; + ULONG RefExportSize; + + /* Save the name of the forward dll */ + RtlCopyMemory(ForwardDllName, (PCHAR)VaToPa((PVOID)ThunkData->u1.Function), sizeof(ForwardDllName)); + + /* Strip out its extension */ + *strchr(ForwardDllName,'.') = '\0'; + + DbgPrint((DPRINT_WINDOWS, "WinLdrpBindImportName(): ForwardDllName %s\n", ForwardDllName)); + if (!WinLdrCheckForLoadedDll(WinLdrBlock, ForwardDllName, &DataTableEntry)) + { + /* We can't continue if DLL couldn't be loaded, so bomb out with an error */ + //Print(L"Error loading DLL!\n"); + DbgPrint((DPRINT_WINDOWS, "Error loading DLL!\n")); + return FALSE; + } + + /* Get pointer to the export directory of loaded DLL */ + RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY) + RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase), + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &RefExportSize); + + /* Fail if it's NULL */ + if (RefExportDirectory) + { + UCHAR Buffer[128]; + IMAGE_THUNK_DATA RefThunkData; + PIMAGE_IMPORT_BY_NAME ImportByName; + PCHAR ImportName; + BOOLEAN Status; + + /* Get pointer to the import name */ + ImportName = strchr((PCHAR)VaToPa((PVOID)ThunkData->u1.Function), '.') + 1; + + /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */ + ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer; + + /* Fill the name with the import name */ + RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1); + + /* Set Hint to 0 */ + ImportByName->Hint = 0; + + /* And finally point ThunkData's AddressOfData to that structure */ + RefThunkData.u1.AddressOfData = (ULONG)ImportByName; + + /* And recursively call ourselves */ + Status = WinLdrpBindImportName( + WinLdrBlock, + DataTableEntry->DllBase, + ImageBase, + &RefThunkData, + RefExportDirectory, + RefExportSize, + TRUE); + + /* Fill out the ThunkData with data from RefThunkData */ + ThunkData->u1 = RefThunkData.u1; + + /* Return what we got from the recursive call */ + return Status; + } + else + { + /* Fail if ExportDirectory is NULL */ + return FALSE; + } + } + + /* Success! */ + return TRUE; +} + +BOOLEAN +WinLdrpLoadAndScanReferencedDll(PLOADER_PARAMETER_BLOCK WinLdrBlock, + PCCH DirectoryPath, + PCH ImportName, + PLDR_DATA_TABLE_ENTRY *DataTableEntry) +{ + CHAR FullDllName[256]; + BOOLEAN Status; + PVOID BasePA; + + /* Prepare the full path to the file to be loaded */ + strcpy(FullDllName, DirectoryPath); + strcat(FullDllName, ImportName); + + DbgPrint((DPRINT_WINDOWS, "Loading referenced DLL: %s\n", FullDllName)); + //Print(L"Loading referenced DLL: %s\n", FullDllName); + + /* Load the image */ + Status = WinLdrLoadImage(FullDllName, LoaderHalCode, &BasePA); + + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, "WinLdrLoadImage() failed\n")); + return Status; + } + + /* Allocate DTE for newly loaded DLL */ + Status = WinLdrAllocateDataTableEntry(WinLdrBlock, + ImportName, + FullDllName, + BasePA, + DataTableEntry); + + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, + "WinLdrAllocateDataTableEntry() failed with Status=0x%X\n", Status)); + return Status; + } + + /* Scan its dependencies too */ + DbgPrint((DPRINT_WINDOWS, + "WinLdrScanImportDescriptorTable() calling ourselves for %S\n", + VaToPa((*DataTableEntry)->BaseDllName.Buffer))); + Status = WinLdrScanImportDescriptorTable(WinLdrBlock, DirectoryPath, *DataTableEntry); + + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, + "WinLdrScanImportDescriptorTable() failed with Status=0x%X\n", Status)); + return Status; + } + + return TRUE; +} + +BOOLEAN +WinLdrpScanImportAddressTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock, + IN PVOID DllBase, + IN PVOID ImageBase, + IN PIMAGE_THUNK_DATA ThunkData) +{ + PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; + BOOLEAN Status; + ULONG ExportSize; + + DbgPrint((DPRINT_WINDOWS, "WinLdrpScanImportAddressTable(): DllBase 0x%X, " + "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData)); + + /* Obtain the export table from the DLL's base */ + if (DllBase == NULL) + { + //Print(L"Error, DllBase == NULL!\n"); + return FALSE; + } + else + { + ExportDirectory = + (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase), + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &ExportSize); + } + + DbgPrint((DPRINT_WINDOWS, "WinLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory)); + + /* If pointer to Export Directory is */ + if (ExportDirectory == NULL) + return FALSE; + + /* Go through each entry in the thunk table and bind it */ + while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0) + { + /* Bind it */ + Status = WinLdrpBindImportName( + WinLdrBlock, + DllBase, + ImageBase, + ThunkData, + ExportDirectory, + ExportSize, + FALSE); + + /* Move to the next entry */ + ThunkData++; + + /* Return error if binding was unsuccessful */ + if (!Status) + return Status; + } + + /* Return success */ + return TRUE; +} diff --git a/reactos/boot/freeldr/freeldr/windows/winldr.c b/reactos/boot/freeldr/freeldr/windows/winldr.c index 622d258d8f7..4bd5ee5002f 100644 --- a/reactos/boot/freeldr/freeldr/windows/winldr.c +++ b/reactos/boot/freeldr/freeldr/windows/winldr.c @@ -364,55 +364,55 @@ WinLdrLoadBootDrivers(PLOADER_PARAMETER_BLOCK LoaderBlock, PVOID WinLdrLoadModule(PCSTR ModuleName, ULONG *Size, TYPE_OF_MEMORY MemoryType) { - PFILE FileHandle; - PVOID PhysicalBase; - ULONG FileSize; - BOOLEAN Status; - - //CHAR ProgressString[256]; - - /* Inform user we are loading files */ - //sprintf(ProgressString, "Loading %s...", FileName); - //UiDrawProgressBarCenter(1, 100, ProgressString); - - DbgPrint((DPRINT_WINDOWS, "Loading module %s\n", ModuleName)); - *Size = 0; - - /* Open the image file */ - FileHandle = FsOpenFile(ModuleName); - - if (FileHandle == NULL) - { - /* In case of errors, we just return, without complaining to the user */ - return NULL; - } - - /* Get this file's size */ - FileSize = FsGetFileSize(FileHandle); - *Size = FileSize; - - /* Allocate memory */ - PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType); - if (PhysicalBase == NULL) - { - FsCloseFile(FileHandle); - return NULL; - } - - /* Load whole file */ - Status = FsReadFile(FileHandle, FileSize, NULL, PhysicalBase); - if (!Status) - { - FsCloseFile(FileHandle); - return NULL; - } - - DbgPrint((DPRINT_WINDOWS, "Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize)); - - /* We are done with the file - close it */ - FsCloseFile(FileHandle); - - return PhysicalBase; + PFILE FileHandle; + PVOID PhysicalBase; + ULONG FileSize; + BOOLEAN Status; + + //CHAR ProgressString[256]; + + /* Inform user we are loading files */ + //sprintf(ProgressString, "Loading %s...", FileName); + //UiDrawProgressBarCenter(1, 100, ProgressString); + + DbgPrint((DPRINT_WINDOWS, "Loading module %s\n", ModuleName)); + *Size = 0; + + /* Open the image file */ + FileHandle = FsOpenFile(ModuleName); + + if (FileHandle == NULL) + { + /* In case of errors, we just return, without complaining to the user */ + return NULL; + } + + /* Get this file's size */ + FileSize = FsGetFileSize(FileHandle); + *Size = FileSize; + + /* Allocate memory */ + PhysicalBase = MmAllocateMemoryWithType(FileSize, MemoryType); + if (PhysicalBase == NULL) + { + FsCloseFile(FileHandle); + return NULL; + } + + /* Load whole file */ + Status = FsReadFile(FileHandle, FileSize, NULL, PhysicalBase); + if (!Status) + { + FsCloseFile(FileHandle); + return NULL; + } + + DbgPrint((DPRINT_WINDOWS, "Loaded %s at 0x%x with size 0x%x\n", ModuleName, PhysicalBase, FileSize)); + + /* We are done with the file - close it */ + FsCloseFile(FileHandle); + + return PhysicalBase; } diff --git a/reactos/boot/freeldr/freeldr/windows/wlmemory.c b/reactos/boot/freeldr/freeldr/windows/wlmemory.c index 6ae0d1dbca6..b8d5d435863 100644 --- a/reactos/boot/freeldr/freeldr/windows/wlmemory.c +++ b/reactos/boot/freeldr/freeldr/windows/wlmemory.c @@ -1,960 +1,960 @@ -/* - * PROJECT: EFI Windows Loader - * LICENSE: GPL - See COPYING in the top level directory - * FILE: freeldr/winldr/wlmemory.c - * PURPOSE: Memory related routines - * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) - */ - -/* INCLUDES ***************************************************************/ - -#include - -#include -#include - -extern ULONG TotalNLSSize; -extern ULONG LoaderPagesSpanned; - -// This is needed because headers define wrong one for ReactOS -#undef KIP0PCRADDRESS -#define KIP0PCRADDRESS 0xffdff000 - -#define HYPER_SPACE_ENTRY 0x300 - -PCHAR MemTypeDesc[] = { - "ExceptionBlock ", // ? - "SystemBlock ", // ? - "Free ", - "Bad ", // used - "LoadedProgram ", // == Free - "FirmwareTemporary ", // == Free - "FirmwarePermanent ", // == Bad - "OsloaderHeap ", // used - "OsloaderStack ", // == Free - "SystemCode ", - "HalCode ", - "BootDriver ", // not used - "ConsoleInDriver ", // ? - "ConsoleOutDriver ", // ? - "StartupDpcStack ", // ? - "StartupKernelStack", // ? - "StartupPanicStack ", // ? - "StartupPcrPage ", // ? - "StartupPdrPage ", // ? - "RegistryData ", // used - "MemoryData ", // not used - "NlsData ", // used - "SpecialMemory ", // == Bad - "BBTMemory " // == Bad - }; - -VOID -WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock); - - -VOID -MempAddMemoryBlock(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, - ULONG BasePage, - ULONG PageCount, - ULONG Type); -VOID -WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, - IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor); - -VOID -WinLdrRemoveDescriptor(IN PMEMORY_ALLOCATION_DESCRIPTOR Descriptor); - -VOID -WinLdrSetProcessorContext(PVOID GdtIdt, IN ULONG Pcr, IN ULONG Tss); - -// This is needed only for SetProcessorContext routine -#pragma pack(2) - typedef struct - { - USHORT Limit; - ULONG Base; - } GDTIDT; -#pragma pack(4) - -// this is needed for new IDT filling -#if 0 -extern ULONG_PTR i386DivideByZero; -extern ULONG_PTR i386DebugException; -extern ULONG_PTR i386NMIException; -extern ULONG_PTR i386Breakpoint; -extern ULONG_PTR i386Overflow; -extern ULONG_PTR i386BoundException; -extern ULONG_PTR i386InvalidOpcode; -extern ULONG_PTR i386FPUNotAvailable; -extern ULONG_PTR i386DoubleFault; -extern ULONG_PTR i386CoprocessorSegment; -extern ULONG_PTR i386InvalidTSS; -extern ULONG_PTR i386SegmentNotPresent; -extern ULONG_PTR i386StackException; -extern ULONG_PTR i386GeneralProtectionFault; -extern ULONG_PTR i386PageFault; // exc 14 -extern ULONG_PTR i386CoprocessorError; // exc 16 -extern ULONG_PTR i386AlignmentCheck; // exc 17 -#endif - -/* GLOBALS ***************************************************************/ - -PHARDWARE_PTE PDE; -PHARDWARE_PTE HalPageTable; - -PUCHAR PhysicalPageTablesBuffer; -PUCHAR KernelPageTablesBuffer; -ULONG PhysicalPageTables; -ULONG KernelPageTables; - -MEMORY_ALLOCATION_DESCRIPTOR *Mad; -ULONG MadCount = 0; - - -/* FUNCTIONS **************************************************************/ - -BOOLEAN -MempAllocatePageTables() -{ - ULONG NumPageTables, TotalSize; - PUCHAR Buffer; - // It's better to allocate PDE + PTEs contigiuos - - // Max number of entries = MaxPageNum >> 10 - // FIXME: This is a number to describe ALL physical memory - // and windows doesn't expect ALL memory mapped... - NumPageTables = (GetSystemMemorySize() >> MM_PAGE_SHIFT) >> 10; - - DbgPrint((DPRINT_WINDOWS, "NumPageTables = %d\n", NumPageTables)); - - // Allocate memory block for all these things: - // PDE, HAL mapping page table, physical mapping, kernel mapping - TotalSize = (1+1+NumPageTables*2)*MM_PAGE_SIZE; - - // PDE+HAL+KernelPTEs == MemoryData - Buffer = MmAllocateMemoryWithType( - TotalSize - NumPageTables*MM_PAGE_SIZE, LoaderMemoryData); - - // Physical PTEs = FirmwareTemporary - PhysicalPageTablesBuffer = MmAllocateMemoryWithType( - NumPageTables*MM_PAGE_SIZE, LoaderFirmwareTemporary); - - if (Buffer + (TotalSize - NumPageTables*MM_PAGE_SIZE) != - PhysicalPageTablesBuffer) - { - DbgPrint((DPRINT_WINDOWS, "There was a problem allocating two adjacent blocks of memory!")); - } - - if (Buffer == NULL || PhysicalPageTablesBuffer == NULL) - { - UiMessageBox("Impossible to allocate memory block for page tables!"); - return FALSE; - } - - // Zero all this memory block - RtlZeroMemory(Buffer, TotalSize); - - // Set up pointers correctly now - PDE = (PHARDWARE_PTE)Buffer; - - // Map the page directory at 0xC0000000 (maps itself) - PDE[HYPER_SPACE_ENTRY].PageFrameNumber = (ULONG)PDE >> MM_PAGE_SHIFT; - PDE[HYPER_SPACE_ENTRY].Valid = 1; - PDE[HYPER_SPACE_ENTRY].Write = 1; - - // The last PDE slot is allocated for HAL's memory mapping (Virtual Addresses 0xFFC00000 - 0xFFFFFFFF) - HalPageTable = (PHARDWARE_PTE)&Buffer[MM_PAGE_SIZE*1]; - - // Map it - PDE[1023].PageFrameNumber = (ULONG)HalPageTable >> MM_PAGE_SHIFT; - PDE[1023].Valid = 1; - PDE[1023].Write = 1; - - // Store pointer to the table for easier access - KernelPageTablesBuffer = &Buffer[MM_PAGE_SIZE*2]; - - // Zero counters of page tables used - PhysicalPageTables = 0; - KernelPageTables = 0; - - return TRUE; -} - -VOID -MempAllocatePTE(ULONG Entry, PHARDWARE_PTE *PhysicalPT, PHARDWARE_PTE *KernelPT) -{ - //Print(L"Creating PDE Entry %X\n", Entry); - - // Identity mapping - *PhysicalPT = (PHARDWARE_PTE)&PhysicalPageTablesBuffer[PhysicalPageTables*MM_PAGE_SIZE]; - PhysicalPageTables++; - - PDE[Entry].PageFrameNumber = (ULONG)*PhysicalPT >> MM_PAGE_SHIFT; - PDE[Entry].Valid = 1; - PDE[Entry].Write = 1; - - if (Entry+(KSEG0_BASE >> 22) > 1023) - { - DbgPrint((DPRINT_WINDOWS, "WARNING! Entry: %X > 1023\n", Entry+(KSEG0_BASE >> 22))); - } - - // Kernel-mode mapping - *KernelPT = (PHARDWARE_PTE)&KernelPageTablesBuffer[KernelPageTables*MM_PAGE_SIZE]; - KernelPageTables++; - - PDE[Entry+(KSEG0_BASE >> 22)].PageFrameNumber = ((ULONG)*KernelPT >> MM_PAGE_SHIFT); - PDE[Entry+(KSEG0_BASE >> 22)].Valid = 1; - PDE[Entry+(KSEG0_BASE >> 22)].Write = 1; -} - -BOOLEAN -MempSetupPaging(IN ULONG StartPage, - IN ULONG NumberOfPages) -{ - PHARDWARE_PTE PhysicalPT; - PHARDWARE_PTE KernelPT; - ULONG Entry, Page; - - //Print(L"MempSetupPaging: SP 0x%X, Number: 0x%X\n", StartPage, NumberOfPages); - - // HACK - if (StartPage+NumberOfPages >= 0x80000) - { - // - // We can't map this as it requires more than 1 PDE - // and in fact it's not possible at all ;) - // - //Print(L"skipping...\n"); - return TRUE; - } - - // - // Now actually set up the page tables for identity mapping - // - for (Page=StartPage; Page < StartPage+NumberOfPages; Page++) - { - Entry = Page >> 10; - - if (((PULONG)PDE)[Entry] == 0) - { - MempAllocatePTE(Entry, &PhysicalPT, &KernelPT); - } - else - { - PhysicalPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT); - KernelPT = (PHARDWARE_PTE)(PDE[Entry+(KSEG0_BASE >> 22)].PageFrameNumber << MM_PAGE_SHIFT); - } - - if (Page == 0) - { - PhysicalPT[Page & 0x3ff].PageFrameNumber = Page; - PhysicalPT[Page & 0x3ff].Valid = 0; - PhysicalPT[Page & 0x3ff].Write = 0; - - KernelPT[Page & 0x3ff].PageFrameNumber = Page; - KernelPT[Page & 0x3ff].Valid = 0; - KernelPT[Page & 0x3ff].Write = 0; - } - else - { - PhysicalPT[Page & 0x3ff].PageFrameNumber = Page; - PhysicalPT[Page & 0x3ff].Valid = 1; - PhysicalPT[Page & 0x3ff].Write = 1; - - KernelPT[Page & 0x3ff].PageFrameNumber = Page; - KernelPT[Page & 0x3ff].Valid = 1; - KernelPT[Page & 0x3ff].Write = 1; - } - } - - return TRUE; -} - -VOID -MempDisablePages() -{ - int i; - - // - // We need to delete kernel mapping from memory areas which are - // marked as Special or Permanent memory (thus non-accessible) - // - - for (i=0; i LoaderPagesSpanned) - EndPage = LoaderPagesSpanned; - } - - for (Page = StartPage; Page < EndPage; Page++) - { - PHARDWARE_PTE KernelPT; - ULONG Entry = (Page >> 10) + (KSEG0_BASE >> 22); - - if (PDE[Entry].Valid) - { - KernelPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT); - - if (KernelPT) - { - KernelPT[Page & 0x3ff].PageFrameNumber = 0; - KernelPT[Page & 0x3ff].Valid = 0; - KernelPT[Page & 0x3ff].Write = 0; - } - } - } - } - } -} - -VOID -MempAddMemoryBlock(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, - ULONG BasePage, - ULONG PageCount, - ULONG Type) -{ - BOOLEAN Status; - - // - // Check for some weird stuff at the top - // - if (BasePage + PageCount > 0xF0000) - { - // - // Just skip this, without even adding to MAD list - // - return; - } - - // - // Set Base page, page count and type - // - Mad[MadCount].BasePage = BasePage; - Mad[MadCount].PageCount = PageCount; - Mad[MadCount].MemoryType = Type; - - // - // Check if it's more than the allowed for OS loader - // if yes - don't map the pages, just add as FirmwareTemporary - // - if (BasePage + PageCount > LoaderPagesSpanned) - { - if (Mad[MadCount].MemoryType != LoaderSpecialMemory && - Mad[MadCount].MemoryType != LoaderFirmwarePermanent && - Mad[MadCount].MemoryType != LoaderFree) - { - DbgPrint((DPRINT_WINDOWS, "Setting page %x %x to Temporary from %d\n", - BasePage, PageCount, Mad[MadCount].MemoryType)); - Mad[MadCount].MemoryType = LoaderFirmwareTemporary; - } - - WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); - MadCount++; - - return; - } - - // - // Add descriptor - // - WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); - MadCount++; - - // - // Map it (don't map low 1Mb because it was already contigiously - // mapped in WinLdrTurnOnPaging) - // - if (BasePage >= 0x100) - { - Status = MempSetupPaging(BasePage, PageCount); - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, "Error during MempSetupPaging\n")); - return; - } - } -} - -#ifdef _M_IX86 - -BOOLEAN LocalAPIC = FALSE; -ULONG_PTR APICAddress = 0; - -VOID -WinLdrpMapApic() -{ - /* Check if we have a local APIC */ - asm(".intel_syntax noprefix\n"); - asm("mov eax, 1\n"); - asm("cpuid\n"); - asm("shr edx, 9\n"); - asm("and edx, 0x1\n"); - asm("mov _LocalAPIC, edx\n"); - asm(".att_syntax\n"); - - /* If there is no APIC, just return */ - if (!LocalAPIC) - return; - - asm(".intel_syntax noprefix\n"); - asm("mov ecx, 0x1B\n"); - asm("rdmsr\n"); - asm("mov edx, eax\n"); - asm("and edx, 0xFFFFF000\n"); - asm("mov _APICAddress, edx"); - asm(".att_syntax\n"); - - DbgPrint((DPRINT_WINDOWS, "Local APIC detected at address 0x%x\n", - APICAddress)); - - /* Map it */ - HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber - = APICAddress >> MM_PAGE_SHIFT; - HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1; - HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1; - HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].WriteThrough = 1; - HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].CacheDisable = 1; -} -#else -VOID -WinLdrpMapApic() -{ - /* Implement it for another arch */ -} -#endif - -BOOLEAN -WinLdrTurnOnPaging(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, - ULONG PcrBasePage, - ULONG TssBasePage, - PVOID GdtIdt) -{ - ULONG i, PagesCount, MemoryMapSizeInPages; - ULONG LastPageIndex, LastPageType, MemoryMapStartPage; - PPAGE_LOOKUP_TABLE_ITEM MemoryMap; - ULONG NoEntries; - PKTSS Tss; - BOOLEAN Status; - - // - // Creating a suitable memory map for the Windows can be tricky, so let's - // give a few advices: - // 1) One must not map the whole available memory pages to PDE! - // Map only what's needed - 16Mb, 24Mb, 32Mb max I think, - // thus occupying 4, 6 or 8 PDE entries for identical mapping, - // the same quantity for KSEG0_BASE mapping, one more entry for - // hyperspace and one more entry for HAL physical pages mapping. - // 2) Memory descriptors must map *the whole* physical memory - // showing any memory above 16/24/32 as FirmwareTemporary - // - // 3) Overall memory blocks count must not exceed 30 (?? why?) - // - - // - // During MmInitMachineDependent, the kernel zeroes PDE at the following address - // 0xC0300000 - 0xC03007FC - // - // Then it finds the best place for non-paged pool: - // StartPde C0300F70, EndPde C0300FF8, NumberOfPages C13, NextPhysPage 3AD - // - - // Before we start mapping pages, create a block of memory, which will contain - // PDE and PTEs - if (MempAllocatePageTables() == FALSE) - return FALSE; - - // Allocate memory for memory allocation descriptors - Mad = MmHeapAlloc(sizeof(MEMORY_ALLOCATION_DESCRIPTOR) * 1024); - - // Setup an entry for each descriptor - MemoryMap = MmGetMemoryMap(&NoEntries); - if (MemoryMap == NULL) - { - UiMessageBox("Can not retrieve the current memory map"); - return FALSE; - } - - // Calculate parameters of the memory map - MemoryMapStartPage = (ULONG_PTR)MemoryMap >> MM_PAGE_SHIFT; - MemoryMapSizeInPages = NoEntries * sizeof(PAGE_LOOKUP_TABLE_ITEM); - - DbgPrint((DPRINT_WINDOWS, "Got memory map with %d entries\n", NoEntries)); - - // Always contigiously map low 1Mb of memory - Status = MempSetupPaging(0, 0x100); - if (!Status) - { - DbgPrint((DPRINT_WINDOWS, "Error during MempSetupPaging of low 1Mb\n")); - return FALSE; - } - - // Construct a good memory map from what we've got, - // but mark entries which the memory allocation bitmap takes - // as free entries (this is done in order to have the ability - // to place mem alloc bitmap outside lower 16Mb zone) - PagesCount = 1; - LastPageIndex = 0; - LastPageType = MemoryMap[0].PageAllocated; - for(i=1;i= MemoryMapStartPage && - i < (MemoryMapStartPage+MemoryMapSizeInPages)) - { - // Exclude it if current page belongs to the memory map - MemoryMap[i].PageAllocated = LoaderFree; - } - - // Process entry - if (MemoryMap[i].PageAllocated == LastPageType && - (i != NoEntries-1) ) - { - PagesCount++; - } - else - { - // Add the resulting region - MempAddMemoryBlock(LoaderBlock, LastPageIndex, PagesCount, LastPageType); - - // Reset our counter vars - LastPageIndex = i; - LastPageType = MemoryMap[i].PageAllocated; - PagesCount = 1; - } - } - - // TEMP, DEBUG! - // adding special reserved memory zones for vmware workstation -#if 0 - { - Mad[MadCount].BasePage = 0xfec00; - Mad[MadCount].PageCount = 0x10; - Mad[MadCount].MemoryType = LoaderSpecialMemory; - WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); - MadCount++; - - Mad[MadCount].BasePage = 0xfee00; - Mad[MadCount].PageCount = 0x1; - Mad[MadCount].MemoryType = LoaderSpecialMemory; - WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); - MadCount++; - - Mad[MadCount].BasePage = 0xfffe0; - Mad[MadCount].PageCount = 0x20; - Mad[MadCount].MemoryType = LoaderSpecialMemory; - WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); - MadCount++; - } -#endif - - DbgPrint((DPRINT_WINDOWS, "MadCount: %d\n", MadCount)); - - WinLdrpDumpMemoryDescriptors(LoaderBlock); //FIXME: Delete! - - // Map our loader image, so we can continue running - /*Status = MempSetupPaging(OsLoaderBase >> MM_PAGE_SHIFT, OsLoaderSize >> MM_PAGE_SHIFT); - if (!Status) - { - UiMessageBox("Error during MempSetupPaging"); - return; - }*/ - - //VideoDisplayString(L"Hello from VGA, going into the kernel\n"); - DbgPrint((DPRINT_WINDOWS, "HalPageTable: 0x%X\n", HalPageTable)); - - // Page Tables have been setup, make special handling for PCR and TSS - // (which is done in BlSetupFotNt in usual ntldr) - HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber = PcrBasePage+1; - HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1; - HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1; - - HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber = PcrBasePage; - HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1; - HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1; - - // Map APIC - WinLdrpMapApic(); - - // Map VGA memory - //VideoMemoryBase = MmMapIoSpace(0xb8000, 4000, MmNonCached); - //DbgPrint((DPRINT_WINDOWS, "VideoMemoryBase: 0x%X\n", VideoMemoryBase)); - - Tss = (PKTSS)(KSEG0_BASE | (TssBasePage << MM_PAGE_SHIFT)); - - // Unmap what is not needed from kernel page table - MempDisablePages(); - - // Fill the memory descriptor list and - //PrepareMemoryDescriptorList(); - DbgPrint((DPRINT_WINDOWS, "Memory Descriptor List prepared, printing PDE\n")); - List_PaToVa(&LoaderBlock->MemoryDescriptorListHead); - -#ifdef DBG - { - ULONG *PDE_Addr=(ULONG *)PDE;//0xC0300000; - int j; - - DbgPrint((DPRINT_WINDOWS, "\nPDE\n")); - - for (i=0; i<128; i++) - { - DbgPrint((DPRINT_WINDOWS, "0x%04X | ", i*8)); - - for (j=0; j<8; j++) - { - DbgPrint((DPRINT_WINDOWS, "0x%08X ", PDE_Addr[i*8+j])); - } - - DbgPrint((DPRINT_WINDOWS, "\n")); - } - } -#endif - - - // Enable paging - //BS->ExitBootServices(ImageHandle,MapKey); - - // Disable Interrupts - _disable(); - - // Re-initalize EFLAGS - Ke386EraseFlags(); - - // Set the PDBR - __writecr3((ULONG_PTR)PDE); - - // Enable paging by modifying CR0 - __writecr0(__readcr0() | CR0_PG); - - // Set processor context - WinLdrSetProcessorContext(GdtIdt, KIP0PCRADDRESS, KSEG0_BASE | (TssBasePage << MM_PAGE_SHIFT)); - - // Zero KI_USER_SHARED_DATA page - memset((PVOID)KI_USER_SHARED_DATA, 0, MM_PAGE_SIZE); - - return TRUE; -} - -// Two special things this func does: it sorts descriptors, -// and it merges free ones -VOID -WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, - IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor) -{ - PLIST_ENTRY ListHead = &LoaderBlock->MemoryDescriptorListHead; - PLIST_ENTRY PreviousEntry, NextEntry; - PMEMORY_ALLOCATION_DESCRIPTOR PreviousDescriptor = NULL, NextDescriptor = NULL; - - DbgPrint((DPRINT_WINDOWS, "BP=0x%X PC=0x%X %s\n", NewDescriptor->BasePage, - NewDescriptor->PageCount, MemTypeDesc[NewDescriptor->MemoryType])); - - /* Find a place where to insert the new descriptor to */ - PreviousEntry = ListHead; - NextEntry = ListHead->Flink; - while (NextEntry != ListHead) - { - NextDescriptor = CONTAINING_RECORD(NextEntry, - MEMORY_ALLOCATION_DESCRIPTOR, - ListEntry); - if (NewDescriptor->BasePage < NextDescriptor->BasePage) - break; - - PreviousEntry = NextEntry; - PreviousDescriptor = NextDescriptor; - NextEntry = NextEntry->Flink; - } - - /* Don't forget about merging free areas */ - if (NewDescriptor->MemoryType != LoaderFree) - { - /* Just insert, nothing to merge */ - InsertHeadList(PreviousEntry, &NewDescriptor->ListEntry); - } - else - { - /* Previous block also free? */ - if ((PreviousEntry != ListHead) && (PreviousDescriptor->MemoryType == LoaderFree) && - ((PreviousDescriptor->BasePage + PreviousDescriptor->PageCount) == - NewDescriptor->BasePage)) - { - /* Just enlarge previous descriptor's PageCount */ - PreviousDescriptor->PageCount += NewDescriptor->PageCount; - NewDescriptor = PreviousDescriptor; - } - else - { - /* Nope, just insert */ - InsertHeadList(PreviousEntry, &NewDescriptor->ListEntry); - } - - /* Next block is free ?*/ - if ((NextEntry != ListHead) && - (NextDescriptor->MemoryType == LoaderFree) && - ((NewDescriptor->BasePage + NewDescriptor->PageCount) == NextDescriptor->BasePage)) - { - /* Enlarge next descriptor's PageCount */ - NewDescriptor->PageCount += NextDescriptor->PageCount; - RemoveEntryList(&NextDescriptor->ListEntry); - } - } - - return; -} - -VOID -WinLdrSetProcessorContext(PVOID GdtIdt, IN ULONG Pcr, IN ULONG Tss) -{ - GDTIDT GdtDesc, IdtDesc, OldIdt; - PKGDTENTRY pGdt; - PKIDTENTRY pIdt; - ULONG Ldt = 0; - //ULONG i; - - DbgPrint((DPRINT_WINDOWS, "GDtIdt %p, Pcr %p, Tss 0x%08X\n", - GdtIdt, Pcr, Tss)); - - // Kernel expects the PCR to be zero-filled on startup - // FIXME: Why zero it here when we can zero it right after allocation? - RtlZeroMemory((PVOID)Pcr, MM_PAGE_SIZE); //FIXME: Why zero only 1 page when we allocate 2? - - // Get old values of GDT and IDT - Ke386GetGlobalDescriptorTable(GdtDesc); - Ke386GetInterruptDescriptorTable(IdtDesc); - - // Save old IDT - OldIdt.Base = IdtDesc.Base; - OldIdt.Limit = IdtDesc.Limit; - - // Prepare new IDT+GDT - GdtDesc.Base = KSEG0_BASE | (ULONG_PTR)GdtIdt; - GdtDesc.Limit = NUM_GDT * sizeof(KGDTENTRY) - 1; - IdtDesc.Base = (ULONG)((PUCHAR)GdtDesc.Base + GdtDesc.Limit + 1); - IdtDesc.Limit = NUM_IDT * sizeof(KIDTENTRY) - 1; - - // ======================== - // Fill all descriptors now - // ======================== - - pGdt = (PKGDTENTRY)GdtDesc.Base; - pIdt = (PKIDTENTRY)IdtDesc.Base; - - // - // Code selector (0x8) - // Flat 4Gb - // - pGdt[1].LimitLow = 0xFFFF; - pGdt[1].BaseLow = 0; - pGdt[1].HighWord.Bytes.BaseMid = 0; - pGdt[1].HighWord.Bytes.Flags1 = 0x9A; - pGdt[1].HighWord.Bytes.Flags2 = 0xCF; - pGdt[1].HighWord.Bytes.BaseHi = 0; - - // - // Data selector (0x10) - // Flat 4Gb - // - pGdt[2].LimitLow = 0xFFFF; - pGdt[2].BaseLow = 0; - pGdt[2].HighWord.Bytes.BaseMid = 0; - pGdt[2].HighWord.Bytes.Flags1 = 0x92; - pGdt[2].HighWord.Bytes.Flags2 = 0xCF; - pGdt[2].HighWord.Bytes.BaseHi = 0; - - // - // Selector (0x18) - // Flat 2Gb - // - pGdt[3].LimitLow = 0xFFFF; - pGdt[3].BaseLow = 0; - pGdt[3].HighWord.Bytes.BaseMid = 0; - pGdt[3].HighWord.Bytes.Flags1 = 0xFA; - pGdt[3].HighWord.Bytes.Flags2 = 0xCF; - pGdt[3].HighWord.Bytes.BaseHi = 0; - - // - // Selector (0x20) - // Flat 2Gb - // - pGdt[4].LimitLow = 0xFFFF; - pGdt[4].BaseLow = 0; - pGdt[4].HighWord.Bytes.BaseMid = 0; - pGdt[4].HighWord.Bytes.Flags1 = 0xF2; - pGdt[4].HighWord.Bytes.Flags2 = 0xCF; - pGdt[4].HighWord.Bytes.BaseHi = 0; - - // - // TSS Selector (0x28) - // - pGdt[5].LimitLow = 0x78-1; //FIXME: Check this - pGdt[5].BaseLow = (USHORT)(Tss & 0xffff); - pGdt[5].HighWord.Bytes.BaseMid = (UCHAR)((Tss >> 16) & 0xff); - pGdt[5].HighWord.Bytes.Flags1 = 0x89; - pGdt[5].HighWord.Bytes.Flags2 = 0x00; - pGdt[5].HighWord.Bytes.BaseHi = (UCHAR)((Tss >> 24) & 0xff); - - // - // PCR Selector (0x30) - // - pGdt[6].LimitLow = 0x01; - pGdt[6].BaseLow = (USHORT)(Pcr & 0xffff); - pGdt[6].HighWord.Bytes.BaseMid = (UCHAR)((Pcr >> 16) & 0xff); - pGdt[6].HighWord.Bytes.Flags1 = 0x92; - pGdt[6].HighWord.Bytes.Flags2 = 0xC0; - pGdt[6].HighWord.Bytes.BaseHi = (UCHAR)((Pcr >> 24) & 0xff); - - // - // Selector (0x38) - // - pGdt[7].LimitLow = 0xFFFF; - pGdt[7].BaseLow = 0; - pGdt[7].HighWord.Bytes.BaseMid = 0; - pGdt[7].HighWord.Bytes.Flags1 = 0xF3; - pGdt[7].HighWord.Bytes.Flags2 = 0x40; - pGdt[7].HighWord.Bytes.BaseHi = 0; - - // - // Some BIOS stuff (0x40) - // - pGdt[8].LimitLow = 0xFFFF; - pGdt[8].BaseLow = 0x400; - pGdt[8].HighWord.Bytes.BaseMid = 0; - pGdt[8].HighWord.Bytes.Flags1 = 0xF2; - pGdt[8].HighWord.Bytes.Flags2 = 0x0; - pGdt[8].HighWord.Bytes.BaseHi = 0; - - // - // Selector (0x48) - // - pGdt[9].LimitLow = 0; - pGdt[9].BaseLow = 0; - pGdt[9].HighWord.Bytes.BaseMid = 0; - pGdt[9].HighWord.Bytes.Flags1 = 0; - pGdt[9].HighWord.Bytes.Flags2 = 0; - pGdt[9].HighWord.Bytes.BaseHi = 0; - - // - // Selector (0x50) - // - pGdt[10].LimitLow = 0xFFFF; //FIXME: Not correct! - pGdt[10].BaseLow = 0; - pGdt[10].HighWord.Bytes.BaseMid = 0x2; - pGdt[10].HighWord.Bytes.Flags1 = 0x89; - pGdt[10].HighWord.Bytes.Flags2 = 0; - pGdt[10].HighWord.Bytes.BaseHi = 0; - - // - // Selector (0x58) - // - pGdt[11].LimitLow = 0xFFFF; - pGdt[11].BaseLow = 0; - pGdt[11].HighWord.Bytes.BaseMid = 0x2; - pGdt[11].HighWord.Bytes.Flags1 = 0x9A; - pGdt[11].HighWord.Bytes.Flags2 = 0; - pGdt[11].HighWord.Bytes.BaseHi = 0; - - // - // Selector (0x60) - // - pGdt[12].LimitLow = 0xFFFF; - pGdt[12].BaseLow = 0; //FIXME: Maybe not correct, but noone cares - pGdt[12].HighWord.Bytes.BaseMid = 0x2; - pGdt[12].HighWord.Bytes.Flags1 = 0x92; - pGdt[12].HighWord.Bytes.Flags2 = 0; - pGdt[12].HighWord.Bytes.BaseHi = 0; - - // - // Video buffer Selector (0x68) - // - pGdt[13].LimitLow = 0x3FFF; - pGdt[13].BaseLow = 0x8000; - pGdt[13].HighWord.Bytes.BaseMid = 0x0B; - pGdt[13].HighWord.Bytes.Flags1 = 0x92; - pGdt[13].HighWord.Bytes.Flags2 = 0; - pGdt[13].HighWord.Bytes.BaseHi = 0; - - // - // Points to GDT (0x70) - // - pGdt[14].LimitLow = NUM_GDT*sizeof(KGDTENTRY) - 1; - pGdt[14].BaseLow = 0x7000; - pGdt[14].HighWord.Bytes.BaseMid = 0xFF; - pGdt[14].HighWord.Bytes.Flags1 = 0x92; - pGdt[14].HighWord.Bytes.Flags2 = 0; - pGdt[14].HighWord.Bytes.BaseHi = 0xFF; - - // - // Some unused descriptors should go here - // - - // Copy the old IDT - RtlCopyMemory(pIdt, (PVOID)OldIdt.Base, OldIdt.Limit); - - // Mask interrupts - //asm("cli\n"); // they are already masked before enabling paged mode - - // Load GDT+IDT - Ke386SetGlobalDescriptorTable(GdtDesc); - Ke386SetInterruptDescriptorTable(IdtDesc); - - // Jump to proper CS and clear prefetch queue - asm("ljmp $0x08, $mb1\n" - "mb1:\n"); - - // Set SS selector - asm(".intel_syntax noprefix\n"); - asm("mov ax, 0x10\n"); // DataSelector=0x10 - asm("mov ss, ax\n"); - asm(".att_syntax\n"); - - // Set DS and ES selectors - Ke386SetDs(0x10); - Ke386SetEs(0x10); // this is vital for rep stosd - - // LDT = not used ever, thus set to 0 - Ke386SetLocalDescriptorTable(Ldt); - - // Load TSR - Ke386SetTr(0x28); - - // Clear GS - asm(".intel_syntax noprefix\n"); - asm("push 0\n"); - asm("pop gs\n"); - asm(".att_syntax\n"); - - // Set FS to PCR - Ke386SetFs(0x30); - - // Real end of the function, just for information - /* do not uncomment! - pop edi; - pop esi; - pop ebx; - mov esp, ebp; - pop ebp; - ret - */ -} +/* + * PROJECT: EFI Windows Loader + * LICENSE: GPL - See COPYING in the top level directory + * FILE: freeldr/winldr/wlmemory.c + * PURPOSE: Memory related routines + * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org) + */ + +/* INCLUDES ***************************************************************/ + +#include + +#include +#include + +extern ULONG TotalNLSSize; +extern ULONG LoaderPagesSpanned; + +// This is needed because headers define wrong one for ReactOS +#undef KIP0PCRADDRESS +#define KIP0PCRADDRESS 0xffdff000 + +#define HYPER_SPACE_ENTRY 0x300 + +PCHAR MemTypeDesc[] = { + "ExceptionBlock ", // ? + "SystemBlock ", // ? + "Free ", + "Bad ", // used + "LoadedProgram ", // == Free + "FirmwareTemporary ", // == Free + "FirmwarePermanent ", // == Bad + "OsloaderHeap ", // used + "OsloaderStack ", // == Free + "SystemCode ", + "HalCode ", + "BootDriver ", // not used + "ConsoleInDriver ", // ? + "ConsoleOutDriver ", // ? + "StartupDpcStack ", // ? + "StartupKernelStack", // ? + "StartupPanicStack ", // ? + "StartupPcrPage ", // ? + "StartupPdrPage ", // ? + "RegistryData ", // used + "MemoryData ", // not used + "NlsData ", // used + "SpecialMemory ", // == Bad + "BBTMemory " // == Bad + }; + +VOID +WinLdrpDumpMemoryDescriptors(PLOADER_PARAMETER_BLOCK LoaderBlock); + + +VOID +MempAddMemoryBlock(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, + ULONG BasePage, + ULONG PageCount, + ULONG Type); +VOID +WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, + IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor); + +VOID +WinLdrRemoveDescriptor(IN PMEMORY_ALLOCATION_DESCRIPTOR Descriptor); + +VOID +WinLdrSetProcessorContext(PVOID GdtIdt, IN ULONG Pcr, IN ULONG Tss); + +// This is needed only for SetProcessorContext routine +#pragma pack(2) + typedef struct + { + USHORT Limit; + ULONG Base; + } GDTIDT; +#pragma pack(4) + +// this is needed for new IDT filling +#if 0 +extern ULONG_PTR i386DivideByZero; +extern ULONG_PTR i386DebugException; +extern ULONG_PTR i386NMIException; +extern ULONG_PTR i386Breakpoint; +extern ULONG_PTR i386Overflow; +extern ULONG_PTR i386BoundException; +extern ULONG_PTR i386InvalidOpcode; +extern ULONG_PTR i386FPUNotAvailable; +extern ULONG_PTR i386DoubleFault; +extern ULONG_PTR i386CoprocessorSegment; +extern ULONG_PTR i386InvalidTSS; +extern ULONG_PTR i386SegmentNotPresent; +extern ULONG_PTR i386StackException; +extern ULONG_PTR i386GeneralProtectionFault; +extern ULONG_PTR i386PageFault; // exc 14 +extern ULONG_PTR i386CoprocessorError; // exc 16 +extern ULONG_PTR i386AlignmentCheck; // exc 17 +#endif + +/* GLOBALS ***************************************************************/ + +PHARDWARE_PTE PDE; +PHARDWARE_PTE HalPageTable; + +PUCHAR PhysicalPageTablesBuffer; +PUCHAR KernelPageTablesBuffer; +ULONG PhysicalPageTables; +ULONG KernelPageTables; + +MEMORY_ALLOCATION_DESCRIPTOR *Mad; +ULONG MadCount = 0; + + +/* FUNCTIONS **************************************************************/ + +BOOLEAN +MempAllocatePageTables() +{ + ULONG NumPageTables, TotalSize; + PUCHAR Buffer; + // It's better to allocate PDE + PTEs contigiuos + + // Max number of entries = MaxPageNum >> 10 + // FIXME: This is a number to describe ALL physical memory + // and windows doesn't expect ALL memory mapped... + NumPageTables = (GetSystemMemorySize() >> MM_PAGE_SHIFT) >> 10; + + DbgPrint((DPRINT_WINDOWS, "NumPageTables = %d\n", NumPageTables)); + + // Allocate memory block for all these things: + // PDE, HAL mapping page table, physical mapping, kernel mapping + TotalSize = (1+1+NumPageTables*2)*MM_PAGE_SIZE; + + // PDE+HAL+KernelPTEs == MemoryData + Buffer = MmAllocateMemoryWithType( + TotalSize - NumPageTables*MM_PAGE_SIZE, LoaderMemoryData); + + // Physical PTEs = FirmwareTemporary + PhysicalPageTablesBuffer = MmAllocateMemoryWithType( + NumPageTables*MM_PAGE_SIZE, LoaderFirmwareTemporary); + + if (Buffer + (TotalSize - NumPageTables*MM_PAGE_SIZE) != + PhysicalPageTablesBuffer) + { + DbgPrint((DPRINT_WINDOWS, "There was a problem allocating two adjacent blocks of memory!")); + } + + if (Buffer == NULL || PhysicalPageTablesBuffer == NULL) + { + UiMessageBox("Impossible to allocate memory block for page tables!"); + return FALSE; + } + + // Zero all this memory block + RtlZeroMemory(Buffer, TotalSize); + + // Set up pointers correctly now + PDE = (PHARDWARE_PTE)Buffer; + + // Map the page directory at 0xC0000000 (maps itself) + PDE[HYPER_SPACE_ENTRY].PageFrameNumber = (ULONG)PDE >> MM_PAGE_SHIFT; + PDE[HYPER_SPACE_ENTRY].Valid = 1; + PDE[HYPER_SPACE_ENTRY].Write = 1; + + // The last PDE slot is allocated for HAL's memory mapping (Virtual Addresses 0xFFC00000 - 0xFFFFFFFF) + HalPageTable = (PHARDWARE_PTE)&Buffer[MM_PAGE_SIZE*1]; + + // Map it + PDE[1023].PageFrameNumber = (ULONG)HalPageTable >> MM_PAGE_SHIFT; + PDE[1023].Valid = 1; + PDE[1023].Write = 1; + + // Store pointer to the table for easier access + KernelPageTablesBuffer = &Buffer[MM_PAGE_SIZE*2]; + + // Zero counters of page tables used + PhysicalPageTables = 0; + KernelPageTables = 0; + + return TRUE; +} + +VOID +MempAllocatePTE(ULONG Entry, PHARDWARE_PTE *PhysicalPT, PHARDWARE_PTE *KernelPT) +{ + //Print(L"Creating PDE Entry %X\n", Entry); + + // Identity mapping + *PhysicalPT = (PHARDWARE_PTE)&PhysicalPageTablesBuffer[PhysicalPageTables*MM_PAGE_SIZE]; + PhysicalPageTables++; + + PDE[Entry].PageFrameNumber = (ULONG)*PhysicalPT >> MM_PAGE_SHIFT; + PDE[Entry].Valid = 1; + PDE[Entry].Write = 1; + + if (Entry+(KSEG0_BASE >> 22) > 1023) + { + DbgPrint((DPRINT_WINDOWS, "WARNING! Entry: %X > 1023\n", Entry+(KSEG0_BASE >> 22))); + } + + // Kernel-mode mapping + *KernelPT = (PHARDWARE_PTE)&KernelPageTablesBuffer[KernelPageTables*MM_PAGE_SIZE]; + KernelPageTables++; + + PDE[Entry+(KSEG0_BASE >> 22)].PageFrameNumber = ((ULONG)*KernelPT >> MM_PAGE_SHIFT); + PDE[Entry+(KSEG0_BASE >> 22)].Valid = 1; + PDE[Entry+(KSEG0_BASE >> 22)].Write = 1; +} + +BOOLEAN +MempSetupPaging(IN ULONG StartPage, + IN ULONG NumberOfPages) +{ + PHARDWARE_PTE PhysicalPT; + PHARDWARE_PTE KernelPT; + ULONG Entry, Page; + + //Print(L"MempSetupPaging: SP 0x%X, Number: 0x%X\n", StartPage, NumberOfPages); + + // HACK + if (StartPage+NumberOfPages >= 0x80000) + { + // + // We can't map this as it requires more than 1 PDE + // and in fact it's not possible at all ;) + // + //Print(L"skipping...\n"); + return TRUE; + } + + // + // Now actually set up the page tables for identity mapping + // + for (Page=StartPage; Page < StartPage+NumberOfPages; Page++) + { + Entry = Page >> 10; + + if (((PULONG)PDE)[Entry] == 0) + { + MempAllocatePTE(Entry, &PhysicalPT, &KernelPT); + } + else + { + PhysicalPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT); + KernelPT = (PHARDWARE_PTE)(PDE[Entry+(KSEG0_BASE >> 22)].PageFrameNumber << MM_PAGE_SHIFT); + } + + if (Page == 0) + { + PhysicalPT[Page & 0x3ff].PageFrameNumber = Page; + PhysicalPT[Page & 0x3ff].Valid = 0; + PhysicalPT[Page & 0x3ff].Write = 0; + + KernelPT[Page & 0x3ff].PageFrameNumber = Page; + KernelPT[Page & 0x3ff].Valid = 0; + KernelPT[Page & 0x3ff].Write = 0; + } + else + { + PhysicalPT[Page & 0x3ff].PageFrameNumber = Page; + PhysicalPT[Page & 0x3ff].Valid = 1; + PhysicalPT[Page & 0x3ff].Write = 1; + + KernelPT[Page & 0x3ff].PageFrameNumber = Page; + KernelPT[Page & 0x3ff].Valid = 1; + KernelPT[Page & 0x3ff].Write = 1; + } + } + + return TRUE; +} + +VOID +MempDisablePages() +{ + int i; + + // + // We need to delete kernel mapping from memory areas which are + // marked as Special or Permanent memory (thus non-accessible) + // + + for (i=0; i LoaderPagesSpanned) + EndPage = LoaderPagesSpanned; + } + + for (Page = StartPage; Page < EndPage; Page++) + { + PHARDWARE_PTE KernelPT; + ULONG Entry = (Page >> 10) + (KSEG0_BASE >> 22); + + if (PDE[Entry].Valid) + { + KernelPT = (PHARDWARE_PTE)(PDE[Entry].PageFrameNumber << MM_PAGE_SHIFT); + + if (KernelPT) + { + KernelPT[Page & 0x3ff].PageFrameNumber = 0; + KernelPT[Page & 0x3ff].Valid = 0; + KernelPT[Page & 0x3ff].Write = 0; + } + } + } + } + } +} + +VOID +MempAddMemoryBlock(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, + ULONG BasePage, + ULONG PageCount, + ULONG Type) +{ + BOOLEAN Status; + + // + // Check for some weird stuff at the top + // + if (BasePage + PageCount > 0xF0000) + { + // + // Just skip this, without even adding to MAD list + // + return; + } + + // + // Set Base page, page count and type + // + Mad[MadCount].BasePage = BasePage; + Mad[MadCount].PageCount = PageCount; + Mad[MadCount].MemoryType = Type; + + // + // Check if it's more than the allowed for OS loader + // if yes - don't map the pages, just add as FirmwareTemporary + // + if (BasePage + PageCount > LoaderPagesSpanned) + { + if (Mad[MadCount].MemoryType != LoaderSpecialMemory && + Mad[MadCount].MemoryType != LoaderFirmwarePermanent && + Mad[MadCount].MemoryType != LoaderFree) + { + DbgPrint((DPRINT_WINDOWS, "Setting page %x %x to Temporary from %d\n", + BasePage, PageCount, Mad[MadCount].MemoryType)); + Mad[MadCount].MemoryType = LoaderFirmwareTemporary; + } + + WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); + MadCount++; + + return; + } + + // + // Add descriptor + // + WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); + MadCount++; + + // + // Map it (don't map low 1Mb because it was already contigiously + // mapped in WinLdrTurnOnPaging) + // + if (BasePage >= 0x100) + { + Status = MempSetupPaging(BasePage, PageCount); + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, "Error during MempSetupPaging\n")); + return; + } + } +} + +#ifdef _M_IX86 + +BOOLEAN LocalAPIC = FALSE; +ULONG_PTR APICAddress = 0; + +VOID +WinLdrpMapApic() +{ + /* Check if we have a local APIC */ + asm(".intel_syntax noprefix\n"); + asm("mov eax, 1\n"); + asm("cpuid\n"); + asm("shr edx, 9\n"); + asm("and edx, 0x1\n"); + asm("mov _LocalAPIC, edx\n"); + asm(".att_syntax\n"); + + /* If there is no APIC, just return */ + if (!LocalAPIC) + return; + + asm(".intel_syntax noprefix\n"); + asm("mov ecx, 0x1B\n"); + asm("rdmsr\n"); + asm("mov edx, eax\n"); + asm("and edx, 0xFFFFF000\n"); + asm("mov _APICAddress, edx"); + asm(".att_syntax\n"); + + DbgPrint((DPRINT_WINDOWS, "Local APIC detected at address 0x%x\n", + APICAddress)); + + /* Map it */ + HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber + = APICAddress >> MM_PAGE_SHIFT; + HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1; + HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1; + HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].WriteThrough = 1; + HalPageTable[(APIC_BASE - 0xFFC00000) >> MM_PAGE_SHIFT].CacheDisable = 1; +} +#else +VOID +WinLdrpMapApic() +{ + /* Implement it for another arch */ +} +#endif + +BOOLEAN +WinLdrTurnOnPaging(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, + ULONG PcrBasePage, + ULONG TssBasePage, + PVOID GdtIdt) +{ + ULONG i, PagesCount, MemoryMapSizeInPages; + ULONG LastPageIndex, LastPageType, MemoryMapStartPage; + PPAGE_LOOKUP_TABLE_ITEM MemoryMap; + ULONG NoEntries; + PKTSS Tss; + BOOLEAN Status; + + // + // Creating a suitable memory map for the Windows can be tricky, so let's + // give a few advices: + // 1) One must not map the whole available memory pages to PDE! + // Map only what's needed - 16Mb, 24Mb, 32Mb max I think, + // thus occupying 4, 6 or 8 PDE entries for identical mapping, + // the same quantity for KSEG0_BASE mapping, one more entry for + // hyperspace and one more entry for HAL physical pages mapping. + // 2) Memory descriptors must map *the whole* physical memory + // showing any memory above 16/24/32 as FirmwareTemporary + // + // 3) Overall memory blocks count must not exceed 30 (?? why?) + // + + // + // During MmInitMachineDependent, the kernel zeroes PDE at the following address + // 0xC0300000 - 0xC03007FC + // + // Then it finds the best place for non-paged pool: + // StartPde C0300F70, EndPde C0300FF8, NumberOfPages C13, NextPhysPage 3AD + // + + // Before we start mapping pages, create a block of memory, which will contain + // PDE and PTEs + if (MempAllocatePageTables() == FALSE) + return FALSE; + + // Allocate memory for memory allocation descriptors + Mad = MmHeapAlloc(sizeof(MEMORY_ALLOCATION_DESCRIPTOR) * 1024); + + // Setup an entry for each descriptor + MemoryMap = MmGetMemoryMap(&NoEntries); + if (MemoryMap == NULL) + { + UiMessageBox("Can not retrieve the current memory map"); + return FALSE; + } + + // Calculate parameters of the memory map + MemoryMapStartPage = (ULONG_PTR)MemoryMap >> MM_PAGE_SHIFT; + MemoryMapSizeInPages = NoEntries * sizeof(PAGE_LOOKUP_TABLE_ITEM); + + DbgPrint((DPRINT_WINDOWS, "Got memory map with %d entries\n", NoEntries)); + + // Always contigiously map low 1Mb of memory + Status = MempSetupPaging(0, 0x100); + if (!Status) + { + DbgPrint((DPRINT_WINDOWS, "Error during MempSetupPaging of low 1Mb\n")); + return FALSE; + } + + // Construct a good memory map from what we've got, + // but mark entries which the memory allocation bitmap takes + // as free entries (this is done in order to have the ability + // to place mem alloc bitmap outside lower 16Mb zone) + PagesCount = 1; + LastPageIndex = 0; + LastPageType = MemoryMap[0].PageAllocated; + for(i=1;i= MemoryMapStartPage && + i < (MemoryMapStartPage+MemoryMapSizeInPages)) + { + // Exclude it if current page belongs to the memory map + MemoryMap[i].PageAllocated = LoaderFree; + } + + // Process entry + if (MemoryMap[i].PageAllocated == LastPageType && + (i != NoEntries-1) ) + { + PagesCount++; + } + else + { + // Add the resulting region + MempAddMemoryBlock(LoaderBlock, LastPageIndex, PagesCount, LastPageType); + + // Reset our counter vars + LastPageIndex = i; + LastPageType = MemoryMap[i].PageAllocated; + PagesCount = 1; + } + } + + // TEMP, DEBUG! + // adding special reserved memory zones for vmware workstation +#if 0 + { + Mad[MadCount].BasePage = 0xfec00; + Mad[MadCount].PageCount = 0x10; + Mad[MadCount].MemoryType = LoaderSpecialMemory; + WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); + MadCount++; + + Mad[MadCount].BasePage = 0xfee00; + Mad[MadCount].PageCount = 0x1; + Mad[MadCount].MemoryType = LoaderSpecialMemory; + WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); + MadCount++; + + Mad[MadCount].BasePage = 0xfffe0; + Mad[MadCount].PageCount = 0x20; + Mad[MadCount].MemoryType = LoaderSpecialMemory; + WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]); + MadCount++; + } +#endif + + DbgPrint((DPRINT_WINDOWS, "MadCount: %d\n", MadCount)); + + WinLdrpDumpMemoryDescriptors(LoaderBlock); //FIXME: Delete! + + // Map our loader image, so we can continue running + /*Status = MempSetupPaging(OsLoaderBase >> MM_PAGE_SHIFT, OsLoaderSize >> MM_PAGE_SHIFT); + if (!Status) + { + UiMessageBox("Error during MempSetupPaging"); + return; + }*/ + + //VideoDisplayString(L"Hello from VGA, going into the kernel\n"); + DbgPrint((DPRINT_WINDOWS, "HalPageTable: 0x%X\n", HalPageTable)); + + // Page Tables have been setup, make special handling for PCR and TSS + // (which is done in BlSetupFotNt in usual ntldr) + HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber = PcrBasePage+1; + HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1; + HalPageTable[(KI_USER_SHARED_DATA - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1; + + HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].PageFrameNumber = PcrBasePage; + HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].Valid = 1; + HalPageTable[(KIP0PCRADDRESS - 0xFFC00000) >> MM_PAGE_SHIFT].Write = 1; + + // Map APIC + WinLdrpMapApic(); + + // Map VGA memory + //VideoMemoryBase = MmMapIoSpace(0xb8000, 4000, MmNonCached); + //DbgPrint((DPRINT_WINDOWS, "VideoMemoryBase: 0x%X\n", VideoMemoryBase)); + + Tss = (PKTSS)(KSEG0_BASE | (TssBasePage << MM_PAGE_SHIFT)); + + // Unmap what is not needed from kernel page table + MempDisablePages(); + + // Fill the memory descriptor list and + //PrepareMemoryDescriptorList(); + DbgPrint((DPRINT_WINDOWS, "Memory Descriptor List prepared, printing PDE\n")); + List_PaToVa(&LoaderBlock->MemoryDescriptorListHead); + +#ifdef DBG + { + ULONG *PDE_Addr=(ULONG *)PDE;//0xC0300000; + int j; + + DbgPrint((DPRINT_WINDOWS, "\nPDE\n")); + + for (i=0; i<128; i++) + { + DbgPrint((DPRINT_WINDOWS, "0x%04X | ", i*8)); + + for (j=0; j<8; j++) + { + DbgPrint((DPRINT_WINDOWS, "0x%08X ", PDE_Addr[i*8+j])); + } + + DbgPrint((DPRINT_WINDOWS, "\n")); + } + } +#endif + + + // Enable paging + //BS->ExitBootServices(ImageHandle,MapKey); + + // Disable Interrupts + _disable(); + + // Re-initalize EFLAGS + Ke386EraseFlags(); + + // Set the PDBR + __writecr3((ULONG_PTR)PDE); + + // Enable paging by modifying CR0 + __writecr0(__readcr0() | CR0_PG); + + // Set processor context + WinLdrSetProcessorContext(GdtIdt, KIP0PCRADDRESS, KSEG0_BASE | (TssBasePage << MM_PAGE_SHIFT)); + + // Zero KI_USER_SHARED_DATA page + memset((PVOID)KI_USER_SHARED_DATA, 0, MM_PAGE_SIZE); + + return TRUE; +} + +// Two special things this func does: it sorts descriptors, +// and it merges free ones +VOID +WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, + IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor) +{ + PLIST_ENTRY ListHead = &LoaderBlock->MemoryDescriptorListHead; + PLIST_ENTRY PreviousEntry, NextEntry; + PMEMORY_ALLOCATION_DESCRIPTOR PreviousDescriptor = NULL, NextDescriptor = NULL; + + DbgPrint((DPRINT_WINDOWS, "BP=0x%X PC=0x%X %s\n", NewDescriptor->BasePage, + NewDescriptor->PageCount, MemTypeDesc[NewDescriptor->MemoryType])); + + /* Find a place where to insert the new descriptor to */ + PreviousEntry = ListHead; + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) + { + NextDescriptor = CONTAINING_RECORD(NextEntry, + MEMORY_ALLOCATION_DESCRIPTOR, + ListEntry); + if (NewDescriptor->BasePage < NextDescriptor->BasePage) + break; + + PreviousEntry = NextEntry; + PreviousDescriptor = NextDescriptor; + NextEntry = NextEntry->Flink; + } + + /* Don't forget about merging free areas */ + if (NewDescriptor->MemoryType != LoaderFree) + { + /* Just insert, nothing to merge */ + InsertHeadList(PreviousEntry, &NewDescriptor->ListEntry); + } + else + { + /* Previous block also free? */ + if ((PreviousEntry != ListHead) && (PreviousDescriptor->MemoryType == LoaderFree) && + ((PreviousDescriptor->BasePage + PreviousDescriptor->PageCount) == + NewDescriptor->BasePage)) + { + /* Just enlarge previous descriptor's PageCount */ + PreviousDescriptor->PageCount += NewDescriptor->PageCount; + NewDescriptor = PreviousDescriptor; + } + else + { + /* Nope, just insert */ + InsertHeadList(PreviousEntry, &NewDescriptor->ListEntry); + } + + /* Next block is free ?*/ + if ((NextEntry != ListHead) && + (NextDescriptor->MemoryType == LoaderFree) && + ((NewDescriptor->BasePage + NewDescriptor->PageCount) == NextDescriptor->BasePage)) + { + /* Enlarge next descriptor's PageCount */ + NewDescriptor->PageCount += NextDescriptor->PageCount; + RemoveEntryList(&NextDescriptor->ListEntry); + } + } + + return; +} + +VOID +WinLdrSetProcessorContext(PVOID GdtIdt, IN ULONG Pcr, IN ULONG Tss) +{ + GDTIDT GdtDesc, IdtDesc, OldIdt; + PKGDTENTRY pGdt; + PKIDTENTRY pIdt; + ULONG Ldt = 0; + //ULONG i; + + DbgPrint((DPRINT_WINDOWS, "GDtIdt %p, Pcr %p, Tss 0x%08X\n", + GdtIdt, Pcr, Tss)); + + // Kernel expects the PCR to be zero-filled on startup + // FIXME: Why zero it here when we can zero it right after allocation? + RtlZeroMemory((PVOID)Pcr, MM_PAGE_SIZE); //FIXME: Why zero only 1 page when we allocate 2? + + // Get old values of GDT and IDT + Ke386GetGlobalDescriptorTable(GdtDesc); + Ke386GetInterruptDescriptorTable(IdtDesc); + + // Save old IDT + OldIdt.Base = IdtDesc.Base; + OldIdt.Limit = IdtDesc.Limit; + + // Prepare new IDT+GDT + GdtDesc.Base = KSEG0_BASE | (ULONG_PTR)GdtIdt; + GdtDesc.Limit = NUM_GDT * sizeof(KGDTENTRY) - 1; + IdtDesc.Base = (ULONG)((PUCHAR)GdtDesc.Base + GdtDesc.Limit + 1); + IdtDesc.Limit = NUM_IDT * sizeof(KIDTENTRY) - 1; + + // ======================== + // Fill all descriptors now + // ======================== + + pGdt = (PKGDTENTRY)GdtDesc.Base; + pIdt = (PKIDTENTRY)IdtDesc.Base; + + // + // Code selector (0x8) + // Flat 4Gb + // + pGdt[1].LimitLow = 0xFFFF; + pGdt[1].BaseLow = 0; + pGdt[1].HighWord.Bytes.BaseMid = 0; + pGdt[1].HighWord.Bytes.Flags1 = 0x9A; + pGdt[1].HighWord.Bytes.Flags2 = 0xCF; + pGdt[1].HighWord.Bytes.BaseHi = 0; + + // + // Data selector (0x10) + // Flat 4Gb + // + pGdt[2].LimitLow = 0xFFFF; + pGdt[2].BaseLow = 0; + pGdt[2].HighWord.Bytes.BaseMid = 0; + pGdt[2].HighWord.Bytes.Flags1 = 0x92; + pGdt[2].HighWord.Bytes.Flags2 = 0xCF; + pGdt[2].HighWord.Bytes.BaseHi = 0; + + // + // Selector (0x18) + // Flat 2Gb + // + pGdt[3].LimitLow = 0xFFFF; + pGdt[3].BaseLow = 0; + pGdt[3].HighWord.Bytes.BaseMid = 0; + pGdt[3].HighWord.Bytes.Flags1 = 0xFA; + pGdt[3].HighWord.Bytes.Flags2 = 0xCF; + pGdt[3].HighWord.Bytes.BaseHi = 0; + + // + // Selector (0x20) + // Flat 2Gb + // + pGdt[4].LimitLow = 0xFFFF; + pGdt[4].BaseLow = 0; + pGdt[4].HighWord.Bytes.BaseMid = 0; + pGdt[4].HighWord.Bytes.Flags1 = 0xF2; + pGdt[4].HighWord.Bytes.Flags2 = 0xCF; + pGdt[4].HighWord.Bytes.BaseHi = 0; + + // + // TSS Selector (0x28) + // + pGdt[5].LimitLow = 0x78-1; //FIXME: Check this + pGdt[5].BaseLow = (USHORT)(Tss & 0xffff); + pGdt[5].HighWord.Bytes.BaseMid = (UCHAR)((Tss >> 16) & 0xff); + pGdt[5].HighWord.Bytes.Flags1 = 0x89; + pGdt[5].HighWord.Bytes.Flags2 = 0x00; + pGdt[5].HighWord.Bytes.BaseHi = (UCHAR)((Tss >> 24) & 0xff); + + // + // PCR Selector (0x30) + // + pGdt[6].LimitLow = 0x01; + pGdt[6].BaseLow = (USHORT)(Pcr & 0xffff); + pGdt[6].HighWord.Bytes.BaseMid = (UCHAR)((Pcr >> 16) & 0xff); + pGdt[6].HighWord.Bytes.Flags1 = 0x92; + pGdt[6].HighWord.Bytes.Flags2 = 0xC0; + pGdt[6].HighWord.Bytes.BaseHi = (UCHAR)((Pcr >> 24) & 0xff); + + // + // Selector (0x38) + // + pGdt[7].LimitLow = 0xFFFF; + pGdt[7].BaseLow = 0; + pGdt[7].HighWord.Bytes.BaseMid = 0; + pGdt[7].HighWord.Bytes.Flags1 = 0xF3; + pGdt[7].HighWord.Bytes.Flags2 = 0x40; + pGdt[7].HighWord.Bytes.BaseHi = 0; + + // + // Some BIOS stuff (0x40) + // + pGdt[8].LimitLow = 0xFFFF; + pGdt[8].BaseLow = 0x400; + pGdt[8].HighWord.Bytes.BaseMid = 0; + pGdt[8].HighWord.Bytes.Flags1 = 0xF2; + pGdt[8].HighWord.Bytes.Flags2 = 0x0; + pGdt[8].HighWord.Bytes.BaseHi = 0; + + // + // Selector (0x48) + // + pGdt[9].LimitLow = 0; + pGdt[9].BaseLow = 0; + pGdt[9].HighWord.Bytes.BaseMid = 0; + pGdt[9].HighWord.Bytes.Flags1 = 0; + pGdt[9].HighWord.Bytes.Flags2 = 0; + pGdt[9].HighWord.Bytes.BaseHi = 0; + + // + // Selector (0x50) + // + pGdt[10].LimitLow = 0xFFFF; //FIXME: Not correct! + pGdt[10].BaseLow = 0; + pGdt[10].HighWord.Bytes.BaseMid = 0x2; + pGdt[10].HighWord.Bytes.Flags1 = 0x89; + pGdt[10].HighWord.Bytes.Flags2 = 0; + pGdt[10].HighWord.Bytes.BaseHi = 0; + + // + // Selector (0x58) + // + pGdt[11].LimitLow = 0xFFFF; + pGdt[11].BaseLow = 0; + pGdt[11].HighWord.Bytes.BaseMid = 0x2; + pGdt[11].HighWord.Bytes.Flags1 = 0x9A; + pGdt[11].HighWord.Bytes.Flags2 = 0; + pGdt[11].HighWord.Bytes.BaseHi = 0; + + // + // Selector (0x60) + // + pGdt[12].LimitLow = 0xFFFF; + pGdt[12].BaseLow = 0; //FIXME: Maybe not correct, but noone cares + pGdt[12].HighWord.Bytes.BaseMid = 0x2; + pGdt[12].HighWord.Bytes.Flags1 = 0x92; + pGdt[12].HighWord.Bytes.Flags2 = 0; + pGdt[12].HighWord.Bytes.BaseHi = 0; + + // + // Video buffer Selector (0x68) + // + pGdt[13].LimitLow = 0x3FFF; + pGdt[13].BaseLow = 0x8000; + pGdt[13].HighWord.Bytes.BaseMid = 0x0B; + pGdt[13].HighWord.Bytes.Flags1 = 0x92; + pGdt[13].HighWord.Bytes.Flags2 = 0; + pGdt[13].HighWord.Bytes.BaseHi = 0; + + // + // Points to GDT (0x70) + // + pGdt[14].LimitLow = NUM_GDT*sizeof(KGDTENTRY) - 1; + pGdt[14].BaseLow = 0x7000; + pGdt[14].HighWord.Bytes.BaseMid = 0xFF; + pGdt[14].HighWord.Bytes.Flags1 = 0x92; + pGdt[14].HighWord.Bytes.Flags2 = 0; + pGdt[14].HighWord.Bytes.BaseHi = 0xFF; + + // + // Some unused descriptors should go here + // + + // Copy the old IDT + RtlCopyMemory(pIdt, (PVOID)OldIdt.Base, OldIdt.Limit); + + // Mask interrupts + //asm("cli\n"); // they are already masked before enabling paged mode + + // Load GDT+IDT + Ke386SetGlobalDescriptorTable(GdtDesc); + Ke386SetInterruptDescriptorTable(IdtDesc); + + // Jump to proper CS and clear prefetch queue + asm("ljmp $0x08, $mb1\n" + "mb1:\n"); + + // Set SS selector + asm(".intel_syntax noprefix\n"); + asm("mov ax, 0x10\n"); // DataSelector=0x10 + asm("mov ss, ax\n"); + asm(".att_syntax\n"); + + // Set DS and ES selectors + Ke386SetDs(0x10); + Ke386SetEs(0x10); // this is vital for rep stosd + + // LDT = not used ever, thus set to 0 + Ke386SetLocalDescriptorTable(Ldt); + + // Load TSR + Ke386SetTr(0x28); + + // Clear GS + asm(".intel_syntax noprefix\n"); + asm("push 0\n"); + asm("pop gs\n"); + asm(".att_syntax\n"); + + // Set FS to PCR + Ke386SetFs(0x30); + + // Real end of the function, just for information + /* do not uncomment! + pop edi; + pop esi; + pop ebx; + mov esp, ebp; + pop ebp; + ret + */ +}