-/*\r
- * PROJECT: WinLoader\r
- * LICENSE: GPL - See COPYING in the top level directory\r
- * FILE: freeldr/winldr/peloader.c\r
- * PURPOSE: Provides routines for loading PE files. To be merged with\r
- * arch/i386/loader.c in future\r
- * This article was very handy during development:\r
- * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/\r
- * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)\r
- * The source code in this file is based on the work of respective\r
- * authors of PE loading code in ReactOS and Brian Palmer and\r
- * Alex Ionescu's arch/i386/loader.c, and my research project\r
- * (creating a native EFI loader for Windows)\r
- */\r
-\r
-/* INCLUDES ***************************************************************/\r
-#include <freeldr.h>\r
-\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-/* FUNCTIONS **************************************************************/\r
-\r
-BOOLEAN\r
-WinLdrCheckForLoadedDll(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,\r
- IN PCH DllName,\r
- OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry)\r
-{\r
- return FALSE;\r
-}\r
-\r
-\r
-BOOLEAN\r
-WinLdrScanImportDescriptorTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,\r
- IN PCCH DirectoryPath,\r
- IN PLDR_DATA_TABLE_ENTRY ScanDTE)\r
-{\r
- return FALSE;\r
-}\r
-\r
-BOOLEAN\r
-WinLdrAllocateDataTableEntry(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,\r
- IN PCCH BaseDllName,\r
- IN PCCH FullDllName,\r
- IN PVOID BasePA,\r
- OUT PLDR_DATA_TABLE_ENTRY *NewEntry)\r
-{\r
- PVOID BaseVA = PaToVa(BasePA);\r
- PWSTR Buffer;\r
- PLDR_DATA_TABLE_ENTRY DataTableEntry;\r
- PIMAGE_NT_HEADERS NtHeaders;\r
- USHORT Length;\r
-\r
- /* Allocate memory for a data table entry, zero-initialize it */\r
- DataTableEntry = (PLDR_DATA_TABLE_ENTRY)MmAllocateMemory(sizeof(LDR_DATA_TABLE_ENTRY));\r
- if (DataTableEntry == NULL)\r
- return FALSE;\r
- RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY));\r
-\r
- /* Get NT headers from the image */\r
- NtHeaders = RtlImageNtHeader(BasePA);\r
-\r
- /* Initialize corresponding fields of DTE based on NT headers value */\r
- DataTableEntry->DllBase = BaseVA;\r
- DataTableEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;\r
- DataTableEntry->EntryPoint = (PVOID)((ULONG)BaseVA +\r
- NtHeaders->OptionalHeader.AddressOfEntryPoint);\r
- DataTableEntry->SectionPointer = 0;\r
- DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;\r
-\r
- /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName\r
- by simple conversion - copying each character */\r
- Length = (USHORT)(strlen(BaseDllName) * sizeof(WCHAR));\r
- Buffer = (PWSTR)MmAllocateMemory(Length);\r
- if (Buffer == NULL)\r
- return FALSE;\r
- RtlZeroMemory(Buffer, Length);\r
-\r
- DataTableEntry->BaseDllName.Length = Length;\r
- DataTableEntry->BaseDllName.MaximumLength = Length;\r
- DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer);\r
- while (*BaseDllName != 0)\r
- {\r
- *Buffer++ = *BaseDllName++;\r
- }\r
-\r
- /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName\r
- using the same method */\r
- Length = (USHORT)(strlen(FullDllName) * sizeof(WCHAR));\r
- Buffer = (PWSTR)MmAllocateMemory(Length);\r
- if (Buffer == NULL)\r
- return FALSE;\r
- RtlZeroMemory(Buffer, Length);\r
-\r
- DataTableEntry->FullDllName.Length = Length;\r
- DataTableEntry->FullDllName.MaximumLength = Length;\r
- DataTableEntry->FullDllName.Buffer = PaToVa(Buffer);\r
- while (*FullDllName != 0)\r
- {\r
- *Buffer++ = *FullDllName++;\r
- }\r
-\r
- /* Initialize what's left - LoadCount which is 1, and set Flags so that\r
- we know this entry is processed */\r
- DataTableEntry->Flags = LDRP_ENTRY_PROCESSED;\r
- DataTableEntry->LoadCount = 1;\r
-\r
- /* Insert this DTE to a list in the LPB */\r
- InsertTailList(&WinLdrBlock->LoadOrderListHead, &DataTableEntry->InLoadOrderLinks);\r
-\r
- /* Save pointer to a newly allocated and initialized entry */\r
- *NewEntry = DataTableEntry;\r
-\r
- /* Return success */\r
- return TRUE;\r
-}\r
-\r
-/* WinLdrLoadImage loads the specified image from the file (it doesn't\r
- perform any additional operations on the filename, just directly\r
- calls the file I/O routines), and relocates it so that it's ready\r
- to be used when paging is enabled.\r
- Addressing mode: physical\r
- */\r
-BOOLEAN\r
-WinLdrLoadImage(IN PCHAR FileName,\r
- OUT PVOID *ImageBasePA)\r
-{\r
- PFILE FileHandle;\r
- PVOID PhysicalBase;\r
- PVOID VirtualBase = NULL;\r
- UCHAR HeadersBuffer[SECTOR_SIZE * 2];\r
- PIMAGE_NT_HEADERS NtHeaders;\r
- PIMAGE_SECTION_HEADER SectionHeader;\r
- ULONG VirtualSize, SizeOfRawData, NumberOfSections;\r
- BOOLEAN Status;\r
- ULONG i, BytesRead;\r
-\r
- //Print(L"Loading %s... ", FileName);\r
-\r
- /* Open the image file */\r
- FileHandle = FsOpenFile(FileName);\r
-\r
- if (FileHandle == NULL)\r
- {\r
- //Print(L"Can not open the file %s\n",FileName);\r
- UiMessageBox("Can not open the file");\r
- return FALSE;\r
- }\r
-\r
- /* Load the first 2 sectors of the image so we can read the PE header */\r
- Status = FsReadFile(FileHandle, SECTOR_SIZE * 2, NULL, HeadersBuffer);\r
- if (!Status)\r
- {\r
- //Print(L"Error reading from file %s\n", FileName);\r
- UiMessageBox("Error reading from file");\r
- FsCloseFile(FileHandle);\r
- return FALSE;\r
- }\r
-\r
- /* Now read the MZ header to get the offset to the PE Header */\r
- NtHeaders = RtlImageNtHeader(HeadersBuffer);\r
-\r
- if (!NtHeaders)\r
- {\r
- //Print(L"Error - no NT header found in %s\n", FileName);\r
- UiMessageBox("Error - no NT header found");\r
- FsCloseFile(FileHandle);\r
- return FALSE;\r
- }\r
-\r
- /* Ensure this is executable image */\r
- if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0))\r
- {\r
- //Print(L"Not an executable image %s\n", FileName);\r
- UiMessageBox("Not an executable image");\r
- FsCloseFile(FileHandle);\r
- return FALSE;\r
- }\r
-\r
- /* Store number of sections to read and a pointer to the first section */\r
- NumberOfSections = NtHeaders->FileHeader.NumberOfSections;\r
- SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);\r
-\r
- /* Try to allocate this memory, if fails - allocate somewhere else */\r
- PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage,\r
- (PVOID)NtHeaders->OptionalHeader.ImageBase);\r
-\r
- if (PhysicalBase == NULL)\r
- {\r
- /* It's ok, we don't panic - let's allocate again at any other "low" place */\r
- MmChangeAllocationPolicy(FALSE);\r
- PhysicalBase = MmAllocateMemory(NtHeaders->OptionalHeader.SizeOfImage);\r
- MmChangeAllocationPolicy(TRUE);\r
-\r
- if (PhysicalBase == NULL)\r
- {\r
- //Print(L"Failed to alloc pages for image %s\n", FileName);\r
- UiMessageBox("Failed to alloc pages for image");\r
- FsCloseFile(FileHandle);\r
- return FALSE;\r
- }\r
- }\r
-\r
- /* This is the real image base - in form of a virtual address */\r
- VirtualBase = PaToVa(PhysicalBase);\r
-\r
- DbgPrint((DPRINT_WINDOWS, "Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase));\r
-\r
- /* Set to 0 position and fully load the file image */\r
- FsSetFilePointer(FileHandle, 0);\r
-\r
- Status = FsReadFile(FileHandle, NtHeaders->OptionalHeader.SizeOfHeaders, NULL, PhysicalBase);\r
-\r
- if (!Status)\r
- {\r
- //Print(L"Error reading headers %s\n", FileName);\r
- UiMessageBox("Error reading headers");\r
- FsCloseFile(FileHandle);\r
- return FALSE;\r
- }\r
-\r
- /* Reload the NT Header */\r
- NtHeaders = RtlImageNtHeader(PhysicalBase);\r
-\r
- /* Load the first section */\r
- SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);\r
-\r
- /* Fill output parameters */\r
- *ImageBasePA = PhysicalBase;\r
-\r
- /* Walk through each section and read it (check/fix any possible\r
- bad situations, if they arise) */\r
- for (i = 0; i < NumberOfSections; i++)\r
- {\r
- VirtualSize = SectionHeader->Misc.VirtualSize;\r
- SizeOfRawData = SectionHeader->SizeOfRawData;\r
-\r
- /* Handle a case when VirtualSize equals 0 */\r
- if (VirtualSize == 0)\r
- VirtualSize = SizeOfRawData;\r
-\r
- /* If PointerToRawData is 0, then force its size to be also 0 */\r
- if (SectionHeader->PointerToRawData == 0)\r
- {\r
- SizeOfRawData = 0;\r
- }\r
- else\r
- {\r
- /* Cut the loaded size to the VirtualSize extents */\r
- if (SizeOfRawData > VirtualSize)\r
- SizeOfRawData = VirtualSize;\r
- }\r
-\r
- /* Actually read the section (if its size is not 0) */\r
- if (SizeOfRawData != 0)\r
- {\r
- /* Seek to the correct position */\r
- FsSetFilePointer(FileHandle, SectionHeader->PointerToRawData);\r
-\r
- DbgPrint((DPRINT_WINDOWS, "SH->VA: 0x%X\n", SectionHeader->VirtualAddress));\r
-\r
- /* Read this section from the file, size = SizeOfRawData */\r
- Status = FsReadFile(FileHandle, SizeOfRawData, &BytesRead, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress);\r
-\r
- if (!Status && (BytesRead == 0))\r
- break;\r
- }\r
-\r
- /* Size of data is less than the virtual size - fill up the remainder with zeroes */\r
- if (SizeOfRawData < VirtualSize)\r
- RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData);\r
-\r
- SectionHeader++;\r
- }\r
-\r
- /* We are done with the file - close it */\r
- FsCloseFile(FileHandle);\r
-\r
- /* If loading failed - return right now */\r
- if (!Status)\r
- return FALSE;\r
-\r
-\r
- /* Relocate the image, if it needs it */\r
- if (NtHeaders->OptionalHeader.ImageBase != (ULONG)VirtualBase)\r
- {\r
- DbgPrint((DPRINT_WINDOWS, "Relocating %p -> %p\n",\r
- NtHeaders->OptionalHeader.ImageBase, VirtualBase));\r
- Status = (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase,\r
- 0,\r
- "FLx86",\r
- TRUE,\r
- 3,\r
- FALSE);\r
- }\r
-\r
- return Status;\r
-}\r
-\r
-/* PRIVATE FUNCTIONS *******************************************************/\r
+/*
+ * PROJECT: WinLoader
+ * 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 <freeldr.h>
+
+#define NDEBUG
+#include <debug.h>
+
+/* FUNCTIONS **************************************************************/
+
+BOOLEAN
+WinLdrCheckForLoadedDll(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,
+ IN PCH DllName,
+ OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry)
+{
+ return FALSE;
+}
+
+
+BOOLEAN
+WinLdrScanImportDescriptorTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,
+ IN PCCH DirectoryPath,
+ IN PLDR_DATA_TABLE_ENTRY ScanDTE)
+{
+ return FALSE;
+}
+
+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)MmAllocateMemory(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 = (PVOID)((ULONG)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)MmAllocateMemory(Length);
+ if (Buffer == NULL)
+ 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)MmAllocateMemory(Length);
+ if (Buffer == NULL)
+ 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,
+ 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;
+
+ //Print(L"Loading %s... ", FileName);
+
+ /* 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)NtHeaders->OptionalHeader.ImageBase);
+
+ if (PhysicalBase == NULL)
+ {
+ /* It's ok, we don't panic - let's allocate again at any other "low" place */
+ MmChangeAllocationPolicy(FALSE);
+ PhysicalBase = MmAllocateMemory(NtHeaders->OptionalHeader.SizeOfImage);
+ MmChangeAllocationPolicy(TRUE);
+
+ 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))
+ break;
+ }
+
+ /* Size of data is less than the virtual size - fill up the remainder with zeroes */
+ if (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,
+ 0,
+ "FLx86",
+ TRUE,
+ 3,
+ FALSE);
+ }
+
+ return Status;
+}
+
+/* PRIVATE FUNCTIONS *******************************************************/