「30日でできる!OS自作入門」をRustで。20日目

Posted on July 16, 2019 , Tags: OS自作入門, OS, Rust

「30日でできる!OS自作入門 」のC言語の部分をできるだけRustですすめてみる。今回は20日目の内容。

アプリケーション経由で文字を表示する

前回、アプリケーションの導入としてHLTするだけのasmファイルをアセンブル、hltコマンドとして実行できるようにした。
アプリケーションでできることを増やしていくにあたり、文字を表示してみる。

コンソールまわりのコード整理

コンソールまわりのコード整理をしたのでまずはそちらの説明をする。

上記のようなstructを定義し、そのメソッドとして各コマンドを実装した。
おおまかな内容は変わっていないため、ここでは詳細には記載しない。

割り込みハンドラの追加

アプリケーション経由で文字を表示する場合、レジスタに表示したい文字を入れておいてシステムコールすることになる。
このシステムコールを割り込みで処理する。

IDTに0x40として割り込みハンドラを追加した。

割り込み処理

CONSOLE_ADDR には、上記のConsole structの番地がはいっている。

コンソール側の処理修正

コンソール側の処理はfarjmpしていたところをfarcallにして、実行後、もどって処理を継続できるようにしている。

実行結果

hlt.asmを変更してhelloと表示するようにする。

far callされるのでfar returnするようにしている。

hltと入力してエンターキーを押すと、以下のようにコンソール画面にhelloと表示された。

hello

どんなファイル名でも呼び出せるようにする

これまで、hltというコマンドとしてhlt.binを呼び出していたが、他にも.binファイルを追加したらそのファイル名で呼び出せるようにする。

// console.rs

    fn run_cmd(&mut self, cmdline: [u8; MAX_CMD], memtotal: usize, fat: &[u32; MAX_FAT]) {
        self.cursor_x = 8;
        let cmdline_strs = cmdline.split(|s| *s == 0 || *s == b' ');
        let mut cmdline_strs = cmdline_strs.skip_while(|cmd| cmd.len() == 0);
        let cmd = cmdline_strs.next();
        if cmd.is_none() {
            self.display_error("Bad Command");
            return;
        }
        let cmd = cmd.unwrap();
        let cmd_str = from_utf8(&cmd).unwrap();
        if cmd_str == "mem" {
            self.cmd_mem(memtotal);
        } else if cmd_str == "clear" {
            self.cmd_clear();
        } else if cmd_str == "ls" {
            self.cmd_ls();
        } else if cmd_str == "cat" {
            self.cmd_cat(cmdline_strs, fat);
        } else {
            // 任意のコマンドを処理する関数を追加
            self.cmd_app(&cmd, fat);
        }
    }

    fn cmd_app<'a>(&mut self, filename: &'a [u8], fat: &[u32; MAX_FAT]) {
        let memman = unsafe { &mut *(MEMMAN_ADDR as *mut MemMan) };
        let mut finfo = search_file(filename);
        // 見つからなかったら、末尾に .bin をつけて再度探してみる
        if finfo.is_none() && filename[filename.len() - 2] != b'.' {
            let mut filename_ext = [b' '; MAX_CMD + 4];
            let filename_ext = &mut filename_ext[0..(filename.len() + 4)];
            filename_ext[..filename.len()].copy_from_slice(filename);
            filename_ext[filename.len()] = b'.';
            filename_ext[filename.len() + 1] = b'b';
            filename_ext[filename.len() + 2] = b'i';
            filename_ext[filename.len() + 3] = b'n';
            finfo = search_file(filename_ext);
        }
        if finfo.is_none() {
            self.display_error("Bad Command");
            return;
        }
        let finfo = finfo.unwrap();
        let content_addr = memman.alloc_4k(finfo.size).unwrap() as usize;
        finfo.load_file(content_addr, fat, ADR_DISKIMG + 0x003e00);
        let gdt_offset = 1003; // 1,2,3はdesciptor_table.rsで、1002まではmt.rsで使用済
        let gdt = unsafe { &mut *((ADR_GDT + gdt_offset * 8) as *mut SegmentDescriptor) };
        *gdt = SegmentDescriptor::new(finfo.size - 1, content_addr as i32, AR_CODE32_ER);
        farcall(0, gdt_offset * 8);
        memman.free_4k(content_addr as u32, finfo.size).unwrap();
        self.newline();
    }

実行結果

上記のhlt.asmhello.asmとして、hello.binにアセンブルするようにMakefileを変更した結果、以下の通り、helloでもhello.binでも実行できるようになった。

任意コマンド実行

システムコールの汎用化

文字列を表示するようなシステムコールを追加する。
IDTが枯渇しないように、IDTに追加していく形ではなく、0x40の割り込みハンドラ側で処理を分岐させる。

bin_apiではEDXの値でどの処理を行うかを決めるようにする。

これを使って、以下のhello.asmhello2.asmを実行する

hello.asmはEDXが1、hello2.asmはEDXが2になっている。

実行結果

実行してみると、以下の通りhelloでもhello2でもhelloという出力が得られた。

hello2

20日目は以上となる。ここまでの内容のコードはyoshitsugu/hariboteos_in_rustのday20としてタグを打ってある。