ゼロからのOS自作入門(その4)osbook_day03c の続き。
4日目はUEFIアプリからグラフィックスの情報をカーネルに渡します。今、UEFIアプリがC、カーネルをRustで書いているので、CとRustの間で構造体のやりとりが必要です。
std::os::raw を使う
実践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 というクレート使ってできないですか?https://t.co/VJdPzHebo0
— 基礎から学ぶ 組込みRust 好評発売中 (@tnakabayashi) 2021年5月6日
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からもらったグラフィックスの情報を使って、座標を指定して色を塗ることが出来るようになりました。
全ソース
カーネル側の全ソースです。四日目分はまだ作業中。
#![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") } } }