Initial paging support
This commit is contained in:
parent
8f40683270
commit
89c18bbce6
@ -92,9 +92,13 @@ typedef EFI_STATUS (*PBL_FREE_POOL)(IN PVOID Memory);
|
|||||||
typedef EFI_STATUS (*PBL_GET_MEMORY_MAP)(OUT PEFI_MEMORY_MAP MemoryMap);
|
typedef EFI_STATUS (*PBL_GET_MEMORY_MAP)(OUT PEFI_MEMORY_MAP MemoryMap);
|
||||||
typedef PLIST_ENTRY (*PBL_GET_MODULES_LIST)();
|
typedef PLIST_ENTRY (*PBL_GET_MODULES_LIST)();
|
||||||
typedef INT_PTR (*PBL_GET_SECURE_BOOT_STATUS)();
|
typedef INT_PTR (*PBL_GET_SECURE_BOOT_STATUS)();
|
||||||
|
typedef VOID (*PBL_INITIALIZE_PAGE_MAP)(OUT PXTBL_PAGE_MAPPING PageMap, IN SHORT PageMapLevel, IN PVOID *MemoryMapAddress);
|
||||||
typedef EFI_STATUS (*PBL_INSTALL_XT_PROTOCOL)(IN PVOID Interface, IN PEFI_GUID Guid);
|
typedef EFI_STATUS (*PBL_INSTALL_XT_PROTOCOL)(IN PVOID Interface, IN PEFI_GUID Guid);
|
||||||
typedef EFI_STATUS (*PBL_INVOKE_BOOT_PROTOCOL)(IN PLIST_ENTRY OptionsList);
|
typedef EFI_STATUS (*PBL_INVOKE_BOOT_PROTOCOL)(IN PLIST_ENTRY OptionsList);
|
||||||
typedef EFI_STATUS (*PBL_LOCATE_PROTOCOL_HANDLES)(OUT PEFI_HANDLE *Handles, OUT PUINT_PTR Count, IN PEFI_GUID ProtocolGuid);
|
typedef EFI_STATUS (*PBL_LOCATE_PROTOCOL_HANDLES)(OUT PEFI_HANDLE *Handles, OUT PUINT_PTR Count, IN PEFI_GUID ProtocolGuid);
|
||||||
|
typedef EFI_STATUS (*PBL_MAP_EFI_MEMORY)(IN OUT PXTBL_PAGE_MAPPING PageMap, IN OUT PVOID *MemoryMapAddress);
|
||||||
|
typedef EFI_STATUS (*PBL_MAP_PAGE)(IN PXTBL_PAGE_MAPPING PageMap, IN UINT_PTR VirtualAddress, IN UINT_PTR PhysicalAddress, IN UINT NumberOfPages);
|
||||||
|
typedef EFI_STATUS (*PBL_MAP_VIRTUAL_MEMORY)(IN OUT PXTBL_PAGE_MAPPING PageMap, IN PVOID VirtualAddress, IN PVOID PhysicalAddress, IN UINT NumberOfPages, IN LOADER_MEMORY_TYPE MemoryType);
|
||||||
typedef EFI_STATUS (*PBL_OPEN_VOLUME)(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, OUT PEFI_HANDLE DiskHandle, OUT PEFI_FILE_HANDLE *FsHandle);
|
typedef EFI_STATUS (*PBL_OPEN_VOLUME)(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, OUT PEFI_HANDLE DiskHandle, OUT PEFI_FILE_HANDLE *FsHandle);
|
||||||
typedef EFI_STATUS (*PBL_OPEN_PROTOCOL)(OUT PEFI_HANDLE Handle, OUT PVOID *ProtocolHandler, IN PEFI_GUID ProtocolGuid);
|
typedef EFI_STATUS (*PBL_OPEN_PROTOCOL)(OUT PEFI_HANDLE Handle, OUT PVOID *ProtocolHandler, IN PEFI_GUID ProtocolGuid);
|
||||||
typedef EFI_STATUS (*PBL_OPEN_PROTOCOL_HANDLE)(IN EFI_HANDLE Handle, OUT PVOID *ProtocolHandler, IN PEFI_GUID ProtocolGuid);
|
typedef EFI_STATUS (*PBL_OPEN_PROTOCOL_HANDLE)(IN EFI_HANDLE Handle, OUT PVOID *ProtocolHandler, IN PEFI_GUID ProtocolGuid);
|
||||||
@ -191,6 +195,15 @@ typedef struct _XTBL_MODULE_INFO
|
|||||||
PEFI_IMAGE_UNLOAD UnloadModule;
|
PEFI_IMAGE_UNLOAD UnloadModule;
|
||||||
} XTBL_MODULE_INFO, *PXTBL_MODULE_INFO;
|
} XTBL_MODULE_INFO, *PXTBL_MODULE_INFO;
|
||||||
|
|
||||||
|
/* Boot Loader page mapping information */
|
||||||
|
typedef struct _XTBL_PAGE_MAPPING
|
||||||
|
{
|
||||||
|
LIST_ENTRY MemoryMap;
|
||||||
|
PVOID PtePointer;
|
||||||
|
PVOID MemoryMapAddress;
|
||||||
|
SHORT PageMapLevel;
|
||||||
|
} XTBL_PAGE_MAPPING, *PXTBL_PAGE_MAPPING;
|
||||||
|
|
||||||
/* XTLDR Status data */
|
/* XTLDR Status data */
|
||||||
typedef struct _XTBL_STATUS
|
typedef struct _XTBL_STATUS
|
||||||
{
|
{
|
||||||
@ -268,6 +281,10 @@ typedef struct _XTBL_LOADER_PROTOCOL
|
|||||||
PBL_FREE_PAGES FreePages;
|
PBL_FREE_PAGES FreePages;
|
||||||
PBL_FREE_POOL FreePool;
|
PBL_FREE_POOL FreePool;
|
||||||
PBL_GET_MEMORY_MAP GetMemoryMap;
|
PBL_GET_MEMORY_MAP GetMemoryMap;
|
||||||
|
PBL_INITIALIZE_PAGE_MAP InitializePageMap;
|
||||||
|
PBL_MAP_EFI_MEMORY MapEfiMemory;
|
||||||
|
PBL_MAP_PAGE MapPage;
|
||||||
|
PBL_MAP_VIRTUAL_MEMORY MapVirtualMemory;
|
||||||
PBL_SET_MEMORY SetMemory;
|
PBL_SET_MEMORY SetMemory;
|
||||||
PBL_ZERO_MEMORY ZeroMemory;
|
PBL_ZERO_MEMORY ZeroMemory;
|
||||||
} Memory;
|
} Memory;
|
||||||
|
@ -111,6 +111,7 @@ typedef struct _LOADER_INFORMATION_BLOCK
|
|||||||
LOADER_GRAPHICS_INFORMATION_BLOCK FrameBuffer;
|
LOADER_GRAPHICS_INFORMATION_BLOCK FrameBuffer;
|
||||||
} LOADER_INFORMATION_BLOCK, *PLOADER_INFORMATION_BLOCK;
|
} LOADER_INFORMATION_BLOCK, *PLOADER_INFORMATION_BLOCK;
|
||||||
|
|
||||||
|
/* Boot Loader memory mapping information */
|
||||||
typedef struct _LOADER_MEMORY_MAPPING
|
typedef struct _LOADER_MEMORY_MAPPING
|
||||||
{
|
{
|
||||||
LIST_ENTRY ListEntry;
|
LIST_ENTRY ListEntry;
|
||||||
|
@ -281,6 +281,7 @@ typedef struct _XTBL_KNOWN_BOOT_PROTOCOL XTBL_KNOWN_BOOT_PROTOCOL, *PXTBL_KNOWN_
|
|||||||
typedef struct _XTBL_LOADER_PROTOCOL XTBL_LOADER_PROTOCOL, *PXTBL_LOADER_PROTOCOL;
|
typedef struct _XTBL_LOADER_PROTOCOL XTBL_LOADER_PROTOCOL, *PXTBL_LOADER_PROTOCOL;
|
||||||
typedef struct _XTBL_MODULE_DEPS XTBL_MODULE_DEPS, *PXTBL_MODULE_DEPS;
|
typedef struct _XTBL_MODULE_DEPS XTBL_MODULE_DEPS, *PXTBL_MODULE_DEPS;
|
||||||
typedef struct _XTBL_MODULE_INFO XTBL_MODULE_INFO, *PXTBL_MODULE_INFO;
|
typedef struct _XTBL_MODULE_INFO XTBL_MODULE_INFO, *PXTBL_MODULE_INFO;
|
||||||
|
typedef struct _XTBL_PAGE_MAPPING XTBL_PAGE_MAPPING, *PXTBL_PAGE_MAPPING;
|
||||||
typedef struct _XTBL_STATUS XTBL_STATUS, *PXTBL_STATUS;
|
typedef struct _XTBL_STATUS XTBL_STATUS, *PXTBL_STATUS;
|
||||||
|
|
||||||
/* Unions forward references */
|
/* Unions forward references */
|
||||||
|
@ -15,6 +15,7 @@ list(APPEND LIBXTLDR_SOURCE
|
|||||||
|
|
||||||
# Specify list of source code files
|
# Specify list of source code files
|
||||||
list(APPEND XTLDR_SOURCE
|
list(APPEND XTLDR_SOURCE
|
||||||
|
${XTLDR_SOURCE_DIR}/arch/${ARCH}/memory.c
|
||||||
${XTLDR_SOURCE_DIR}/config.c
|
${XTLDR_SOURCE_DIR}/config.c
|
||||||
${XTLDR_SOURCE_DIR}/console.c
|
${XTLDR_SOURCE_DIR}/console.c
|
||||||
${XTLDR_SOURCE_DIR}/debug.c
|
${XTLDR_SOURCE_DIR}/debug.c
|
||||||
|
115
xtldr/arch/amd64/memory.c
Normal file
115
xtldr/arch/amd64/memory.c
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* PROJECT: ExectOS
|
||||||
|
* COPYRIGHT: See COPYING.md in the top level directory
|
||||||
|
* FILE: xtldr/arch/amd64/memory.c
|
||||||
|
* DESCRIPTION: XT Boot Loader AMD64 specific memory management
|
||||||
|
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xtldr.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the actual virtual memory mapping.
|
||||||
|
*
|
||||||
|
* @param PageMap
|
||||||
|
* Supplies a pointer to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param VirtualAddress
|
||||||
|
* Supplies a virtual address of the mapping.
|
||||||
|
*
|
||||||
|
* @param PhysicalAddress
|
||||||
|
* Supplies a physical address of the mapping.
|
||||||
|
*
|
||||||
|
* @param NumberOfPages
|
||||||
|
* Supplies a number of the pages of the mapping.
|
||||||
|
*
|
||||||
|
* @return This routine returns a status code.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapPage(IN PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN UINT_PTR VirtualAddress,
|
||||||
|
IN UINT_PTR PhysicalAddress,
|
||||||
|
IN UINT NumberOfPages)
|
||||||
|
{
|
||||||
|
SIZE_T Pml1Entry, Pml2Entry, Pml3Entry, Pml4Entry, Pml5Entry;
|
||||||
|
PHARDWARE_PTE Pml1, Pml2, Pml3, Pml4, Pml5;
|
||||||
|
SIZE_T PageFrameNumber;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
/* Set the Page Frame Number (PFN) */
|
||||||
|
PageFrameNumber = PhysicalAddress >> EFI_PAGE_SHIFT;
|
||||||
|
|
||||||
|
/* Do the recursive mapping */
|
||||||
|
while(NumberOfPages > 0)
|
||||||
|
{
|
||||||
|
/* Calculate the indices in the various Page Tables from the virtual address */
|
||||||
|
Pml5Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 48)) >> 48;
|
||||||
|
Pml4Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 39)) >> 39;
|
||||||
|
Pml3Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 30)) >> 30;
|
||||||
|
Pml2Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 21)) >> 21;
|
||||||
|
Pml1Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 12)) >> 12;
|
||||||
|
|
||||||
|
/* Check page map level */
|
||||||
|
if(PageMap->PageMapLevel == 5)
|
||||||
|
{
|
||||||
|
/* Five level Page Map */
|
||||||
|
Pml5 = ((PHARDWARE_PTE)(PageMap->PtePointer));
|
||||||
|
|
||||||
|
/* Get PML4 */
|
||||||
|
Status = BlpGetNextPageTable(PageMap, Pml5, Pml5Entry, &Pml4);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Four level Page Map */
|
||||||
|
Pml4 = ((PHARDWARE_PTE)(PageMap->PtePointer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PML3 */
|
||||||
|
Status = BlpGetNextPageTable(PageMap, Pml4, Pml4Entry, &Pml3);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PML 2 */
|
||||||
|
Status = BlpGetNextPageTable(PageMap, Pml3, Pml3Entry, &Pml2);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PML1 */
|
||||||
|
Status = BlpGetNextPageTable(PageMap, Pml2, Pml2Entry, &Pml1);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set paging entry settings */
|
||||||
|
Pml1[Pml1Entry].PageFrameNumber = PageFrameNumber;
|
||||||
|
Pml1[Pml1Entry].Valid = 1;
|
||||||
|
Pml1[Pml1Entry].Write = 1;
|
||||||
|
|
||||||
|
/* Take next virtual address and PFN */
|
||||||
|
VirtualAddress += EFI_PAGE_SIZE;
|
||||||
|
PageFrameNumber++;
|
||||||
|
|
||||||
|
/* Decrease number of pages left */
|
||||||
|
NumberOfPages--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return success */
|
||||||
|
return STATUS_EFI_SUCCESS;
|
||||||
|
}
|
97
xtldr/arch/i686/memory.c
Normal file
97
xtldr/arch/i686/memory.c
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* PROJECT: ExectOS
|
||||||
|
* COPYRIGHT: See COPYING.md in the top level directory
|
||||||
|
* FILE: xtldr/arch/i686/memory.c
|
||||||
|
* DESCRIPTION: XT Boot Loader i686 specific memory management
|
||||||
|
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <xtldr.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does the actual virtual memory mapping.
|
||||||
|
*
|
||||||
|
* @param PageMap
|
||||||
|
* Supplies a pointer to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param VirtualAddress
|
||||||
|
* Supplies a virtual address of the mapping.
|
||||||
|
*
|
||||||
|
* @param PhysicalAddress
|
||||||
|
* Supplies a physical address of the mapping.
|
||||||
|
*
|
||||||
|
* @param NumberOfPages
|
||||||
|
* Supplies a number of the pages of the mapping.
|
||||||
|
*
|
||||||
|
* @return This routine returns a status code.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapPage(IN PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN UINT_PTR VirtualAddress,
|
||||||
|
IN UINT_PTR PhysicalAddress,
|
||||||
|
IN UINT NumberOfPages)
|
||||||
|
{
|
||||||
|
SIZE_T Pml1Entry, Pml2Entry, Pml3Entry;
|
||||||
|
PHARDWARE_PTE Pml1, Pml2, Pml3;
|
||||||
|
SIZE_T PageFrameNumber;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
/* Set the Page Frame Number (PFN) */
|
||||||
|
PageFrameNumber = PhysicalAddress >> EFI_PAGE_SHIFT;
|
||||||
|
|
||||||
|
/* Do the recursive mapping */
|
||||||
|
while(NumberOfPages > 0)
|
||||||
|
{
|
||||||
|
/* Calculate the indices in the various Page Tables from the virtual address */
|
||||||
|
Pml3Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 30)) >> 30;
|
||||||
|
Pml2Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 21)) >> 21;
|
||||||
|
Pml1Entry = (VirtualAddress & ((ULONGLONG)0x1FF << 12)) >> 12;
|
||||||
|
|
||||||
|
/* Check page map level */
|
||||||
|
if(PageMap->PageMapLevel == 3)
|
||||||
|
{
|
||||||
|
/* Three level Page Map */
|
||||||
|
Pml3 = ((PHARDWARE_PTE)(PageMap->PtePointer));
|
||||||
|
|
||||||
|
/* Get PML2 */
|
||||||
|
Status = BlpGetNextPageTable(PageMap, Pml3, Pml3Entry, &Pml2);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Two level Page Map */
|
||||||
|
Pml2 = ((PHARDWARE_PTE)(PageMap->PtePointer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get PML1 */
|
||||||
|
Status = BlpGetNextPageTable(PageMap, Pml2, Pml2Entry, &Pml1);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set paging entry settings */
|
||||||
|
Pml1[Pml1Entry].PageFrameNumber = PageFrameNumber;
|
||||||
|
Pml1[Pml1Entry].Valid = 1;
|
||||||
|
Pml1[Pml1Entry].Write = 1;
|
||||||
|
|
||||||
|
/* Take next virtual address and PFN */
|
||||||
|
VirtualAddress += EFI_PAGE_SIZE;
|
||||||
|
PageFrameNumber++;
|
||||||
|
|
||||||
|
/* Decrease number of pages left */
|
||||||
|
NumberOfPages--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return success */
|
||||||
|
return STATUS_EFI_SUCCESS;
|
||||||
|
}
|
@ -154,6 +154,12 @@ XTCDECL
|
|||||||
VOID
|
VOID
|
||||||
BlInitializeConsole();
|
BlInitializeConsole();
|
||||||
|
|
||||||
|
XTCDECL
|
||||||
|
VOID
|
||||||
|
BlInitializePageMap(OUT PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN SHORT PageMapLevel,
|
||||||
|
IN PVOID *MemoryMapAddress);
|
||||||
|
|
||||||
XTCDECL
|
XTCDECL
|
||||||
EFI_STATUS
|
EFI_STATUS
|
||||||
BlInstallProtocol(IN PVOID Interface,
|
BlInstallProtocol(IN PVOID Interface,
|
||||||
@ -177,6 +183,26 @@ BlLocateProtocolHandles(OUT PEFI_HANDLE *Handles,
|
|||||||
OUT PUINT_PTR Count,
|
OUT PUINT_PTR Count,
|
||||||
IN PEFI_GUID ProtocolGuid);
|
IN PEFI_GUID ProtocolGuid);
|
||||||
|
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapEfiMemory(IN OUT PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN OUT PVOID *DesiredVirtualAddress);
|
||||||
|
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapPage(IN PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN UINT_PTR VirtualAddress,
|
||||||
|
IN UINT_PTR PhysicalAddress,
|
||||||
|
IN UINT NumberOfPages);
|
||||||
|
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapVirtualMemory(IN OUT PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN PVOID VirtualAddress,
|
||||||
|
IN PVOID PhysicalAddress,
|
||||||
|
IN UINT NumberOfPages,
|
||||||
|
IN LOADER_MEMORY_TYPE MemoryType);
|
||||||
|
|
||||||
XTCDECL
|
XTCDECL
|
||||||
EFI_STATUS
|
EFI_STATUS
|
||||||
BlOpenVolume(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath,
|
BlOpenVolume(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath,
|
||||||
@ -356,6 +382,17 @@ BlpFindParentBlockDevice(IN PLIST_ENTRY BlockDevices,
|
|||||||
IN PEFI_BLOCK_DEVICE_DATA ChildNode,
|
IN PEFI_BLOCK_DEVICE_DATA ChildNode,
|
||||||
OUT PEFI_BLOCK_DEVICE_DATA ParentNode);
|
OUT PEFI_BLOCK_DEVICE_DATA ParentNode);
|
||||||
|
|
||||||
|
XTCDECL
|
||||||
|
LOADER_MEMORY_TYPE
|
||||||
|
BlpGetLoaderMemoryType(IN EFI_MEMORY_TYPE EfiMemoryType);
|
||||||
|
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlpGetNextPageTable(IN PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN PHARDWARE_PTE PageTable,
|
||||||
|
IN SIZE_T Entry,
|
||||||
|
OUT PHARDWARE_PTE *NextPageTable);
|
||||||
|
|
||||||
XTCDECL
|
XTCDECL
|
||||||
EFI_STATUS
|
EFI_STATUS
|
||||||
BlpInitializeDebugConsole();
|
BlpInitializeDebugConsole();
|
||||||
|
427
xtldr/memory.c
427
xtldr/memory.c
@ -153,3 +153,430 @@ BlGetMemoryMap(OUT PEFI_MEMORY_MAP MemoryMap)
|
|||||||
/* Return success */
|
/* Return success */
|
||||||
return STATUS_EFI_SUCCESS;
|
return STATUS_EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the page mapping structures.
|
||||||
|
*
|
||||||
|
* @param PageMap
|
||||||
|
* Supplies a pointer to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param PageMapLevel
|
||||||
|
* Specifies a number of of paging structures levels.
|
||||||
|
*
|
||||||
|
* @param MemoryMapAddress
|
||||||
|
* Supplies an address of the mapped virtual memory area.
|
||||||
|
*
|
||||||
|
* @return This routine does not return any value.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
VOID
|
||||||
|
BlInitializePageMap(OUT PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN SHORT PageMapLevel,
|
||||||
|
IN PVOID *MemoryMapAddress)
|
||||||
|
{
|
||||||
|
/* Initialize memory mappings */
|
||||||
|
RtlInitializeListHead(&PageMap->MemoryMap);
|
||||||
|
|
||||||
|
/* Set page map level and memory map address */
|
||||||
|
PageMap->PageMapLevel = PageMapLevel;
|
||||||
|
PageMap->MemoryMapAddress = &MemoryMapAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds EFI memory mapping to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param PageMap
|
||||||
|
* Supplies a pointer to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param DesiredVirtualAddress
|
||||||
|
* Supplies a desired virtual address, where EFI memory will be mapped.
|
||||||
|
* If not specified, memory map address will be used.
|
||||||
|
*
|
||||||
|
* @return This routine returns a status code.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapEfiMemory(IN OUT PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN OUT PVOID *DesiredVirtualAddress)
|
||||||
|
{
|
||||||
|
PEFI_MEMORY_DESCRIPTOR Descriptor;
|
||||||
|
LOADER_MEMORY_TYPE MemoryType;
|
||||||
|
PEFI_MEMORY_MAP MemoryMap;
|
||||||
|
SIZE_T DescriptorCount;
|
||||||
|
PUCHAR VirtualAddress;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
SIZE_T Index;
|
||||||
|
|
||||||
|
/* Set initial virtual address */
|
||||||
|
if(*DesiredVirtualAddress != NULL)
|
||||||
|
{
|
||||||
|
/* Set virtual address as specified in argument */
|
||||||
|
VirtualAddress = *DesiredVirtualAddress;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Otherwise, set virtual address as specified in page map */
|
||||||
|
VirtualAddress = PageMap->MemoryMapAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate and zero-fill buffer for EFI memory map */
|
||||||
|
BlAllocateMemoryPool(sizeof(EFI_MEMORY_MAP), (PVOID*)&MemoryMap);
|
||||||
|
RtlZeroMemory(MemoryMap, sizeof(EFI_MEMORY_MAP));
|
||||||
|
|
||||||
|
/* Get EFI memory map */
|
||||||
|
Status = BlGetMemoryMap(MemoryMap);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Failed to get EFI memory map */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate descriptors count and get first one */
|
||||||
|
Descriptor = MemoryMap->Map;
|
||||||
|
DescriptorCount = MemoryMap->MapSize / MemoryMap->DescriptorSize;
|
||||||
|
|
||||||
|
/* Iterate through all descriptors from the memory map */
|
||||||
|
for(Index = 0; Index < DescriptorCount; Index++)
|
||||||
|
{
|
||||||
|
/* Make sure descriptor does not go beyond lowest physical page */
|
||||||
|
if((Descriptor->PhysicalStart + (Descriptor->NumberOfPages * EFI_PAGE_SIZE)) <= (UINT_PTR)-1)
|
||||||
|
{
|
||||||
|
/* Convert EFI memory type into XTLDR memory type */
|
||||||
|
MemoryType = BlpGetLoaderMemoryType(Descriptor->Type);
|
||||||
|
|
||||||
|
/* Do memory mappings depending on memory type */
|
||||||
|
if(MemoryType == LoaderFirmwareTemporary)
|
||||||
|
{
|
||||||
|
/* Map EFI firmware code */
|
||||||
|
Status = BlMapVirtualMemory(PageMap, (PVOID)Descriptor->PhysicalStart,
|
||||||
|
(PVOID)Descriptor->PhysicalStart, Descriptor->NumberOfPages, MemoryType);
|
||||||
|
}
|
||||||
|
else if(MemoryType != LoaderFree)
|
||||||
|
{
|
||||||
|
/* Add any non-free memory mapping */
|
||||||
|
Status = BlMapVirtualMemory(PageMap, VirtualAddress, (PVOID)Descriptor->PhysicalStart,
|
||||||
|
Descriptor->NumberOfPages, MemoryType);
|
||||||
|
|
||||||
|
/* Calculate next valid virtual address */
|
||||||
|
VirtualAddress += Descriptor->NumberOfPages * EFI_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Map all other memory as loader free */
|
||||||
|
Status = BlMapVirtualMemory(PageMap, NULL, (PVOID)Descriptor->PhysicalStart,
|
||||||
|
Descriptor->NumberOfPages, LoaderFree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure memory mapping succeeded */
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Mapping failed */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grab next descriptor */
|
||||||
|
Descriptor = (PEFI_MEMORY_DESCRIPTOR)((PUCHAR)Descriptor + MemoryMap->DescriptorSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return success */
|
||||||
|
return STATUS_EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a physical to virtual address mappings.
|
||||||
|
*
|
||||||
|
* @param PageMap
|
||||||
|
* Supplies a pointer to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param VirtualAddress
|
||||||
|
* Supplies a virtual address where the physical address should be mapped.
|
||||||
|
*
|
||||||
|
* @param PhysicalAddress
|
||||||
|
* Supplies a physical address which will be mapped.
|
||||||
|
*
|
||||||
|
* @param NumberOfPages
|
||||||
|
* Supplies a number of pages that will be mapped.
|
||||||
|
*
|
||||||
|
* @param MemoryType
|
||||||
|
* Supplies the type of mapped memory that will be assigned to the memory descriptor.
|
||||||
|
*
|
||||||
|
* @return This routine returns a status code.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlMapVirtualMemory(IN OUT PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN PVOID VirtualAddress,
|
||||||
|
IN PVOID PhysicalAddress,
|
||||||
|
IN UINT NumberOfPages,
|
||||||
|
IN LOADER_MEMORY_TYPE MemoryType)
|
||||||
|
{
|
||||||
|
PLOADER_MEMORY_MAPPING Mapping1, Mapping2, Mapping3;
|
||||||
|
PVOID PhysicalAddressEnd, PhysicalAddress2End;
|
||||||
|
PLIST_ENTRY ListEntry, MappingListEntry;
|
||||||
|
SIZE_T NumberOfMappedPages;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
/* Allocate memory for new mapping */
|
||||||
|
Status = BlAllocateMemoryPool(sizeof(LOADER_MEMORY_MAPPING), (PVOID *)&Mapping1);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory allocation failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set mapping fields */
|
||||||
|
Mapping1->PhysicalAddress = PhysicalAddress;
|
||||||
|
Mapping1->VirtualAddress = VirtualAddress;
|
||||||
|
Mapping1->NumberOfPages = NumberOfPages;
|
||||||
|
Mapping1->MemoryType = MemoryType;
|
||||||
|
|
||||||
|
/* Calculate the end of the physical address */
|
||||||
|
PhysicalAddressEnd = (PUINT8)PhysicalAddress + (NumberOfPages * EFI_PAGE_SIZE) - 1;
|
||||||
|
|
||||||
|
/* Iterate through all the mappings already set to insert new mapping at the correct place */
|
||||||
|
ListEntry = PageMap->MemoryMap.Flink;
|
||||||
|
while(ListEntry != &PageMap->MemoryMap)
|
||||||
|
{
|
||||||
|
/* Take a mapping from the list and calculate its end of physical address */
|
||||||
|
Mapping2 = CONTAIN_RECORD(ListEntry, LOADER_MEMORY_MAPPING, ListEntry);
|
||||||
|
PhysicalAddress2End = (PUINT8)Mapping2->PhysicalAddress + (Mapping2->NumberOfPages * EFI_PAGE_SIZE) - 1 ;
|
||||||
|
|
||||||
|
/* Check if they overlap */
|
||||||
|
if(PhysicalAddressEnd > Mapping2->PhysicalAddress && PhysicalAddressEnd <= PhysicalAddress2End)
|
||||||
|
{
|
||||||
|
/* Make sure it's memory type is LoaderFree */
|
||||||
|
if(Mapping2->MemoryType != LoaderFree)
|
||||||
|
{
|
||||||
|
/* LoaderFree memory type is strictly expected */
|
||||||
|
return STATUS_EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate number of pages for this mapping */
|
||||||
|
NumberOfMappedPages = ((PUINT8)PhysicalAddress2End - (PUINT8)PhysicalAddressEnd) / EFI_PAGE_SIZE;
|
||||||
|
if(NumberOfMappedPages > 0)
|
||||||
|
{
|
||||||
|
/* Pages associated to the mapping, allocate memory for it */
|
||||||
|
Status = BlAllocateMemoryPool(sizeof(LOADER_MEMORY_MAPPING), (PVOID*)&Mapping3);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory allocation failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set mapping fields and insert it on the top */
|
||||||
|
Mapping3->PhysicalAddress = (PUINT8)PhysicalAddressEnd + 1;
|
||||||
|
Mapping3->VirtualAddress = NULL;
|
||||||
|
Mapping3->NumberOfPages = NumberOfMappedPages;
|
||||||
|
Mapping3->MemoryType = Mapping2->MemoryType;
|
||||||
|
RtlInsertHeadList(&Mapping2->ListEntry, &Mapping3->ListEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate number of pages and the end of the physical address */
|
||||||
|
Mapping2->NumberOfPages = ((PUINT8)PhysicalAddressEnd + 1 -
|
||||||
|
(PUINT8)Mapping2->PhysicalAddress) / EFI_PAGE_SIZE;
|
||||||
|
PhysicalAddress2End = (PUINT8)Mapping2->PhysicalAddress + (Mapping2->NumberOfPages * EFI_PAGE_SIZE) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if they overlap */
|
||||||
|
if(Mapping1->PhysicalAddress > Mapping2->PhysicalAddress && Mapping1->PhysicalAddress < PhysicalAddress2End)
|
||||||
|
{
|
||||||
|
/* Make sure it's memory type is LoaderFree */
|
||||||
|
if(Mapping2->MemoryType != LoaderFree)
|
||||||
|
{
|
||||||
|
/* LoaderFree memory type is strictly expected */
|
||||||
|
return STATUS_EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate number of pages for this mapping */
|
||||||
|
NumberOfMappedPages = ((PUINT8)PhysicalAddress2End + 1 - (PUINT8)Mapping1->PhysicalAddress) / EFI_PAGE_SIZE;
|
||||||
|
if(NumberOfMappedPages > 0)
|
||||||
|
{
|
||||||
|
/* Pages associated to the mapping, allocate memory for it */
|
||||||
|
Status = BlAllocateMemoryPool(sizeof(LOADER_MEMORY_MAPPING), (PVOID*)&Mapping3);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory allocation failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set mapping fields and insert it on the top */
|
||||||
|
Mapping3->PhysicalAddress = Mapping1->PhysicalAddress;
|
||||||
|
Mapping3->VirtualAddress = NULL;
|
||||||
|
Mapping3->NumberOfPages = NumberOfMappedPages;
|
||||||
|
Mapping3->MemoryType = Mapping2->MemoryType;
|
||||||
|
RtlInsertHeadList(&Mapping2->ListEntry, &Mapping3->ListEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate number of pages and the end of the physical address */
|
||||||
|
Mapping2->NumberOfPages = ((PUINT8)Mapping1->PhysicalAddress -
|
||||||
|
(PUINT8)Mapping2->PhysicalAddress) / EFI_PAGE_SIZE;
|
||||||
|
PhysicalAddress2End = (PUINT8)Mapping2->PhysicalAddress + (Mapping2->NumberOfPages * EFI_PAGE_SIZE) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if mapping is really needed */
|
||||||
|
if((Mapping2->PhysicalAddress >= Mapping1->PhysicalAddress && PhysicalAddress2End <= PhysicalAddressEnd) ||
|
||||||
|
(Mapping2->NumberOfPages == 0))
|
||||||
|
{
|
||||||
|
/* Make sure it's memory type is LoaderFree */
|
||||||
|
if(Mapping2->MemoryType != LoaderFree)
|
||||||
|
{
|
||||||
|
/* LoaderFree memory type is strictly expected */
|
||||||
|
return STATUS_EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store address of the next mapping */
|
||||||
|
MappingListEntry = ListEntry->Flink;
|
||||||
|
|
||||||
|
/* Remove mapping from the list and free up it's memory */
|
||||||
|
RtlRemoveEntryList(&Mapping2->ListEntry);
|
||||||
|
Status = BlFreeMemoryPool(Mapping2);
|
||||||
|
ListEntry = MappingListEntry;
|
||||||
|
|
||||||
|
/* Go to the next mapping */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine physical address order */
|
||||||
|
if(Mapping2->PhysicalAddress > Mapping1->PhysicalAddress)
|
||||||
|
{
|
||||||
|
/* Insert new mapping in front */
|
||||||
|
RtlInsertHeadList(Mapping2->ListEntry.Blink, &Mapping1->ListEntry);
|
||||||
|
return STATUS_EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get next mapping from the list */
|
||||||
|
ListEntry = ListEntry->Flink;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert new mapping to the list */
|
||||||
|
RtlInsertTailList(&PageMap->MemoryMap, &Mapping1->ListEntry);
|
||||||
|
|
||||||
|
/* Return success */
|
||||||
|
return STATUS_EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts EFI memory type to XTLDR memory type.
|
||||||
|
*
|
||||||
|
* @param EfiMemoryType
|
||||||
|
* Specifies EFI memory type.
|
||||||
|
*
|
||||||
|
* @return This routine returns a mapped XTLDR memory type.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
LOADER_MEMORY_TYPE
|
||||||
|
BlpGetLoaderMemoryType(IN EFI_MEMORY_TYPE EfiMemoryType)
|
||||||
|
{
|
||||||
|
LOADER_MEMORY_TYPE MemoryType;
|
||||||
|
|
||||||
|
/* Check EFI memory type and convert to XTLDR memory type */
|
||||||
|
switch(EfiMemoryType)
|
||||||
|
{
|
||||||
|
case EfiACPIMemoryNVS:
|
||||||
|
case EfiACPIReclaimMemory:
|
||||||
|
case EfiPalCode:
|
||||||
|
MemoryType = LoaderSpecialMemory;
|
||||||
|
break;
|
||||||
|
case EfiRuntimeServicesCode:
|
||||||
|
case EfiRuntimeServicesData:
|
||||||
|
case EfiMemoryMappedIO:
|
||||||
|
case EfiMemoryMappedIOPortSpace:
|
||||||
|
MemoryType = LoaderFirmwarePermanent;
|
||||||
|
break;
|
||||||
|
case EfiBootServicesData:
|
||||||
|
case EfiLoaderCode:
|
||||||
|
case EfiLoaderData:
|
||||||
|
MemoryType = LoaderFirmwareTemporary;
|
||||||
|
break;
|
||||||
|
case EfiUnusableMemory:
|
||||||
|
MemoryType = LoaderBad;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MemoryType = LoaderFree;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return XTLDR memory type */
|
||||||
|
return MemoryType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns next level of the Page Table.
|
||||||
|
*
|
||||||
|
* @param PageMap
|
||||||
|
* Supplies a pointer to the page mapping structure.
|
||||||
|
*
|
||||||
|
* @param PageTable
|
||||||
|
* Supplies a pointer to the current Page Table.
|
||||||
|
*
|
||||||
|
* @param Entry
|
||||||
|
* Supplies an index of the current Page Table entry.
|
||||||
|
*
|
||||||
|
* @param NextPageTable
|
||||||
|
* Supplies a pointer to the memory area where the next Page Table level is returned.
|
||||||
|
*
|
||||||
|
* @return This routine returns a status code.
|
||||||
|
*
|
||||||
|
* @since XT 1.0
|
||||||
|
*/
|
||||||
|
XTCDECL
|
||||||
|
EFI_STATUS
|
||||||
|
BlpGetNextPageTable(IN PXTBL_PAGE_MAPPING PageMap,
|
||||||
|
IN PHARDWARE_PTE PageTable,
|
||||||
|
IN SIZE_T Entry,
|
||||||
|
OUT PHARDWARE_PTE *NextPageTable)
|
||||||
|
{
|
||||||
|
EFI_PHYSICAL_ADDRESS Address;
|
||||||
|
ULONGLONG PmlPointer;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
/* Check if this is a valid table */
|
||||||
|
if(PageTable[Entry].Valid)
|
||||||
|
{
|
||||||
|
/* Get PML pointer */
|
||||||
|
PmlPointer = PageTable[Entry].PageFrameNumber;
|
||||||
|
PmlPointer <<= EFI_PAGE_SHIFT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Allocate pages for new PML entry */
|
||||||
|
Status = BlAllocateMemoryPages(1, &Address);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory allocation failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add new memory mapping */
|
||||||
|
Status = BlMapVirtualMemory(PageMap, NULL, (PVOID)(UINT_PTR)Address, 1, LoaderMemoryData);
|
||||||
|
if(Status != STATUS_EFI_SUCCESS)
|
||||||
|
{
|
||||||
|
/* Memory mapping failure */
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill allocated memory with zeros */
|
||||||
|
RtlZeroMemory((PVOID)(ULONGLONG)Address, EFI_PAGE_SIZE);
|
||||||
|
|
||||||
|
/* Set paging entry settings */
|
||||||
|
PageTable[Entry].PageFrameNumber = Address / EFI_PAGE_SIZE;
|
||||||
|
PageTable[Entry].Valid = 1;
|
||||||
|
PageTable[Entry].Write = 1;
|
||||||
|
PmlPointer = (ULONGLONG)Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set next Page Map Level (PML) */
|
||||||
|
*NextPageTable = (PHARDWARE_PTE)(ULONGLONG)PmlPointer;
|
||||||
|
|
||||||
|
/* Return success */
|
||||||
|
return STATUS_EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
@ -631,6 +631,10 @@ BlpInstallXtLoaderProtocol()
|
|||||||
BlpLdrProtocol.Memory.FreePages = BlFreeMemoryPages;
|
BlpLdrProtocol.Memory.FreePages = BlFreeMemoryPages;
|
||||||
BlpLdrProtocol.Memory.FreePool = BlFreeMemoryPool;
|
BlpLdrProtocol.Memory.FreePool = BlFreeMemoryPool;
|
||||||
BlpLdrProtocol.Memory.GetMemoryMap = BlGetMemoryMap;
|
BlpLdrProtocol.Memory.GetMemoryMap = BlGetMemoryMap;
|
||||||
|
BlpLdrProtocol.Memory.InitializePageMap = BlInitializePageMap;
|
||||||
|
BlpLdrProtocol.Memory.MapEfiMemory = BlMapEfiMemory;
|
||||||
|
BlpLdrProtocol.Memory.MapPage = BlMapPage;
|
||||||
|
BlpLdrProtocol.Memory.MapVirtualMemory = BlMapVirtualMemory;
|
||||||
BlpLdrProtocol.Memory.SetMemory = RtlSetMemory;
|
BlpLdrProtocol.Memory.SetMemory = RtlSetMemory;
|
||||||
BlpLdrProtocol.Memory.ZeroMemory = RtlZeroMemory;
|
BlpLdrProtocol.Memory.ZeroMemory = RtlZeroMemory;
|
||||||
BlpLdrProtocol.Protocol.Close = BlCloseProtocol;
|
BlpLdrProtocol.Protocol.Close = BlCloseProtocol;
|
||||||
|
Loading…
Reference in New Issue
Block a user