Print available memory areas using multiboot info

This commit is contained in:
Philipp Oppermann
2015-11-13 15:04:29 +01:00
parent 73aa41e25e
commit 33fdbce530
3 changed files with 57 additions and 0 deletions

View File

@@ -9,3 +9,6 @@ crate-type = ["staticlib"]
[dependencies]
rlibc = "0.1.4"
spin = "0.3.4"
[dependencies.multiboot2]
git = "https://github.com/phil-opp/multiboot2-elf64"

View File

@@ -27,6 +27,52 @@ Now we can add the argument to our `rust_main`:
pub extern fn rust_main(multiboot_information_address: usize) { ... }
```
Now we can use the [multiboot2-elf64] crate to query get some information about mapped kernel sections and available memory. I just wrote it for this blog post since I could not find any other Multiboot 2 crate. It's really ugly and incomplete, but it does its job.
[multiboot2-elf64]: https://github.com/phil-opp/multiboot2-elf64
So let's add a dependency on the git repository in the `Cargo.toml`:
```toml
...
[dependencies.multiboot2]
git = "https://github.com/phil-opp/multiboot2-elf64"
```
Now we can add `extern crate multiboot2` and use it to print available memory areas.
### Available Memory
The boot information structure consists of various _tags_. The _memory map_ tag contains a list of all areas of available RAM. Special areas such as the VGA text buffer at `0xb8000` are not available. Note that some of the available memory is already used by our kernel and by the multiboot information structure itself.
To print available memory areas, we can use the `multiboot2` crate in our `rust_main` as follows:
```rust
let boot_info = unsafe{ multiboot2::load(multiboot_information_address) };
println!("memory areas:");
for area in boot_info.memory_map_tag().unwrap().memory_areas() {
println!(" start: 0x{:x}, length: 0x{:x}", area.base_addr, area.length);
}
```
The `load` function is `unsafe` because it relies on a valid address. Since the memory tag is not required, the `memory_map_tag()` function returns an `Option`. The `memory_areas()` function returns the desired memory area iterator.
The output looks like this:
```
Hello World!
memory areas:
start: 0x0, length: 0x9fc00
start: 0x100000, length: 0x7ee0000
```
So we have one area from `0x0` to `0x9fc00`, which is a bit below the 1MiB mark. The second, bigger area starts at 1MiB and contains the rest of available memory. The area from `0x9fc00` to 1MiB is not available. For example the VGA text buffer at `0xb8000` is in that area. This is the reason for putting our kernel at 1MiB and not at e.g. `0x0`.
If you give QEMU more than 4GiB of memory by passing `-m 5G`, you get another unusable area below the 4GiB mark. This memory is normally mapped to some hardware devices. See the [OSDev Wiki][Memory_map] for more information.
[Memory_map]: http://wiki.osdev.org/Memory_Map_(x86)
### Kernel ELF Sections
## Start and End of Kernel
We can now use the ELF section tag to calculate the start and end address of our loaded kernel:

View File

@@ -18,6 +18,7 @@
extern crate rlibc;
extern crate spin;
extern crate multiboot2;
#[macro_use]
mod vga_buffer;
@@ -28,6 +29,13 @@ pub extern fn rust_main(multiboot_information_address: usize) {
vga_buffer::clear_screen();
println!("Hello World{}", "!");
let boot_info = unsafe{ multiboot2::load(multiboot_information_address) };
println!("memory areas:");
for area in boot_info.memory_map_tag().unwrap().memory_areas() {
println!(" start: 0x{:x}, length: 0x{:x}", area.base_addr, area.length);
}
loop{}
}