自作OSに挑戦する日記 27日目

 「30日でできる!OS自作入門」を読んで分かったことや、とりあえず書いておきたいことなどを書いていきます。
 この本はChapterが1から30まであるので、各チャプター毎に1記事書いていきます。

Chapter 27 「LDTとライブラリ」

今回やった内容

  • バグ修正
  • アプリ実行中でもコンソールウィンドウを閉じれるように
  • アプリを守ろう
  • ファイル・Makefile整理

環境

作業記録

バグ修正

 前回追加した open コマンドで立ち上げたアプリは「x」をクリックしても終了できないというバグがありました…。 アプリはキー入力やタイマなどの受信処理以外は常にスリープしているため、終了メッセージがFIFOに追加されても気づくことができません。なので、メッセージをFIFOに追加した後に task_run を呼び、終了メッセージを強制的に確認させるようにしました。

/* bootpack.c */

// コンソール上のアプリケーションを強制終了する(backspace + shift)
if(data == 256 + 0x0e && key_shift){
    struct TASK *task = key_to_window->task;
    if(task != 0 && task->tss.ss0 != 0){
        // 強制終了割り込み表示
        struct CONSOLE *console = task->console;
        console_putstr(console, "\nKeyboard Interrupt\n");

        // end_appに飛ぶようにする
        io_cli();
        task->tss.eax = (int) &(task->tss.esp0);
        task->tss.eip = (int) end_app;
        io_sti();
        task_run(task, -1, 0);
        continue;
    }
}

アプリ実行中でもコンソールを閉じれるように

 現在のHariboteOSは、アプリ実行中のコンソールウィンドウの「x」を押してもコンソールを閉じることが出来ないようになっています(コンソールへの入力は全てアプリ側で受信しているため)。これを直すために、アプリ側でも終了メッセージを待機しておいて、受信時には対応するコンソールを終了させる処理を実行するようにします。

f:id:guguru0014:20190519010840p:plain
イメージ

/* api.c */

// FIFOから取り出したデータが終了メッセージだった
if(data == 4){
    timer_cancel(console->cursor_timer);
    io_cli();
    fifo32_put(osfifo, console->sheet - sheet_ctl->sheet_data + 2024);
    console->sheet = 0;
    io_sti();
}
/* bootpack.c */

// コンソールウィンドウを消す、タスクは動かしたまま
if(2024 <= data && data <= 2279){
    struct SHEET *sheet = sheet_ctl->sheet_data + (data - 2024);
    memman_free_4k(memman, (int) sheet->buf, 400 * 300);
    sheet_free(sheet);
}

アプリケーションを守ろう

 HariboteOSでのアプリ実行に1つ重大な欠陥があったので直します。欠陥とは、あるアプリが他のアプリのセグメントにアクセスする事が出来るというものです。

f:id:guguru0014:20190519012115p:plain
悪意のあるアプリを動かすと…

 この欠陥を直すために、LDT(Local Descriptor Table)を使います。GDT(Grobal Descriptro Table)とは少し異なり、GDTは動作している全てのタスクから共通で使えるセグメントを設定するのに対し、LDTはあるタスク内のみで有効なセグメントを設定する事ができます。このLDTを使って、アプリ内のセグメントへ外部からのアクセスが出来ないようにします。

 タスクを管理する構造体でLDT情報を扱えるようにし…

/* bootpack.h */

struct TASK{
    int sel, flags;
    int level, priority;
    struct FIFO32 fifo;
    struct TSS32 tss;
    struct SEGMENT_DESCRIPTOR ldt[2];
    struct CONSOLE *console;
    int ds_base, stack_addr;
};

 タスク初期化時にLDTのアドレスを記録して…

/* multitask.c */

// TASK_CTL初期化
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
task_ctl = (struct TASK_CTL *) memman_alloc_4k(memman, sizeof(struct TASK_CTL));
for(int i = 0; i < MAX_TASKS; ++ i){
    task_ctl->tasks[i].flags = 0;
    task_ctl->tasks[i].sel = (TASK_GDT0 + i) * 8;
    task_ctl->tasks[i].tss.ldtr = (TASK_GDT0 + i + MAX_TASKS) * 8;
    task_ctl->tasks[i].priority = 1;
    set_segmdesc(gdt + TASK_GDT0 + i, 103, (int) &task_ctl->tasks[i].tss, AR_TSS32);
    set_segmdesc(gdt + TASK_GDT0 + i + MAX_TASKS, 15, (int) task_ctl->tasks[i].ldt, AR_LDT);
}

 アプリ起動時にセグメントの設定を行うようにします。

/* console.c */

// セグメント設定
// アクセス権に0x60を足すとアプリケーション用のセグメントになる
task->ds_base = (int) app_mem_addr;
set_segmdesc(task->ldt + 0, file_info->size - 1, (int) exec_addr, AR_CODE32_ER + 0x60);
set_segmdesc(task->ldt + 1, seg_size - 1, (int) app_mem_addr, AR_DATA32_RW + 0x60);

~~~

// 実行
start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));

 アプリ内のセグメントへの外部からのアクセスが例外として扱われるようになりました〜!

f:id:guguru0014:20190519013254p:plain

ファイル・Makefile整理

 ファイルを整理し、OSとアプリのソースを分離していきます。ファイルを整理して…Makefileを変更して…を繰り返し、最終的には以下のような構成になりました。

├──    Makefile
├──    app_make.txt
└──    appbase/
 │  ├────    Makefile
 │  ├────    api_link_head.h
 │  ├────    api_link_obj.asm
 │  ├────    har.ld
 │  └────    msprintf.c
 └──    build_result/
 │  ├────    colorapp.hrb
 │  ├────    haribote.img
 │  ├────    haribote.sys
 │  ├────    timerapp.hrb
 │  └────    walkapp.hrb
 └──    colorapp/
 │  ├────    Makefile
 │  ├────    api_link_head.h
 │  ├────    api_link_obj.asm
 │  ├────    colorapp.c
 │  ├────    har.ld
 │  └────    msprintf.c
 └──    haribote/
 │  ├────    Makefile
 │  ├────    api.c
 │  ├────    asmhead.asm
 │  ├────    bootpack.c
 │  ├────    bootpack.h
 │  ├────    bootpack.map
 │  ├────    console.c
 │  ├────    desctbl.c
 │  ├────    fifo32.c
 │  ├────    fifo8.c
 │  ├────    file.c
 │  ├────    functions.c
 │  ├────    graphic.c
 │  ├────    hankaku.c
 │  ├────    har.ld
 │  ├────    int.c
 │  ├────    inthandler.c
 │  ├────    ipl10.asm
 │  ├────    keyboard.c
 │  ├────    memory.c
 │  ├────    mouse.c
 │  ├────    msprintf.c
 │  ├────    multitask.c
 │  ├────    nasmfunc.asm
 │  ├────    other_disk.asm
 │  ├────    sheet.c
 │  ├────    timer.c
 │  └────    window.c
 └──    other/
 │  ├────    hankaku.txt
 │  ├────    har.ld
 │  └────    makefont.py
 ├──    reference.md
 └──    timerapp/
 │  ├────    Makefile
 │  ├────    api_link_head.h
 │  ├────    api_link_obj.asm
 │  ├────    har.ld
 │  ├────    msprintf.c
 │  └────    timerapp.c
 └──    walkapp/
 │  ├────    Makefile
 │  ├────    api_link_head.h
 │  ├────    api_link_obj.asm
 │  ├────    har.ld
 │  ├────    msprintf.c
 │  └────    walkapp.c

 OS本体のソースコードharibote 内に、アプリはそれぞれにディレクトリを与えてビルドやリンクをアプリ単位で行うようにしました。
 整理に少し時間はかかりましたが…これまでに比べ格段にファイルの管理がし易くなりました!

まとめ

 実装!実装!だった24~26日目と比べ、今回の内容は優しめでした(時間的にも実装量的にも)。見た目や動作には影響しませんが、ファイル管理がし易くなったのが今回1番嬉しいところです。
 残り3日、自作OS本も遂に終わりが見えてきました…!