mikanOS 3章を整理 [後編]


はじめに

前回の記事では何もしない kernel を
ブートしていました。
今回はカーネルから画面の色を
変更する所まで整理したいと思います。

⇦前|mikanOS 3章を整理[前編] || mikanOS 4章を整理[前編]|後⇨

ブートローダーから画像を変えるフロー

まずは画素を変える事をゴールに
フローを整理してみます。

Graphics Output Protocol を検索

LocateHandleBuffer() を使います。
検索したプロトコルを workmemory に用意した領域に一度貯めます。
ただココで注意なのは貯めたプロトコルは EFI_HANDLE であることです。

Graphics Output Protocol を Open

OpenProtocol を使っていますが、
実際は HandleProtocol に変形させて使っています。


[注意]
以下にあるように HandleProtocol ではなく、
OpenProtocol の使用を推奨しています。

メモリ領域の割り当て

Description の最後の方に記載があるが、Buffer を使うのであれば
AllocatePool() を call する必要があると書いてある。
おそらく章を追うと何処かで追加されると思う。

Frame Buffer Base で色指定

まずは画素の定義を確認。Blt() で行けそうな気がした。

しかし、3章では Blt() を使わない
Frame Buffer Base を使う
sample コードを参考に UEFI の規格を追ってみた。


なるほど。
FrameBufferSize も取り出して for 分を回せば、
全ピクセルの色を指定したプログラムができる。

プログラムで総括

Graphic_Open.c

EFI_STATUS OpenGOP(EFI_HANDLE image_handle,
                   EFI_GRAPHICS_OUTPUT_PROTOCOL** gop) {
  UINTN num_gop_handles = 0;
  EFI_HANDLE* gop_handles = NULL;
  /*search the Graphic open protocol*/
  gBS->LocateHandleBuffer(
      ByProtocol,
      &gEfiGraphicsOutputProtocolGuid,
      NULL,
      &num_gop_handles,
      &gop_handles);
  /*open the Graphic open protocol*/
  gBS->OpenProtocol(
      gop_handles[0],
      &gEfiGraphicsOutputProtocolGuid,
      (VOID**)gop,
      image_handle,
      NULL,
      EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL);
  /*alocate memory area for system*/
  FreePool(gop_handles);

  return EFI_SUCCESS;
}

EFI_STATUS EFIAPI UefiMain(
    EFI_HANDLE image_handle,
    EFI_SYSTEM_TABLE* system_table) 
  {
    //open graphic protocol
    EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
    status = OpenGOP(image_handle, &gop);
    if (EFI_ERROR(status)) {Print(L"Fail to open graphic\n");}

    //write graphic
    UINT8* frame_buffer = (UINT8*)gop->Mode->FrameBufferBase;
    for (UINTN i = 0; i < gop->Mode->FrameBufferSize; ++i) {
        frame_buffer[i] = 255;
    }

    Print(L"All done\n");
    while (1);
    return EFI_SUCCESS;
}

その他メモ

グラフィックの操作は Console I/O protocol に分類されます。
UEFI specification によると、Boot service 中に有効となる protocol になります。

カーネルから Frame Buffer base を制御するために

kernel ファイルの引数として
FrameBufferBase, FrameBufferSize
の 2 つが必要です。

追加してみましょう。

compare.c
  /*[before]*/
    typedef void EntryPointType(void);
    EntryPointType* entry_point = (EntryPointType*)entry_addr;
    entry_point();

  /*[after]*/
    typedef void EntryPointType(UINT64, UINT64);
    EntryPointType* entry_point = (EntryPointType*)entry_addr;
    entry_point(gop->Mode->FrameBufferBase, gop->Mode->FrameBufferSize);

念のため kernel 側のコードも載せておきます。

main.cpp
#include <cstdint>

extern "C" void KernelMain(uint64_t frame_buffer_base,
                           uint64_t frame_buffer_size) {
  uint8_t* frame_buffer = reinterpret_cast<uint8_t*>(frame_buffer_base);
  for (uint64_t i = 0; i < frame_buffer_size; ++i) {
    frame_buffer[i] = i % 256;
  }
  while (1) __asm__("hlt");
}