ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

ゼロからのOS自作入門(その5)RustでCの構造体を受け取る

ゼロからのOS自作入門(その4)osbook_day03c の続き。

natsutan.hatenablog.com

4日目はUEFIアプリからグラフィックスの情報をカーネルに渡します。今、UEFIアプリがC、カーネルをRustで書いているので、CとRustの間で構造体のやりとりが必要です。

std::os::raw を使う

gihyo.jp

実践Rust入門を見ると、#[repr(C)]とstd::os::rawのやり方が紹介されています。

本を見ながら、こんな感じで書いてみました。

use std::os::raw::c_int;
use std::os::raw::c_uint;
use std::os::raw::c_uchar;

#[repr(C)]
struct FrameBufferConfig {
    frame_buffer : *mut c_uchar,
    pixels_per_scan_line : c_int,
    horizontal_reslution: c_uint,
    vertical_resolution: c_uint,
    pixel_format : PixelFormat
}

実際にやってみるとエラーがでます。

error[E0433]: failed to resolve: use of undeclared crate or module `std`
 --> src/main.rs:7:5
  |
7 | use std::os::raw::c_int;
  |     ^^^ use of undeclared crate or module `std`

#![no_std] を設定しているので、stdから始まるクレートが使えないようです。

困っていたらTwitterで的確なアドバイスをいただきました。いつもありがとうございます。

ctyを使う

使い方はとても簡単で、cty::から使いたい型をuseするだけ。

use cty::{uint32_t, c_uchar};

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct FrameBufferConfig {
    frame_buffer : *mut c_uchar,
    pixels_per_scan_line : uint32_t,
    horizontal_reslution: uint32_t,
    vertical_resolution: uint32_t,
    pixel_format : PixelFormat
}


#[no_mangle]
pub extern "C" fn KernelMain(frame_buffer_config: *mut FrameBufferConfig) -> ! {

    let fb_buffer_config = unsafe {*frame_buffer_config};

こんな記述でUEFI側を変更すること無く構造体の受け渡しが出来ました。

C言語側の構造体定義はこうなっています。

struct FrameBufferConfig {
    uint8_t *frame_buffer;
    uint32_t pixels_per_scan_line;
    uint32_t horizontal_reslution;
    uint32_t vertical_resolution;
    enum PixelFormat pixel_format;
};

C言語側もint等を使わずにビット幅を指定しているので、上手く渡せているようです。

これでUEFIからもらったグラフィックスの情報を使って、座標を指定して色を塗ることが出来るようになりました。

f:id:natsutan:20210509103009j:plain

全ソース

カーネル側の全ソースです。四日目分はまだ作業中。

#![no_std]
#![no_main]

#![feature(asm)]
#![feature(abi_efiapi)]

use cty::{uint32_t, c_uchar};

extern crate rlibc;
extern crate panic_halt;


#[derive(Debug, Copy, Clone)]
#[repr(C)]
enum PixelFormat {
    KPixelRGBResv8bitPerColor,
    KPixelBGRResv8BitPerColor
}

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct FrameBufferConfig {
    frame_buffer : *mut c_uchar,
    pixels_per_scan_line : uint32_t,
    horizontal_reslution: uint32_t,
    vertical_resolution: uint32_t,
    pixel_format : PixelFormat
}


#[derive(Debug, Copy, Clone)]
struct PixelColor {
    r : u8,
    g : u8,
    b : u8
}


fn pixel_offset(x:u32, y:u32, config: &FrameBufferConfig) -> u32 {
    4 * (config.pixels_per_scan_line * y + x)
}

fn write(x:u32, y:u32, pixel:PixelColor, config: &FrameBufferConfig) {
    let offset = pixel_offset(x, y, config);
    unsafe {
        *(config.frame_buffer).offset(offset as isize) = pixel.r;
        *(config.frame_buffer).offset(offset as isize + 1) = pixel.g;
        *(config.frame_buffer).offset(offset as isize + 2) = pixel.b;
    }
}


#[no_mangle]
pub extern "C" fn KernelMain(frame_buffer_config: *mut FrameBufferConfig) -> ! {

    let fb_buffer_config = unsafe {*frame_buffer_config};

    let white = PixelColor{r:255, b:255, g:255};
    for y in 0..fb_buffer_config.vertical_resolution {
        for x in 0..fb_buffer_config.horizontal_reslution {
            write(x, y, white, &fb_buffer_config);
        } 
    }
    
    let green = PixelColor{r:0, g:255, b:0};
    for y in 0..200 {
        for x in 0..200 {
            write(x, y, green, &fb_buffer_config);
        } 
    }


    loop {
        unsafe {
            asm!("hlt")
        }
    }
}