| /** @file | |
| Pre-Create a 4G page table (2M pages). | |
| It's used in DUET x64 build needed to enter LongMode. | |
| Create 4G page table (2M pages) | |
| Linear Address | |
| 63 48 47 39 38 30 29 21 20 0 | |
| +--------+-------+---------------+-----------+-----------------------------+ | |
| PML4 Directory-Ptr Directory Offset | |
| Paging-Structures := | |
| PML4 | |
| ( | |
| Directory-Ptr Directory {512} | |
| ) {4} | |
| Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "VirtualMemory.h" | |
| #include "EfiUtilityMsgs.h" | |
| #include "ParseInf.h" | |
| #define EFI_PAGE_BASE_OFFSET_IN_LDR 0x70000 | |
| #define EFI_PAGE_BASE_ADDRESS (EFI_PAGE_BASE_OFFSET_IN_LDR + 0x20000) | |
| UINT32 gPageTableBaseAddress = EFI_PAGE_BASE_ADDRESS; | |
| UINT32 gPageTableOffsetInFile = EFI_PAGE_BASE_OFFSET_IN_LDR; | |
| #define EFI_MAX_ENTRY_NUM 512 | |
| #define EFI_PML4_ENTRY_NUM 1 | |
| #define EFI_PDPTE_ENTRY_NUM 4 | |
| #define EFI_PDE_ENTRY_NUM EFI_MAX_ENTRY_NUM | |
| #define EFI_PML4_PAGE_NUM 1 | |
| #define EFI_PDPTE_PAGE_NUM EFI_PML4_ENTRY_NUM | |
| #define EFI_PDE_PAGE_NUM (EFI_PML4_ENTRY_NUM * EFI_PDPTE_ENTRY_NUM) | |
| #define EFI_PAGE_NUMBER (EFI_PML4_PAGE_NUM + EFI_PDPTE_PAGE_NUM + EFI_PDE_PAGE_NUM) | |
| #define EFI_SIZE_OF_PAGE 0x1000 | |
| #define EFI_PAGE_SIZE_2M 0x200000 | |
| #define CONVERT_BIN_PAGE_ADDRESS(a) ((UINT8 *) a - PageTable + gPageTableBaseAddress) | |
| // | |
| // Utility Name | |
| // | |
| #define UTILITY_NAME "GenPage" | |
| // | |
| // Utility version information | |
| // | |
| #define UTILITY_MAJOR_VERSION 0 | |
| #define UTILITY_MINOR_VERSION 2 | |
| void | |
| Version ( | |
| void | |
| ) | |
| /*++ | |
| Routine Description: | |
| Displays the standard utility information to SDTOUT | |
| Arguments: | |
| None | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| printf ("%s Version %d.%d %s\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION); | |
| } | |
| VOID | |
| Usage ( | |
| void | |
| ) | |
| { | |
| printf ("Usage: GenPage.exe [options] EfiLoaderImageName \n\n\ | |
| Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.\n\n\ | |
| Utility to generate the EfiLoader image containing a page table.\n\n\ | |
| optional arguments:\n\ | |
| -h, --help Show this help message and exit\n\ | |
| --version Show program's version number and exit\n\ | |
| -d [DEBUG], --debug [DEBUG]\n\ | |
| Output DEBUG statements, where DEBUG_LEVEL is 0 (min)\n\ | |
| - 9 (max)\n\ | |
| -v, --verbose Print informational statements\n\ | |
| -q, --quiet Returns the exit code, error messages will be\n\ | |
| displayed\n\ | |
| -s, --silent Returns only the exit code; informational and error\n\ | |
| messages are not displayed\n\ | |
| -o OUTPUT_FILENAME, --output OUTPUT_FILENAME\n\ | |
| Output file contain both the non-page table part and\n\ | |
| the page table\n\ | |
| -b BASE_ADDRESS, --baseaddr BASE_ADDRESS\n\ | |
| The page table location\n\ | |
| -f OFFSET, --offset OFFSET\n\ | |
| The position that the page table will appear in the\n\ | |
| output file\n\ | |
| --sfo Reserved for future use\n"); | |
| } | |
| void * | |
| CreateIdentityMappingPageTables ( | |
| void | |
| ) | |
| /*++ | |
| Routine Description: | |
| To create 4G PAE 2M pagetable | |
| Return: | |
| void * - buffer containing created pagetable | |
| --*/ | |
| { | |
| UINT64 PageAddress; | |
| UINT8 *PageTable; | |
| UINT8 *PageTablePtr; | |
| int PML4Index; | |
| int PDPTEIndex; | |
| int PDEIndex; | |
| X64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *PageMapLevel4Entry; | |
| X64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *PageDirectoryPointerEntry; | |
| X64_PAGE_TABLE_ENTRY_2M *PageDirectoryEntry2MB; | |
| PageTable = (void *)malloc (EFI_PAGE_NUMBER * EFI_SIZE_OF_PAGE); | |
| memset (PageTable, 0, (EFI_PAGE_NUMBER * EFI_SIZE_OF_PAGE)); | |
| PageTablePtr = PageTable; | |
| PageAddress = 0; | |
| // | |
| // Page Table structure 3 level 2MB. | |
| // | |
| // Page-Map-Level-4-Table : bits 47-39 | |
| // Page-Directory-Pointer-Table : bits 38-30 | |
| // | |
| // Page Table 2MB : Page-Directory(2M) : bits 29-21 | |
| // | |
| // | |
| PageMapLevel4Entry = (X64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *)PageTablePtr; | |
| for (PML4Index = 0; PML4Index < EFI_PML4_ENTRY_NUM; PML4Index++, PageMapLevel4Entry++) { | |
| // | |
| // Each Page-Map-Level-4-Table Entry points to the base address of a Page-Directory-Pointer-Table Entry | |
| // | |
| PageTablePtr += EFI_SIZE_OF_PAGE; | |
| PageDirectoryPointerEntry = (X64_PAGE_MAP_AND_DIRECTORY_POINTER_2MB_4K *)PageTablePtr; | |
| // | |
| // Make a Page-Map-Level-4-Table Entry | |
| // | |
| PageMapLevel4Entry->Uint64 = (UINT64)(UINT32)(CONVERT_BIN_PAGE_ADDRESS (PageDirectoryPointerEntry)); | |
| PageMapLevel4Entry->Bits.ReadWrite = 1; | |
| PageMapLevel4Entry->Bits.Present = 1; | |
| for (PDPTEIndex = 0; PDPTEIndex < EFI_PDPTE_ENTRY_NUM; PDPTEIndex++, PageDirectoryPointerEntry++) { | |
| // | |
| // Each Page-Directory-Pointer-Table Entry points to the base address of a Page-Directory Entry | |
| // | |
| PageTablePtr += EFI_SIZE_OF_PAGE; | |
| PageDirectoryEntry2MB = (X64_PAGE_TABLE_ENTRY_2M *)PageTablePtr; | |
| // | |
| // Make a Page-Directory-Pointer-Table Entry | |
| // | |
| PageDirectoryPointerEntry->Uint64 = (UINT64)(UINT32)(CONVERT_BIN_PAGE_ADDRESS (PageDirectoryEntry2MB)); | |
| PageDirectoryPointerEntry->Bits.ReadWrite = 1; | |
| PageDirectoryPointerEntry->Bits.Present = 1; | |
| for (PDEIndex = 0; PDEIndex < EFI_PDE_ENTRY_NUM; PDEIndex++, PageDirectoryEntry2MB++) { | |
| // | |
| // Make a Page-Directory Entry | |
| // | |
| PageDirectoryEntry2MB->Uint64 = (UINT64)PageAddress; | |
| PageDirectoryEntry2MB->Bits.ReadWrite = 1; | |
| PageDirectoryEntry2MB->Bits.Present = 1; | |
| PageDirectoryEntry2MB->Bits.MustBe1 = 1; | |
| PageAddress += EFI_PAGE_SIZE_2M; | |
| } | |
| } | |
| } | |
| return PageTable; | |
| } | |
| INT32 | |
| GenBinPage ( | |
| void *BaseMemory, | |
| char *NoPageFileName, | |
| char *PageFileName | |
| ) | |
| /*++ | |
| Routine Description: | |
| Write the buffer containing page table to file at a specified offset. | |
| Here the offset is defined as EFI_PAGE_BASE_OFFSET_IN_LDR. | |
| Arguments: | |
| BaseMemory - buffer containing page table | |
| NoPageFileName - file to write page table | |
| PageFileName - file save to after writing | |
| return: | |
| 0 : successful | |
| -1 : failed | |
| --*/ | |
| { | |
| FILE *PageFile; | |
| FILE *NoPageFile; | |
| UINT8 Data; | |
| unsigned long FileSize; | |
| // | |
| // Open files | |
| // | |
| PageFile = fopen (LongFilePath (PageFileName), "w+b"); | |
| if (PageFile == NULL) { | |
| Error (NoPageFileName, 0, 0x4002, "Invalid parameter option", "Output File %s open failure", PageFileName); | |
| return -1; | |
| } | |
| NoPageFile = fopen (LongFilePath (NoPageFileName), "r+b"); | |
| if (NoPageFile == NULL) { | |
| Error (NoPageFileName, 0, 0x4002, "Invalid parameter option", "Input File %s open failure", NoPageFileName); | |
| fclose (PageFile); | |
| return -1; | |
| } | |
| // | |
| // Check size - should not be great than EFI_PAGE_BASE_OFFSET_IN_LDR | |
| // | |
| fseek (NoPageFile, 0, SEEK_END); | |
| FileSize = ftell (NoPageFile); | |
| fseek (NoPageFile, 0, SEEK_SET); | |
| if (FileSize > gPageTableOffsetInFile) { | |
| Error (NoPageFileName, 0, 0x4002, "Invalid parameter option", "Input file size (0x%lx) exceeds the Page Table Offset (0x%x)", FileSize, (unsigned) gPageTableOffsetInFile); | |
| fclose (PageFile); | |
| fclose (NoPageFile); | |
| return -1; | |
| } | |
| // | |
| // Write data | |
| // | |
| while (fread (&Data, sizeof(UINT8), 1, NoPageFile)) { | |
| fwrite (&Data, sizeof(UINT8), 1, PageFile); | |
| } | |
| // | |
| // Write PageTable | |
| // | |
| fseek (PageFile, gPageTableOffsetInFile, SEEK_SET); | |
| fwrite (BaseMemory, (EFI_PAGE_NUMBER * EFI_SIZE_OF_PAGE), 1, PageFile); | |
| // | |
| // Close files | |
| // | |
| fclose (PageFile); | |
| fclose (NoPageFile); | |
| return 0; | |
| } | |
| int | |
| main ( | |
| int argc, | |
| char **argv | |
| ) | |
| { | |
| VOID *BaseMemory; | |
| INTN result; | |
| CHAR8 *OutputFile = NULL; | |
| CHAR8 *InputFile = NULL; | |
| EFI_STATUS Status; | |
| UINT64 TempValue; | |
| SetUtilityName("GenPage"); | |
| if (argc == 1) { | |
| Usage(); | |
| return STATUS_ERROR; | |
| } | |
| argc --; | |
| argv ++; | |
| if ((stricmp (argv[0], "-h") == 0) || (stricmp (argv[0], "--help") == 0)) { | |
| Usage(); | |
| return 0; | |
| } | |
| if (stricmp (argv[0], "--version") == 0) { | |
| Version(); | |
| return 0; | |
| } | |
| while (argc > 0) { | |
| if ((stricmp (argv[0], "-o") == 0) || (stricmp (argv[0], "--output") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Output file is missing for -o option"); | |
| return STATUS_ERROR; | |
| } | |
| OutputFile = argv[1]; | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-b") == 0) || (stricmp (argv[0], "--baseaddr") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Base address is missing for -b option"); | |
| return STATUS_ERROR; | |
| } | |
| Status = AsciiStringToUint64 (argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Base address is not valid intergrator"); | |
| return STATUS_ERROR; | |
| } | |
| gPageTableBaseAddress = (UINT32) TempValue; | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-f") == 0) || (stricmp (argv[0], "--offset") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Offset is missing for -f option"); | |
| return STATUS_ERROR; | |
| } | |
| Status = AsciiStringToUint64 (argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Offset is not valid intergrator"); | |
| return STATUS_ERROR; | |
| } | |
| gPageTableOffsetInFile = (UINT32) TempValue; | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) { | |
| argc --; | |
| argv ++; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-v") ==0) || (stricmp (argv[0], "--verbose") == 0)) { | |
| argc --; | |
| argv ++; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Debug Level is not specified."); | |
| return STATUS_ERROR; | |
| } | |
| Status = AsciiStringToUint64 (argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Debug Level is not valid intergrator."); | |
| return STATUS_ERROR; | |
| } | |
| if (TempValue > 9) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) TempValue); | |
| return STATUS_ERROR; | |
| } | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if (argv[0][0] == '-') { | |
| Error (NULL, 0, 1000, "Unknown option", argv[0]); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Don't recognize the paramter. | |
| // | |
| InputFile = argv[0]; | |
| argc--; | |
| argv++; | |
| } | |
| if (InputFile == NULL) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Input file is not specified"); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Create X64 page table | |
| // | |
| BaseMemory = CreateIdentityMappingPageTables (); | |
| // | |
| // Add page table to binary file | |
| // | |
| result = GenBinPage (BaseMemory, InputFile, OutputFile); | |
| if (result < 0) { | |
| return STATUS_ERROR; | |
| } | |
| return 0; | |
| } | |