Top

第5章 BootServicesやRuntimeServicesのその他の機能

EFI_BOOT_SERVICESや、まだ機能の紹介はしていなかったEFI_RUNTIME_SERVICESには、他にも色々な機能があります。この章ではそれらの一部を紹介します。

5.1 メモリアロケータを使う

UEFIのファームウェアはメモリアロケータを持っていて、EFI_BOOT_SERVICESのAllocatePool()とFreePool()で利用できます(リスト5.1)。

サンプルのディレクトリは"050_bs_malloc"です。

リスト5.1: AllocatePool()とFreePool()の定義(efi.hより)

enum EFI_MEMORY_TYPE {
        ・・・
        EfiLoaderData,
                /* ロードされたUEFIアプリケーションのデータと
                 * UEFIアプリケーションのデフォルトのデータアロケーション領域 */
        ・・・
};

struct EFI_SYSTEM_TABLE {
        ・・・
        struct EFI_BOOT_SERVICES {
                ・・・
                //
                // Memory Services
                //
                unsigned long long _buf3[3];
                unsigned long long (*AllocatePool)(
                        enum EFI_MEMORY_TYPE PoolType,
                                /* どこのメモリプールから確保するかを指定
                                 * この節では"EfiLoaderData"を指定 */
                        unsigned long long Size,
                                /* 確保するサイズをバイト単位で指定 */
                        void **Buffer
                                /* 確保した領域の先頭アドレスを格納するポインタ
                                 * のポインタを指定 */
                        );
                unsigned long long (*FreePool)(
                        void *Buffer
                                /* 開放したい領域のポインタを指定 */
                        );
                ・・・
        } *BootServices;
};

AllocatePool()第1引数の"PoolType"について、"enum EFI_MEMORY_TYPE"には他にもメモリタイプがありますが、仕様書を読む限り、UEFIアプリケーションのデータを配置する領域は"EfiLoaderData"であるようで、この節では"EfiLoaderData"を使用しています。その他のメモリタイプについては仕様書の"6.2 Memory Allocation Services"を見てみてください。

使用例はリスト5.2の通りです。

リスト5.2: AllocatePool()とFreePool()の使用例(main.cより)

 1: #include "efi.h"
 2: #include "common.h"
 3: #include "graphics.h"
 4: 
 5: #define IMG_WIDTH   256
 6: #define IMG_HEIGHT  256
 7: 
 8: void efi_main(void *ImageHandle __attribute__ ((unused)),
 9:           struct EFI_SYSTEM_TABLE *SystemTable)
10: {
11:     unsigned long long status;
12:     struct EFI_GRAPHICS_OUTPUT_BLT_PIXEL *img_buf, *t;
13:     unsigned int i, j;
14: 
15:     efi_init(SystemTable);
16:     ST->ConOut->ClearScreen(ST->ConOut);
17: 
18:     /* 画像バッファ用のメモリを確保 */
19:     status = ST->BootServices->AllocatePool(
20:             EfiLoaderData,
21:             IMG_WIDTH * IMG_HEIGHT *
22:             sizeof(struct EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
23:             (void **)&img_buf);
24:     assert(status, L"AllocatePool");
25: 
26:     /* 画像を生成 */
27:     t = img_buf;
28:     for (i = 0; i < IMG_HEIGHT; i++) {
29:             for (j = 0; j < IMG_WIDTH; j++) {
30:                     t->Blue = i;
31:                     t->Green = j;
32:                     t->Red = 0;
33:                     t->Reserved = 255;
34:                     t++;
35:             }
36:     }
37: 
38:     /* 画像描画(フレームバッファへ書き込み) */
39:     blt((unsigned char *)img_buf, IMG_WIDTH, IMG_HEIGHT);
40: 
41:     /* 確保したメモリを解放 */
42:     status = ST->BootServices->FreePool((void *)img_buf);
43:     assert(status, L"FreePool");
44: 
45:     while (TRUE);
46: }

リスト5.2では、255x255の画像用バッファ(img_buf)を確保してピクセルデータを配置し、blt()でフレームバッファへの書き込みを行っています。

生成している画像はX軸に青色を、Y軸に緑色を、それぞれ255階調で表示するものです。

試してみると図5.1のような感じです。印刷は白黒なので、ぜひご自身で試してみてください。

AllocatePool()とFreePool()の実行例

図5.1: AllocatePool()とFreePool()の実行例

5.2 シャットダウンする

メモリ上で動作するだけのUEFIアプリケーションならば電源ボタンで終了しても良いのですが、EFI_RUNTIME_SERVICESのResetSystem()を使用するとPCをシャットダウンしたり、再起動したりできます。

サンプルのディレクトリは"051_rs_resetsystem"です。

EFI_RUNTIME_SERVICESもEFI_BOOT_SERVICES同様にEFI_SYSTEM_TABLEのメンバです。EFI_BOOT_SERVICESはブートローダーに対して機能を提供しているのに対し、EFI_RUNTIME_SERVICESはOS起動後も使用できるという違いがあります。

具体的な契機はEFI_BOOT_SERVICESのExitBootServices()で、この関数を呼ぶと、それ以降EFI_BOOT_SERVICESの機能は無効になりますが、EFI_RUNTIME_SERVICESの機能は引き続き使用可能です。

そして、ResetSystem()はEFI_RUNTIME_SERVICES内でリスト5.3の様に定義されています。

リスト5.3: ResetSystem()の定義(efi.hより)

enum EFI_RESET_TYPE {
        /* 説明は仕様書("7.5.1 Reset System")の意訳です。 */
        EfiResetCold,
                /* システム全体のリセット。
                 * システム内の全ての回路を初期状態へリセットする。
                 * (追記: いわゆる再起動で、CPU以外の回路も
                 * 電気的に遮断するコールドリセット)
                 * このリセットタイプはシステム操作に対し非同期で、
                 * システムの周期的動作に関係なく行われる。 */
        EfiResetWarm,
                /* システム全体の初期化。
                 * プロセッサは初期状態へリセットされ、
                 * 保留中のサイクルは破壊されない(?)。
                 * (追記: いわゆる再起動で、CPUのみリセットするウォームリセット)
                 * もしシステムがこのリセットタイプをサポートしていないならば、
                 * EfiResetColdが実施されなければならない。 */
        EfiResetShutdown,
                /* システムがACPI G2/S5あるいはG3状態に相当する電源状態へ
                 * 遷移する(追記: いわゆるシャットダウンの状態)。
                 * もしシステムがこのリセットタイプをサポートしておらず、
                 * システムが再起動した時、EfiResetColdの振る舞いを示すべき。 */
        EfiResetPlatformSpecific
                /* システム全体のリセット。
                 * 厳密なリセットタイプは引数"ResetData"に従うEFI_GUID
                 * により定義される。
                 * プラットフォームがResetData内のEFI_GUIDを認識できないならば、
                 * サポートできるリセットタイプを選択しなければならない。
                 * プラットフォームは発生した非正常なりセットから
                 * パラメータを記録することができる(?)。 */
};

struct EFI_SYSTEM_TABLE {
        ・・・
        struct EFI_RUNTIME_SERVICES {
                ・・・
                //
                // Miscellaneous Services
                //
                unsigned long long _buf_rs5;
                void (*ResetSystem)(
                        enum EFI_RESET_TYPE ResetType,
                                /* 実施されるリセットタイプ
                                 * この節ではEfiResetShutdownを使用 */
                        unsigned long long ResetStatus,
                                /* リセットのステータスコードを指定
                                 * システムのリセットが
                                 * 正常なものならばEFI_SUCCESS、
                                 * 異常によるものならばエラーコードを指定
                                 * この節ではEFI_SUCCESS(=0)を指定 */
                        unsigned long long DataSize,
                                /* ResetDataのデータサイズをバイト単位で指定
                                 * この節ではResetDataは使用しないので0を指定 */
                        void *ResetData
                                /* ResetTypeがEfiResetCold・EfiResetWarn
                                 * ・EfiResetShutdownの時、
                                 * ResetStatusがEFI_SUCCESSで無いならば、
                                 * ResetDataへNULL終端された文字列を指定することで、
                                 * 後々、呼び出し元へリセット事由を通知できる
                                 * (ようである)
                                 * ResetTypeがEfiResetPlatformSpecificの時、
                                 * EFI_GUIDをNULL終端文字列として指定する事で、
                                 * プラットフォーム依存のリセットを行える
                                 * この節ではResetStatusはEFI_SUCCESSなので、
                                 * 使用しない(NULLを指定する) */
                        );
        } *RuntimeServices;
};

使用例はリスト5.4の通りです。

リスト5.4: ResetSystem()の使用例(main.cより)

 1: #include "efi.h"
 2: #include "common.h"
 3: 
 4: void efi_main(void *ImageHandle __attribute__ ((unused)),
 5:           struct EFI_SYSTEM_TABLE *SystemTable)
 6: {
 7:     efi_init(SystemTable);
 8:     ST->ConOut->ClearScreen(ST->ConOut);
 9: 
10:     /* キー入力待ち */
11:     puts(L"何かキーを押すとシャットダウンします。。。\r\n");
12:     getc();
13: 
14:     /* シャットダウン */
15:     ST->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SUCCESS, 0,
16:                                      NULL);
17: 
18:     while (TRUE);
19: }

リスト5.4は何かキーを入力するとシャットダウンします。

実行例は図5.2の通りです。

ResetSystem()の実行例

図5.2: ResetSystem()の実行例


Top