After the x86 PC boots, it will be in real mode. At this time, we can access memory below 1 MB. However, the BIOS also uses some memory. Therefore, we must know which areas the BIOS occupies in order to avoid them. This article will introduce the memory map in real mode and how to obtain a memory map larger than 1 MB through BIOS.
Table of Contents
Memory Map
After a normal x86 PC boots, it will enter real mode. The memory map at this time is as shown below.
In 16-bit real mode, the bootloader or kernel needs the interrupt vector table (IVT) to handle IRQ, so we cannot overwrite the IVT area. They will also call BIOS functions, and BDA and EBDA are where BIOS functions store data, so we cannot overwrite the BDA and EBDA areas. However, when the bootloader or kernel is going to enter 32-bit protected mode, we can use the IVT and BDA areas because we will no longer use the BIOS functions.
Although we will no longer use BIOS functions after entering 32-bit protected mode, we still cannot overwrite the EBDA area. Because System Management Mode (SMM) uses EBDA. In addition, the size of EBDA is different under different BIOS. Its end address is always 0x9FFFF, but its starting address is variable. However, the starting address will always be greater than 0x80000.
Detecting Memory – E820
Since EBDA is a variable size, and the kernel also needs to know the total size of RAM, the BIOS provides some functions for the bootloader or kernel to query the usable and unusable memory ranges, that is, the memory map. During the evolution of PC, BIOS has successively provided some functions that allow us to obtain memory map. We will only introduce how to use the E820, please refer to Detecting Memory (x86) for the rest of the methods .
E820 refers to calling the BIOS function through INT 0x15 and EAX=0xE820. Each time it is called, the BIOS will fill in an Address Range Descriptor at the specified place. So, we have to keep calling until we get all the descriptors.
The following is the input for INT 0x15 E820h. On the first call, we set EBX to 0. Set ES:DI to tell the BIOS where to write the next Address Range Descriptor. Depending on the BIOS, the Address Range Descriptor may be 20 bytes or 24 bytes, so it is best to set the ECX to 24 bytes. Finally, set EDX to 0x534D4150, which represents SMAP.
Register | Content | Description |
---|---|---|
EAX | Function Code | E820h |
EBX | Continuation | Contains the continuation value to get the next range of physical memory. If this is the first call, EBX must contain 0. |
ES:DI | Buffer Pointer | Pointer to an Address Range Descriptor structure that the BIOS fills in. |
ECX | Buffer Size | The length in bytes of the structure passed to the BIOS. |
EDX | Signature | ‘SMAP’ (0x534D4150) Used by the BIOS to verify the caller is requesting the system map information to be returned in ES:DI. |
The following is the output of INT 0x15 E820h. After calling the function, if there is an error, CF will be set. EAX will be set to SMAP. If this is the last descriptor, EBX will be set to 0.
Register | Content | Description |
---|---|---|
CF | Carry Flag | Non-Carry – Indicates No Error. |
EAX | Signature | ‘SMAP’ (0x534D4150), Signature to verify correct BIOS revision. |
ES:DI | Buffer Pointer | Returned Address Range Descriptor pointer. Same value as on input. |
ECX | Buffer Size | Number of bytes returned by the BIOS in the address range descriptor. |
EBX | Continuation | Contains the continuation value to get the next address range descriptor. A return value of zero means that this is the last descriptor. |
Each time NT 0x15 E820h is called, the BIOS will fill in the next descriptor with the address pointed to by ES:DI. The following is the structure of Address Range Descriptor. Base address is 64-bit, which is the starting address of the range. Length is also 64-bit, which indicates the length of the range. The size of extended attribute is 32 bits. This field is only available in ACPI 3.0. This is why the Address Range Descriptor Structure may be 20 bytes or 24 bytes.
Offset in Bytes | Name | Description |
---|---|---|
0 | BaseAddrLow | Low 32 Bits of Base Address |
4 | BaseAddrHigh | High 32 Bits of Base Address |
8 | LengthLow | Low 32 Bits of Length in Bytes |
12 | LengthHigh | High 32 Bits of Length in Bytes |
16 | Type | Address type of this range |
20 | Extended Attributes | See the Extended Attributes for Address Range Descriptor Structure |
Type is the type of the range, and its size is 32-bit.
Value | Mnemonic | Usable by OS | Description |
---|---|---|---|
1 | AddressRangeMemory | Yes | Available RAM and usable by the OS. |
2 | AddressRangeReserved | No | In use or reserved by the system and is not to be included in the allocatable memory pool of the OS. |
3 | AddressRangeACPI | Yes | ACPI Reclaim Memory。This range is available RAM usable by the OS after it reads the ACPI tables. |
4 | AddressRangeNVS | No | ACPI NVS Memory。This range of addresses is in use or reserved by the system and must not be used by theOS. |
5 | AddressRange Unusable | No | This range of addresses contains memory in which errors have been detected.This range must not be used by OS. |
The following is the output obtained after calling INT 0x15 E820 in Bochs.
Base Address | Length | Type |
---|---|---|
0x0000 0000 0000 0000 | 0x0000 0000 0009 FC00 | 1 – Free Memory |
0x0000 0000 0009 FC00 | 0x0000 0000 0000 0400 | 2 – Reserved Memory |
0x0000 0000 000E 8000 | 0x0000 0000 0001 8000 | 2 – Reserved Memory |
0x0000 0000 0010 0000 | 0x0000 0000 01F0 0000 | 1 – Free Memory |
0x0000 0000 FFFC 0000 | 0x0000 0000 0004 0000 | 2 – Reserved Memory |
The following is the output obtained after calling INT 0x15 E820 in QEMU.
Base Address | Length | Type |
---|---|---|
0x0000 0000 0000 0000 | 0x0000 0000 0009 FC00 | 1 – Free Memory |
0x0000 0000 0009 FC00 | 0x0000 0000 0000 0400 | 2 – Reserved Memory |
0x0000 0000 000F 0000 | 0x0000 0000 0001 0000 | 2 – Reserved Memory |
0x0000 0000 0010 0000 | 0x0000 0000 07EE 0000 | 1 – Free Memory |
0x0000 0000 07FE 0000 | 0x0000 0000 0002 0000 | 2 – Reserved Memory |
0x0000 0000 FFFC 0000 | 0x0000 0000 0004 0000 | 2 – Reserved Memory |
Example
The following code shows how to call INT 0x15 E820 to read the memory map.
.code16 .text start: movw $0x8000, %di movl %ebp, %ebp # Count the number of descriptors read xorl %ebx, %ebx # Set the Continuation to 0 for the first call repeat: movl $0x534D4150, %edx # Set the Signature 'SMAP' movl $24, %ecx # Request for 24 bytes movl $0xE820, %eax int $0x15 jc failed # if the call was failed # Verify the signature 'SMAP' movl $0x534D4150, %edx cmpl %eax, %edx jne failed # Test if ebx is 0, meaning this is the last descriptor testl %ebx, %ebx je end addw $24, %di addl $1, %ebp jmp repeat end: jmp . failed: hlt
Conclusion
In addition to the E820h, there are several ways to query the memory map. If GRUB is installed, the memory map can be obtained through GRUB. If the BIOS is UEFI, UEFI also provides a better way to obtain the memory map. The good thing about the E820h is that most BIOS will provide this method.
Reference
- Memory Map (x86), OSDev.
- Detecting Memory (x86), OSDev.
- System Address Map Interface, ACPI Specification.
- INT 15h, AX=E820h – Query System Address Map.