個人用雑記

勉強したことを書いていければなーと

自作OS、始まります! #5

始まります

OSの自作が

さて、いきなりですが今まで書いてきたものは自作OSではありませんでした。(今回も自作OS色はないですが。)言うなればUEFIアプリケーションですかね。
いよいよカーネル部分を書き始めたので、ついにタイトル通りの自作OS、始まります!というわけです。
ちなみにまだブートローダーは完成していません。。。

あ、前回で掲げた目標のファイル周りは次回以降(多分)でローダー部分を書く際に使うので、そこで書く予定です。

ブートローダーとしてのUEFI

BootServicesとRuntimeServices

これまではUEFIを疑似的なOSとして扱ってきました。そのため、実装してきたプロトコルを呼び出すだけで文字の描画やキーボード入力など、色々なことを行うことができました。それらは基本的にBootServicesと呼ばれる、カーネルを呼び出す前に使うことのできる関数群です。しかし、ブートローダーとしてUEFIを使う場合、カーネルをブートして処理を移すとそれらは使えなくなり、以降使えるのはRuntimeServicesにある関数群だけになります。
当然キーボードやマウスの入力のためには割り込み処理を自分で書く必要がありますし、メモリ管理や画面の描画に至るまでほぼ全てを自分で書く必要があります。逆に言えば、これらを自分で書いてこその自作OSという話ですね。楽しみです。

ExitBootServices

上記のBootServicesの環境からRuntimeServicesの環境に移すために呼び出す関数がExitBootServices関数です。この関数を呼び出した後(厳密には呼び出しに成功した後)はRuntimeServicesのみ使用できるようになります。
このExitBootServices関数ですが、適切に呼び出さないと失敗する場合があるそうです。参考↓

neriring.hatenablog.jp

ExitBootServices関数を呼び出してRuntimeServicesの環境に移った後は自作したカーネルをメモリにロードして、カーネルのエントリーポイントへジャンプすればブートローダーとしてのUEFIの役割は終了です。

とりあえず文字を出す

まぁ、初歩として典型ですね。とりあえずUEFIにあったSIMPLE_TEXT_OUTPUT_PROTOCOL内の関数に頼らない文字描画から始めていきます。

画面に描画する

BootServices環境時に使ったGRAPHICS_OUTPUT_PROTOCOLですが、この中のMode->FrameBufferBaseに描画する画面の先頭アドレスが取得されています。そして、カーネルに対してこのアドレスさえ渡しておけばRuntimeServices環境でも同様に画面に描画することが可能です。
ある(x, y)という座標に対する描画は以前のdraw_pixel関数とほとんど同様の実装で行えます。

typedef struct
{
    unsigned char Blue;
    unsigned char Green;
    unsigned char Red;
    unsigned char Reserved;
} PixelFormat;

typedef struct
{
    unsigned long long *FrameBufferBase;
    unsigned long long FrameBufferSize;
    unsigned int ResolutionH;
    unsigned int ResolutionV;
} FrameBuffer;

void draw_pixel(unsigned int x, unsigned int y, PixelFormat color, FrameBuffer *fb)
{
    PixelFormat *base = reinterpret_cast<PixelFormat *>(fb->FrameBufferBase);
    PixelFormat *point = base + (fb->ResolutionH * y) + x;

    point->Blue = color.Blue;
    point->Green = color.Green;
    point->Red = color.Red;
    point->Reserved = color.Reserved;
}

上記におけるResolutionHやResolutionVも同様にBootServices環境におけるMode->Info内にあるので、BootService環境時に保存しておきます。

フォントの準備

はりぼてOSでも使われているフォントを使用させていただきました。今回はfont.hとしてソースコードに直接埋め込む形を取りました。こうしておくことで以下のようなコードで文字を描画できるようになります。

void puts_font(FrameBuffer *fb, char *str)
{
    int basex = 1, basey = 1;
    for (; *str != '\0'; ++str)
    {
        if (*str == '\n')
        {
            basex = 1;
            basey += 19;
            continue;
        }
        for (int y = 0; y < 16; ++y)
        {
            for (int x = 0; x < 8; ++x)
            {
                if (font_map[*str][y][x])
                {
                    draw_pixel(basex + x, basey + y, Color_White, fb);
                }
                else
                {
                    draw_pixel(basex + x, basey + y, Color_Black, fb);
                }
            }
        }
        basex += 9;
    }
}

まだ簡易的なものなので、単純に表示したい文字のfont_mapを見て、0なら黒を1なら白を描画しているだけです。一応改行文字の場合は各位置を次の行の先頭にしています。
そんなこんなでとりあえず文字を描画してみました。 f:id:rei_624:20200227025156j:plain
フォントが自分で描画した感がとても出ていていいですね!

次回以降の目標

現在進行形でやっているところですが、まずはカーネルのローダーの実装ですかね。とりあえずカーネルをELF形式で出力したので、次はメモリにコピーするコードを書こうというところです。