1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RustでUEFIのBIOSにHello World!を出す

Posted at

はじめに

UEFI仕様のBIOSにHello World!を表示するブートファイルをRustで作成します。
今回使用した開発環境は以下です。

  • CPUのアーキテクチャ:x86_64
  • 開発環境のホストOS:Windows11
  • 実行環境:devcontainer
  • エミュレーター:QEMU

環境構築

実行環境にはvscodeの拡張機能「devcontainer」を使っています。
検証はしておりませんが、devcontainerを使わない場合でも必要なものをインストールすれば、各自の環境でも開発は可能と思われます。

devcontainerの説明は割愛し、設定ファイルのみ公開します。

devcontainer.json
{
	"name": "Rust",
	"build": {
		"dockerfile": "Dockerfile"
	}
}
Dockerfile
FROM mcr.microsoft.com/devcontainers/rust:1-1-bullseye

RUN sudo apt update && apt upgrade -y && apt install qemu-system -y \
    && rustup target add x86_64-unknown-uefi && cargo install uefi-run
  • エミュレーター「QEMU」をインストールするためにapt install qemu-systemします。
  • RustでUEFIでの実行形式をターゲットとしてコンパイルするためにrustup target add x86_64-unknown-uefiします。
  • Rustランナーをインストールするためにcargo install uefi-runします。

3つ目のuefi-runのインストールは任意ですが、こちらを使えばターミナルでcargo runを実行するだけで、自動的にQEMUでコンパイルしたブートファイルが起動します。

各種設定

devcontainerを起動後、Rustの設定をしていきます。
まずはcargo initを実行して、作業ディレクトリに.cargo/config.tomlを追加してください。

.cargo/config.toml
[build]
target = "x86_64-unknown-uefi"

[target.x86_64-unknown-uefi]
runner = "uefi-run"

実装

srcディレクトリで必要なプログラムを実装していきます。

src/uefi.rs
use core::ffi::c_void;

pub type EfiStatus = usize;
pub type EfiHandle = *const c_void;

#[repr(C)]
struct EfiTableHeader {
    signature: u64,
    revision: u32,
    header_size: u32,
    crc32: u32,
    reserved: u32,
}

#[repr(C)]
pub struct EfiSystemTable {
    hdr: EfiTableHeader,
    firmware_vendor: *const u16,
    firmware_revision: u32,
    console_in_handle: EfiHandle,
    con_in: usize,
    console_out_handle: EfiHandle,
    con_out: *const EfiSimpleTextOutputProtocol,
}

impl EfiSystemTable {
    pub fn con_out(&self) -> &EfiSimpleTextOutputProtocol {
        unsafe { &(*self.con_out) }
    }
}

#[repr(C)]
pub struct EfiSimpleTextOutputProtocol {
    reset: unsafe fn(this: &Self, extended_verification: bool) -> EfiStatus,
    output_string: unsafe fn(this: &Self, string: *const u16) -> EfiStatus,
}

impl EfiSimpleTextOutputProtocol {
    pub fn reset(&self, extended_verification: bool) -> EfiStatus {
        unsafe { (self.reset)(self, extended_verification) }
    }

    pub fn output_string(&self, string: *const u16) -> EfiStatus {
        unsafe { (self.output_string)(self, string) }
    }
}

以下のUEFIの仕様書を参考にして、必要なプロトコルの呼び出し部分だけ実装しています。

src/main.rs
#![no_std]
#![no_main]

mod uefi;

#[no_mangle]
pub fn efi_main(
    _image_handle: uefi::EfiHandle,
    system_table: &uefi::EfiSystemTable,
) -> uefi::EfiStatus {
    system_table.con_out().reset(false);
    system_table.con_out().output_string(
        [
            0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0,
        ]
        .as_ptr(),
    );

    loop {}
}

#[cfg(not(test))]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}

0x48, 0x65, 0x6c, 0x6c, 0x6f, ...はそれぞれ表示する一文字に対応しています。

実行

cargo runで実行して、しばらく待つとHello World!が表示されます。

image.png

おわりに

ソースコードの説明が少なくて申し訳ありませんが、気が向いたときに追加するかもしれません。
RustでのOS開発を進めていく予定ですので、進捗がありましたら共有いたします。

参考書

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?