주요 BIOS/UEFI 작업 중 하나는 OS 메모리 맵을 표시하는 것이다. 운영 체제는 시스템에서 사용 가능한 RAM의 크기와 OS에서 사용할 수 있는 영역 및 사용할 수 없는 영역을 알아야 한다.
이를 위해 UEFI 스펙에서는 EFI_BOOT_SERVICES.GetMemoryMap() 함수를 정의해 주었다.
EFI_BOOT_SERVICES.GetMemoryMap()Summary:Returns the current memory map.Prototype:typedefEFI_STATUS(EFIAPI *EFI_GET_MEMORY_MAP) ( IN OUT UINTN *MemoryMapSize, IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, OUT UINTN *MapKey, OUT UINTN *DescriptorSize, OUT UINT32 *DescriptorVersion );Parameters:MemoryMapSize A pointer to the size, in bytes, of the MemoryMap buffer. On input, this is the size of the buffer allocated by the caller. On output, it is the size of the buffer returned by the firmware if the buffer was large enough, or the size of the buffer needed to contain the map if the buffer was too small.MemoryMap A pointer to the buffer in which firmware places the current memory map. The map is an array of EFI_MEMORY_DESCRIPTORs.MapKey A pointer to the location in which firmware returns the key for the current memory map.DescriptorSize A pointer to the location in which firmware returns the size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.DescriptorVersion A pointer to the location in which firmware returns the version number associated with the EFI_MEMORY_DESCRIPTOR.Status Codes Returned:EFI_SUCCESS The memory map was returned in the MemoryMap buffer.EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current buffer size needed to hold the memory map is returned in MemoryMapSize.EFI_INVALID_PARAMETER MemoryMapSize is NULL.EFI_INVALID_PARAMETER The MemoryMap buffer is not too small and MemoryMap is NULL.
//*******************************************************//EFI_MEMORY_DESCRIPTOR//*******************************************************typedefstruct { UINT32 Type; EFI_PHYSICAL_ADDRESS PhysicalStart; EFI_VIRTUAL_ADDRESS VirtualStart; UINT64 NumberOfPages; UINT64 Attribute;} EFI_MEMORY_DESCRIPTOR;Type Type of the memory region. Type EFI_MEMORY_TYPE is defined in the AllocatePages() function description.PhysicalStart Physical address of the first byte in the memory region. PhysicalStart must be aligned on a 4 KiB boundary, and must not be above 0xfffffffffffff000. Type EFI_PHYSICAL_ADDRESS is defined in the AllocatePages() function description.VirtualStart Virtual address of the first byte in the memory region. VirtualStart must be aligned on a 4 KiB boundary, and must not be above 0xfffffffffffff000.NumberOfPages Number of 4 KiB pages in the memory region. NumberOfPages must not be 0, and must not be any value that would represent a memory page with a start address, either physical or virtual, above 0xfffffffffffff000Attribute Attributes of the memory region that describe the bit mask of capabilities for that memory region, and not necessarily the current settings for that memory region.
EFI_BOOT_SERVICES.AllocatePages()는 스펙에 따라 4KB 페이지를 할당한다.
첫 번째 GetMemoryMap()함수의 호출은 MemoryMapSize=0이 모든 메모리 디스크립터를 저장하기 충분하지 않기 때문에 EFI_BUFFER_TOO_SMALL과 함께 실패할 것이다.
하지만 이 첫 번째 호출로 인해서 할당해야 하는 실제 크기로 MemoryMapSize를 채워준다.
그래서 바로 다음에 gBS->AllocatePool를 호출하여 필요한 크기를 할당한다.
EFI_BOOT_SERVICES.AllocatePool()Summary:Allocates pool memoryPrototype:typedefEFI_STATUS(EFIAPI *EFI_ALLOCATE_POOL) ( IN EFI_MEMORY_TYPE PoolType, IN UINTN Size, OUT VOID **Buffer );Parameters:PoolType The type of pool to allocate. Type EFI_MEMORY_TYPE is defined in the EFI_BOOT_SERVICES.AllocatePages() function description.Size The number of bytes to allocate from the pool.Buffer A pointer to a pointer to the allocated buffer if the call succeeds; undefined otherwise.Description:The AllocatePool() function allocates a memory region of Size bytes from memory of type PoolTypeand returns the address of the allocated memory in the location referenced by Buffer.Status Codes Returned:EFI_SUCCESS The requested number of bytes was allocated.EFI_OUT_OF_RESOURCES The pool requested could not be allocated.EFI_INVALID_PARAMETER PoolType is in the range EfiMaxMemoryType..0x6FFFFFFF.EFI_INVALID_PARAMETER PoolType is EfiPersistentMemory.EFI_INVALID_PARAMETER Buffer is NULL
아까 작성한 코드를 보면 AllocatePool()의 첫번째 인자인 EFI_MEMORY_TYPE으로 우리는 BootServices에 연결되고 OS가 실행을 시작한 후 해제될 데이터를 나타내는 EfiBootServicesData를 전달한다.
메모리가 성공적으로 할당되었으면 마지막에 gBS->FreePool 호출로 할당을 해제해야 한다.
EFI_BOOT_SERVICES.FreePool()Summary:Returns pool memory to the system.Prototype:typedefEFI_STATUS(EFIAPI *EFI_FREE_POOL) (IN VOID *Buffer);Parameters:Buffer Pointer to the buffer to free.Description:The FreePool() function returns the memory specified by Buffer to the system. The Buffer that is freed must have been allocated by AllocatePool().
Status Codes Returned:EFI_SUCCESS The memory was returned to the system.EFI_INVALID_PARAMETER Buffer was invalid.
GetMemoryMap 함수에 대한 두 번째 호출이 성공하면 MemoryMap 주소에서 시작하는 EFI_MEMORY_DESCRIPTOR 개체 배열을 가져온다.
우리는 이 배열을 살펴보고 각각의 디스크립터에 대한 정보를 출력하려고 한다.
메모리 type 값을 문자열로 변환하기 위해 memory_type_to_str 도우미 함수를 만든다.