iT邦幫忙

2023 iThome 鐵人賽

DAY 14
1

嗨我是k66,承上篇講完INF與DSC,今天實作C。


開始前介紹一些以下程式碼會用到的概念:

  • 6個名詞解釋:gBSBltGOPProtocolHandleBuffer
    • 要講gBS順便提gST:
      • gST:EFI的global System Table
      • gBS:EFI的global Boot Service,gST->BootServices即為gBS,以下程式碼用到gBS的地方有gBS->LocateHandleBuffer()
    • GOP:圖形輸出協議,在c檔內是EFI_GRAPHICS_OUTPUT_PROTOCOL型別。
    • Blt:將Buffer內的資料傳送給Block Buffer,在c檔內用法是Gop->Blt()
    • Protocol:用法有: EFI_FILE_PROTOCOLEFI_GRAPHICS_OUTPUT_PROTOCOL
    • Handle:用到的地方有EFI_HANDLEEFI_FILE_PROTOCOL
    • Buffer:可以是HandleFileBlt的緩衝區。

放碼上來!程式碼連結


  • OinkBL.c
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h> //include此Library可直接使用gBS,不然則須寫EFI_BOOT_SERVICES *gBS = SystemTable->BootServices;
#include <Library/BmpSupportLib.h>
#include <Library/MemoryAllocationLib.h>
#include <IndustryStandard/Bmp.h>
#include <Protocol/SimpleFileSystem.h>
#include <Guid/FileInfo.h>

EFI_STATUS
EFIAPI
UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS Status = EFI_SUCCESS;
    /* LocateHandleBuffer() */
    UINTN NoHandles = 0;
    EFI_HANDLE *Buffer = NULL;
    Status = gBS->LocateHandleBuffer( // [註1]
        ByProtocol,
        &gEfiGraphicsOutputProtocolGuid,
        NULL,
        &NoHandles,
        &Buffer
    );

    Print(L"Status = %d.\n", Status);

    if(EFI_ERROR(Status)) { // 開發UEFI時隨時檢查並印出可能有錯誤處,方便日後查看報錯訊息。
        Print(L"Failed to LocateHandleBuffer.\n");
        return Status;
    }
    Print(L"Hello gEfiGraphicsOutputProtocolGuid Protocol.\n");
    Print(L"Number of Handles = %d.\n", NoHandles);
    

    // OpenProtocol
    EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
    Status = gBS->OpenProtocol( // 把Drvier綁至Controller,OpenProtocol這個structure有四個變數: QueryMode, SetMode, Blt, *Mode。
        Buffer[0],
        &gEfiGraphicsOutputProtocolGuid,
        (VOID **)&Gop,
        ImageHandle,
        NULL,
        EFI_OPEN_PROTOCOL_GET_PROTOCOL
    );

    Print(L"Status = %d.\n", Status);
    if(EFI_ERROR(Status)) {
        Print(L"Failed to OpenProtocol\n");
        return Status;
    }

    /* 輸出螢幕分辨率:在UEFI BIOS時需手動調整螢幕解析度,進OS後就不用(Windows/Linux/MacOS會自動調整)。*/

    /* QueryMode查詢顯卡支援的顯示模式 */
    UINTN SizeofInfo = 0;
    EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
    for(UINT8 i = 0; i < Gop->Mode->MaxMode; i++)
    {
        Status = Gop->QueryMode(
            Gop,
            i,
            &SizeofInfo,
            &Info
        );
        if(EFI_ERROR(Status))
        {
            Print(L"Failed To QueryMode.\n");
            return Status;
        }
        // 印出所有支援的螢幕解析度的Mode,打開QEMU右上角view->serial就能看到
        Print(L"Mode = %d, H = %d, V = %d.\n",
            i,
            Info->HorizontalResolution,
            Info->VerticalResolution);
    }

    Status = Gop->SetMode(Gop, 22); // 我們選1920x1080,對應上述印出結果是是序號22,且螢幕會被清空
    if(EFI_ERROR(Status))
    {
        Print(L"Failed to SetMode.\n");
        return Status;
    }
    
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL Red = {0,0,255,0}; //[自我挑戰6]
    Status = Gop->Blt (
                Gop,//This
                &Red, //圖(需是EFI_GRAPHICS_OUTPUT_BLT_PIXEL格式)
                EfiBltVideoFill, // 顯示於螢幕上有幾種方式:EfiBltVideoFill、EfiBltVideoToVideo
                0,0, // 起點座標 x,y
                0,0, // 終點座標 x,y
                100,100,//PixelWidth,PixelHeight
                0//Delta
            );
     return Status;
}


  • 在QEMU上運行結果
    https://ithelp.ithome.com.tw/upload/images/20230920/201618285BSKS3QM8r.png

總結

將本篇的OinkBL.c結合前一篇的OinkBL.inf及OinkBLPkg.dsc,複製到自己的電腦,運行QEMU模擬器即可於螢幕上產生一個100x100紅色方形。下一篇會講如何把Logo.bmp放至buffer,我們明天見!


自我挑戰6

大家可試試將程式碼中的Red{0,0,255,0},改成{255,0,0,0},看看會輸出什麼?


[註1] LocateHandleBuffer在UEFI第7.3章


上一篇
【Day 13】自己寫UEFI顯示自己的Logo (1/5)
下一篇
【Day 15】自己寫UEFI顯示自己的Logo (3/5)
系列文
世界第一簡單的UEFI,實作打造自己的開機畫面31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言