From fe9b7429788c149cb86beb953d15e02f6ee468f1 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Mon, 25 Apr 2016 22:17:14 +0200 Subject: [PATCH] Improve presentation of code snippets --- blog/post/2015-11-15-allocating-frames.md | 106 +++++++++++------- blog/post/2015-12-09-modifying-page-tables.md | 8 +- blog/post/2016-04-11-kernel-heap.md | 16 +-- 3 files changed, 81 insertions(+), 49 deletions(-) diff --git a/blog/post/2015-11-15-allocating-frames.md b/blog/post/2015-11-15-allocating-frames.md index 294b6d7c..acbe416c 100644 --- a/blog/post/2015-11-15-allocating-frames.md +++ b/blog/post/2015-11-15-allocating-frames.md @@ -49,15 +49,20 @@ Instead of writing an own Multiboot module, we use the [multiboot2-elf64] crate. [multiboot2-elf64]: https://github.com/phil-opp/multiboot2-elf64 [^fn-multiboot-crate]: All contributions are welcome! If you want to maintain it, please contact me! -So let's add a dependency on the git repository in the `Cargo.toml`: +So let's add a dependency on the git repository: ```toml -... +# in Cargo.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. +```rust +// in src/lib.rs +extern crate multiboot2; +``` + +Now we can use it to print available memory areas. ### Available Memory The boot information structure consists of various _tags_. See section 3.4 of the Multiboot specification ([PDF][multiboot specification]) for a complete list. The _memory map_ tag contains a list of all available RAM areas. 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. @@ -264,49 +269,64 @@ pub struct AreaFrameAllocator { ``` The `next_free_frame` field is a simple counter that is increased every time we return a frame. It's initialized to `0` and every frame below it counts as used. The `current_area` field holds the memory area that contains `next_free_frame`. If `next_free_frame` leaves this area, we will look for the next one in `areas`. When there are no areas left, all frames are used and `current_area` becomes `None`. The `{kernel, multiboot}_{start, end}` fields are used to avoid returning already used fields. -To implement the `FrameAllocator` trait, we need to implement the `allocate_frame` and the `deallocate_frame` methods. The former looks like this: +To implement the `FrameAllocator` trait, we need to implement the allocation and deallocation methods: ```rust -fn allocate_frame(&mut self) -> Option { - if let Some(area) = self.current_area { - // "clone" the frame to return it if it's free. Frame doesn't - // implement Clone, but we can construct an identical frame. - let frame = Frame{ number: self.next_free_frame.number }; +impl FrameAllocator for AreaFrameAllocator { + fn allocate_frame(&mut self) -> Option { + // TODO (see below) + } - // the last frame of the current area - let current_area_last_frame = { - let address = area.base_addr + area.length - 1; - Frame::containing_address(address as usize) - }; - - if frame > current_area_last_frame { - // all frames of current area are used, switch to next area - self.choose_next_area(); - } else if frame >= self.kernel_start && frame <= self.kernel_end { - // `frame` is used by the kernel - self.next_free_frame = Frame { - number: self.kernel_end.number + 1 - }; - } else if frame >= self.multiboot_start && frame <= self.multiboot_end { - // `frame` is used by the multiboot information structure - self.next_free_frame = Frame { - number: self.multiboot_end.number + 1 - }; - } else { - // frame is unused, increment `next_free_frame` and return it - self.next_free_frame.number += 1; - return Some(frame); - } - // `frame` was not valid, try it again with the updated `next_free_frame` - self.allocate_frame() - } else { - None // no free frames left + fn deallocate_frame(&mut self, frame: Frame) { + // TODO (see below) } } ``` -The `choose_next_area` method isn't part of the trait and thus goes into an `impl AreaFrameAllocator` block: +The `allocate_frame` method looks like this: ```rust +// in `allocate_frame` in `impl FrameAllocator for AreaFrameAllocator` + +if let Some(area) = self.current_area { + // "Clone" the frame to return it if it's free. Frame doesn't + // implement Clone, but we can construct an identical frame. + let frame = Frame{ number: self.next_free_frame.number }; + + // the last frame of the current area + let current_area_last_frame = { + let address = area.base_addr + area.length - 1; + Frame::containing_address(address as usize) + }; + + if frame > current_area_last_frame { + // all frames of current area are used, switch to next area + self.choose_next_area(); + } else if frame >= self.kernel_start && frame <= self.kernel_end { + // `frame` is used by the kernel + self.next_free_frame = Frame { + number: self.kernel_end.number + 1 + }; + } else if frame >= self.multiboot_start && frame <= self.multiboot_end { + // `frame` is used by the multiboot information structure + self.next_free_frame = Frame { + number: self.multiboot_end.number + 1 + }; + } else { + // frame is unused, increment `next_free_frame` and return it + self.next_free_frame.number += 1; + return Some(frame); + } + // `frame` was not valid, try it again with the updated `next_free_frame` + self.allocate_frame() +} else { + None // no free frames left +} +``` +The `choose_next_area` method isn't part of the trait and thus goes into a new `impl AreaFrameAllocator` block: + +```rust +// in `impl AreaFrameAllocator` + fn choose_next_area(&mut self) { self.current_area = self.areas.clone().filter(|area| { let address = area.base_addr + area.length - 1; @@ -330,8 +350,14 @@ If the `next_free_frame` is below the new `current_area`, it needs to be updated We don't have a data structure to store free frames, so we can't implement `deallocate_frame` reasonably. Thus we use the `unimplemented` macro, which just panics when the method is called: ```rust -fn deallocate_frame(&mut self, _frame: Frame) { - unimplemented!() +impl FrameAllocator for AreaFrameAllocator { + fn allocate_frame(&mut self) -> Option { + // described above + } + + fn deallocate_frame(&mut self, _frame: Frame) { + unimplemented!() + } } ``` diff --git a/blog/post/2015-12-09-modifying-page-tables.md b/blog/post/2015-12-09-modifying-page-tables.md index b6a0ee76..1d3d903e 100644 --- a/blog/post/2015-12-09-modifying-page-tables.md +++ b/blog/post/2015-12-09-modifying-page-tables.md @@ -101,7 +101,13 @@ features = ["no_std"] The `no_std` feature is needed because `bitflags` depends on the standard library by default. But it has a [cargo feature] to use the core library instead. It will become the default as soon as `no_std` is stable in a stable Rust release. [cargo feature]: http://doc.crates.io/manifest.html#the-[features]-section -Note that you need a `#[macro_use]` above the `extern crate` definition. +To import the macro, we need to use `#[macro_use]` above the `extern crate` definition: + +```rust +// in src/lib.rs +#[macro_use] +extern crate bitflags; +``` Now we can model the various flags: diff --git a/blog/post/2016-04-11-kernel-heap.md b/blog/post/2016-04-11-kernel-heap.md index 9f5c89b7..57f52713 100644 --- a/blog/post/2016-04-11-kernel-heap.md +++ b/blog/post/2016-04-11-kernel-heap.md @@ -320,7 +320,7 @@ let heap_test = Box::new(42); When we try to compile it using `make run`, we get several linker errors about a function named `_Unwind_Resume`: ``` -target/x86_64-unknown-linux-gnu/debug/libblog_os.a(bump_allocator-947b648f2a584929.0.o): +target/x86_64-unknown-linux-gnu/debug/libblog_os.a(bump_allocator-[…].0.o): In function `bump_allocator::__rust_allocate': /home/…/blog_os/libs/bump_allocator/src/lib.rs:19: undefined reference to `_Unwind_Resume' @@ -461,8 +461,8 @@ The crate provides an [assert_has_not_been_called!] macro (sorry for the long na pub fn init(boot_info: &BootInformation) { assert_has_not_been_called!("memory::init must be called only once"); - let memory_map_tag = … - … + let memory_map_tag = ... + ... } ``` That's it. Now our `memory::init` function can only be called once. The macro works by creating a static [AtomicBool] named `CALLED`, which is initialized to `false`. When the macro is invoked, it checks the value of `CALLED` and sets it to `true`. If the value was already `true` before, the macro panics. @@ -481,7 +481,7 @@ pub fn remap_the_kernel(allocator: &mut A, boot_info: &BootInformation) -> ActivePageTable // new where A: FrameAllocator { - … + ... println!("guard page at {:#x}", old_p4_page.start_address()); active_table // new @@ -494,9 +494,9 @@ Now we have full page table access in the `memory::init` function. This allows u // in src/memory/mod.rs pub fn init(boot_info: &BootInformation) { - … + ... - let mut frame_allocator = …; + let mut frame_allocator = ...; // below is the new part @@ -520,10 +520,10 @@ The `Page::range_inclusive` function is just a copy of the `Frame::range_inclusi // in src/memory/paging/mod.rs #[derive(…, PartialEq, Eq, PartialOrd, Ord)] -pub struct Page {…} +pub struct Page {...} impl Page { - … + ... pub fn range_inclusive(start: Page, end: Page) -> PageIter { PageIter { start: start,