LoginSignup
8
3

More than 5 years have passed since last update.

[Rust] ラズパイ2でベアメタルメモリアロケーション

Last updated at Posted at 2019-01-31

はじめに

前回少しだけ触れたメモリアロケーションについて。

Rustは1.28からGlobal Allocatorという仕組みが使えるようになったので紹介したい。

「ラズパイ2で〜」と銘打ってはいるが、前回とは異なりCPUに依存する部分はあまり無い(アドレス幅を32bit決め打ちにしたりはしている)。

Global Allocatorとliballoc

ここに書いてあるとおりなのだがcore::alloc::GlobalAllocトレイトを実装して(ここではmem::KernelAllocatorというstructを実装した)

main.rs
#[global_allocator]
static GLOBAL: mem::KernelAllocator = mem::KernelAllocator;

変数名に特に縛りはなく#[global_allocator]を指定してやることでGlobal Allocatorとして使用されるようになる。

あとは

main.rs
#[macro_use]
extern crate alloc;

use alloc::prelude::*;

このように宣言してやることでliballocが使用できる、つまりたったこれだけで#[no_std]環境でBoxVecformat!が使えるようになるのだ!

「nightlyじゃなきゃベアメタル厳しいのどうなの?」と言われてしまうRustではあるが、liballocが手軽に使えるようになるGlobal Allocatorという仕組み一点だけとってもベアメタル環境でRustを選択するアドバンテージは計り知れないものがあると個人的には思っている。

ちなみにRust2018からは一般的にextern crateが不要になると理解されているが一部例外があり、上記extern crate alloc;はRust2018でも依然として必要となる。詳しくは以下参照。

実装例

linker.ld
    .kernelheap : {
        __kernel_heap_start__ = .;
        . += 0x100000;
    }

    . = ALIGN(4096);
    __kernel_heap_end__ = .;

リンカースクリプト内で固定領域を指定して__kernel_heap_start____kernel_heap_end__を定義。

mem.rs
extern {
    static __kernel_heap_start__: u32;
    static __kernel_heap_end__: u32;
}

#[inline]
unsafe fn kernel_heap_start() -> u32 {
    &__kernel_heap_start__ as *const _ as u32
}

#[inline]
unsafe fn kernel_heap_end() -> u32 {
    &__kernel_heap_end__ as *const _ as u32
}

Rust内でリンカースクリプトで定義したシンボルのアドレスを使えるようにする。

mem.rs
#[derive(Clone,Copy)]
struct FreeInfo {
    addr: u32,
    size: usize
}

const MAX_FREES:usize = 4090;

static mut FREES: usize = 0;
static mut FREE: [FreeInfo; MAX_FREES] = [FreeInfo{addr:0,size:0}; MAX_FREES];

pub unsafe fn init() {
    FREES = 1;
    FREE[0] = FreeInfo{
        addr: kernel_heap_start() ,
        size: (kernel_heap_end() - kernel_heap_start()) as usize
    };
}

メモリ管理の方法は『30日でできる!OS自作入門』にある空きメモリだけを管理するアルゴリズムを借用した。
最初は全メモリを持つ1つのFreeInfoだけがあり(init()参照)、そこから必要に応じてメモリを取り出していき、開放されたメモリは前後の領域とマージできない場合はFREE[]に追加されていく(KernelAllocator::dealloc()参照)。

mem.rs
unsafe impl GlobalAlloc for KernelAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        // ...
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        // ...
    }
}

#[alloc_error_handler]
fn foo(_: Layout) -> ! {
    uart::write("alloc_error");
    loop {}
}

実装の詳細は省略。

あとは上で記載したようにKernelAllocatorのstaticインスタンスを生成し#[global_allocator]を指定すればliballocが使用可能となる。

今後の課題とか

UserモードでMMUを使用したときのメモリアロケーションをどのように実装するべきか見当がついていない。Global Allocator内でプロセッサモードを見てUserモードであればSVCを実行するような作りになるのだろうか。

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3