Detecting Memory (x86): Difference between revisions
Jump to navigation
Jump to search
[unchecked revision] | [unchecked revision] |
Content deleted Content added
Added a note which explains there is nothing wrong with the multiboot header, the error was with printf and its usage/implementation. |
|||
Line 305: | Line 305: | ||
| If bit 6 in the flags uint16_t is set, then the mmap_* fields are valid, and indicate the address and length of a buffer containing a memory map of the machine provided by the BIOS. mmap_addr is the address, and mmap_length is the total size of the buffer. The buffer consists of one or more of the following size/structure pairs (size is really used for skipping to the next pair):'' |
| If bit 6 in the flags uint16_t is set, then the mmap_* fields are valid, and indicate the address and length of a buffer containing a memory map of the machine provided by the BIOS. mmap_addr is the address, and mmap_length is the total size of the buffer. The buffer consists of one or more of the following size/structure pairs (size is really used for skipping to the next pair):'' |
||
}} |
}} |
||
Taking into account the magic number and mbd, our example code would look like this: |
|||
<source lang="c"> |
|||
#include "multiboot.h" |
|||
void _main(multiboot_info_t* mbd, uint32_t magic) |
|||
{ |
|||
/* Make sure the magic number matches for memory mapping*/ |
|||
if(magic != MULTIBOOT_BOOTLOADER_MAGIC) { |
|||
panic("invalid magic number!"); |
|||
} |
|||
/* Check bit 6 to see if we have a valid memory map */ |
|||
if(!(mbd->flags >> 6 & 0x1)) { |
|||
panic("invalid memory map given by GRUB bootloader"); |
|||
} |
|||
/* Loop through the memory map and display the values */ |
|||
int i; |
|||
for(i = 0; i < mbd->mmap_length; |
|||
i += sizeof(multiboot_memory_map_t)) |
|||
{ |
|||
multiboot_memory_map_t* mmmt = |
|||
(multiboot_memory_map_t*) (mbd->mmap_addr + i); |
|||
printf("Start Addr: %x | Length: %x | Size: %x | Type: %d\n", |
|||
mmmt->addr, mmmt->len, mmmt->size, mmmt->type); |
|||
if(mmmt->type == MULTIBOOT_MEMORY_AVAILABLE) { |
|||
/* |
|||
* Do something with this memory block! |
|||
* BE WARNED that some of memory shown as availiable is actually |
|||
* actively being used by the kernel! You'll need to take that |
|||
* into account before writing to memory! |
|||
*/ |
|||
} |
|||
} |
|||
} |
|||
</source> |
|||
⚫ | '''WARNING:''' If you downloaded the multiboot header from gnu.org (linked above) you probably got a version which defines the base address and length fields as one 64-bit unsigned integer each, rather than two 32-bit unsigned integers each. [https://forum.osdev.org/viewtopic.php?t=30318 This may cause gcc to pack the structure incorrectly] which can lead to nonsensical values when you try to read it. |
||
⚫ | * The linked forum post blames GCC for not properly packing the multiboot struct, however, the real error was the printf implementation/usage. When using the type uint64_t, you must specify %lx (instead of %x) so that printf will read all 64-bits as one argument rather than reading the high-32 as one argument and the low-32 as the next argument. |
||
Alternatively, you can modify the multiboot header, specifically the multiboot_mmap_entry struct, to the following to get the correct values: |
|||
<source lang="c"> |
|||
struct multiboot_mmap_entry |
|||
{ |
|||
multiboot_uint32_t size; |
|||
multiboot_uint32_t addr_low; |
|||
multiboot_uint32_t addr_high; |
|||
multiboot_uint32_t len_low; |
|||
multiboot_uint32_t len_high; |
|||
#define MULTIBOOT_MEMORY_AVAILABLE 1 |
|||
#define MULTIBOOT_MEMORY_RESERVED 2 |
|||
#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 |
|||
#define MULTIBOOT_MEMORY_NVS 4 |
|||
#define MULTIBOOT_MEMORY_BADRAM 5 |
|||
multiboot_uint32_t type; |
|||
} __attribute__((packed)); |
|||
typedef struct multiboot_mmap_entry multiboot_memory_map_t; |
|||
</source> |
|||
Each multiboot mmap entry is stored as the following: |
|||
:{| {{wikitable}} |
:{| {{wikitable}} |
||
|- |
|- |
||
! |
! 0 |
||
| size |
| size |
||
|- |
|- |
||
! |
! 4 |
||
| base_addr_low |
| base_addr_low |
||
|- |
|- |
||
! |
! 8 |
||
| base_addr_high |
| base_addr_high |
||
|- |
|- |
||
! |
! 12 |
||
| length_low |
| length_low |
||
|- |
|- |
||
! |
! 16 |
||
| length_high |
| length_high |
||
|- |
|- |
||
! |
! 20 |
||
| type |
| type |
||
|- |
|- |
||
|} |
|} |
||
⚫ | |||
⚫ | |||
* "size" is the size of the associated structure in bytes, which can be greater than the minimum of 20 bytes. base_addr_low is the lower 32 bits of the starting address, and base_addr_high is the upper 32 bits, for a total of a 64-bit starting address. length_low is the lower 32 bits of the size of the memory region in bytes, and length_high is the upper 32 bits, for a total of a 64-bit length. type is the variety of address range represented, where a value of 1 indicates available RAM, and all other values currently indicated a reserved area. |
* "size" is the size of the associated structure in bytes, which can be greater than the minimum of 20 bytes. base_addr_low is the lower 32 bits of the starting address, and base_addr_high is the upper 32 bits, for a total of a 64-bit starting address. length_low is the lower 32 bits of the size of the memory region in bytes, and length_high is the upper 32 bits, for a total of a 64-bit length. type is the variety of address range represented, where a value of 1 indicates available RAM, and all other values currently indicated a reserved area. |
||
* GRUB simply uses INT 15h, EAX=E820 to get the detailed memory map, and does not verify the "sanity" of the map. It also will not sort the entries, retrieve any available ACPI 3.0 extended uint32_t (with the "ignore this entry" bit), or clean up the table in any other way. |
* GRUB simply uses INT 15h, EAX=E820 to get the detailed memory map, and does not verify the "sanity" of the map. It also will not sort the entries, retrieve any available ACPI 3.0 extended uint32_t (with the "ignore this entry" bit), or clean up the table in any other way. |