はじめに
前回の記事では何もしない 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 分を回せば、
全ピクセルの色を指定したプログラムができる。
プログラムで総括
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 つが必要です。
追加してみましょう。
/*[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 側のコードも載せておきます。
#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");
}