Detecting Memory (x86): Difference between revisions

m
Reverted edits by Atie (talk) to last revision by Drakeor
[unchecked revision][unchecked revision]
m (Reverted edits by Drakeor (talk) to last revision by Zoarial94)
m (Reverted edits by Atie (talk) to last revision by Drakeor)
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):''
}}
 
 
Taking this into account, our example code would look like the following. Note that if you prefer a version that does not require the multiboot.h header downloaded from the link above, there is a version listed in the code examples section of this article.
 
<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}}
|-
! -40
| size
|-
! 04
| base_addr_low
|-
! 48
| base_addr_high
|-
! 812
| length_low
|-
! 1216
| length_high
|-
! 1620
| type
|-
|}
 
 
* '''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.
* "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.
Anonymous user