嗨我是k66,今天是系列文Day24,我們將之前的幾項功能整併,終於能呈現有模有樣的開機畫面了!
設計時按照Day11規劃,目前能顯示系統時間、系統版本、作者等基本資訊及Logo畫面。按鍵功能目前有小錯誤,但仍可呈現以上畫面~
放碼上來!程式碼連結
OinkBL.dsc
請見Day18 https://ithelp.ithome.com.tw/articles/10326813
OinkBL.inf
請見Day18 https://ithelp.ithome.com.tw/articles/10326813
[Sources]
Entry.c
Entry.h
/* Entry */
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/BaseLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <IndustryStandard/Bmp.h>
#include <Guid/FileInfo.h>
/* Time */
#include<Library/TimerLib.h>
#include<Library/IoLib.h>
#define CMOS_ADDRESS 0x70
#define CMOS_DATA 0x71
#include<Entry.h>
EFI_STATUS ConvertBmpToBlt(IN VOID *BmpImage, IN UINTN BmpImageSize, IN OUT VOID **GopBlt, IN OUT UINTN *GopBltSize, OUT UINTN *PixelHeight, OUT UINTN *PixelWidth)
{
UINT8 *ImageData;
UINT8 *ImageBegin;
BMP_IMAGE_HEADER *BmpHeader;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
UINT64 BltBufferSize;
UINTN Height;
UINTN Width;
UINTN ImageIndex;
BmpHeader = (BMP_IMAGE_HEADER *)BmpImage;
ImageBegin = ((UINT8 *)BmpImage) + BmpHeader->ImageOffset;
if (BmpHeader->BitPerPixel != 24 && BmpHeader->BitPerPixel != 32)
return EFI_UNSUPPORTED;
BltBufferSize = BmpHeader->PixelWidth * BmpHeader->PixelHeight * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
*GopBltSize = (UINTN)BltBufferSize;
*GopBlt = AllocatePool(*GopBltSize);
if (*GopBlt == NULL)
{
return EFI_OUT_OF_RESOURCES;
}
*PixelWidth = BmpHeader->PixelWidth;
*PixelHeight = BmpHeader->PixelHeight;
ImageData = ImageBegin;
BltBuffer = *GopBlt;
for (Height = 0; Height < BmpHeader->PixelHeight; Height++)
{
Blt = &BltBuffer[(BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth];
for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Blt++)
{
switch (BmpHeader->BitPerPixel)
{
case 24:
Blt->Blue = *ImageData++;
Blt->Green = *ImageData++;
Blt->Red = *ImageData++;
break;
case 32:
ImageData++;
Blt->Blue = *ImageData++;
Blt->Green = *ImageData++;
Blt->Red = *ImageData++;
break;
default:
break;
}
}
ImageIndex = (UINTN)(ImageData - ImageBegin);
if ((ImageIndex % 4) != 0)
ImageData = ImageData + (4 - (ImageIndex % 4));
}
return EFI_SUCCESS;
}
void ShowCurrTime()
{
UINT8 sec=0;
UINT8 min=0;
UINT8 hour=0;
// UINT8 weekday=0;
UINT8 day=0;
UINT8 month=0;
UINT8 year=0;
IoWrite8(CMOS_ADDRESS,0x00);// sec, 0x00
sec=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x02);// min, 0x02
min=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x04);// hour, 0x04
hour=IoRead8(CMOS_DATA);
// IoWrite8(CMOS_ADDRESS,0x06);// weekday, 0x06
// weekday=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x07);// day, 0x07
day=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x08);// month, 0x08
month=IoRead8(CMOS_DATA);
IoWrite8(CMOS_ADDRESS,0x09);// year, 0x09
year=IoRead8(CMOS_DATA);
Print(L"| Curr time: %02x/%02x/%02x %02x:%02x:%02x |\n",year,month,day,hour,min,sec);
}
void ShowGraphicMode()
{
Print(L"| Graphic Mode: 1920x1080 |\n");
}
void ShowHomeMenu()
{
// 清除畫面並且設定字為白色(0x0F)
gST -> ConOut -> ClearScreen(gST->ConOut);
gST -> ConOut -> SetAttribute(gST->ConOut,0x0F);
Print(L"*********************************\n");
Print(L"| OinkBootLoader v0.0 |\n");
Print(L"| CopyRight 2023 k66 |\n");
Print(L"| |\n");
Print(L"| |\n");
Print(L"|-------------------------------|\n");
ShowCurrTime();
ShowGraphicMode();
Print(L"| UEFI Version: 2.9 |\n");
Print(L"|-------------------------------|\n");
Print(L"| Press B for BOOT MENU |\n");
Print(L"| Press S for SETUP |\n");
Print(L"| Press ESC for SHUTDOWN |\n");
Print(L"*********************************\n");
}
EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE* SystemTable)
{
EFI_STATUS Status;
EFI_GRAPHICS_OUTPUT_PROTOCOL * GraphicsProtocol = NULL;
// EFI_GRAPHICS_OUTPUT_BLT_PIXEL BLACK = { 0x00, 0x00, 0x00, 0 };
UINTN EventIndex;
EFI_EVENT KeyEvent;
EFI_EVENT WaitList[2];
EFI_INPUT_KEY Key;
UINTN x=600, y=100; //欲顯示Logo.bmp的左上角x,y座標
VOID *GopBlt = NULL;
UINTN GopBltSize;
UINTN BmpHeight;
UINTN BmpWidth;
EFI_FILE_PROTOCOL *Root = 0;
EFI_FILE_PROTOCOL *NewHandle = 0;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
EFI_FILE_INFO *fileinfo;
VOID * Buffer;
UINTN BufferSize;
// IMAGE * ImageList = NULL;
UINTN infosize = SIZE_OF_EFI_FILE_INFO;
EFI_GUID info_type = EFI_FILE_INFO_ID;
/* 檔案處理 */
/* 四步: 1.LocateProtocol 2.OpenVolume 3.Image Open 4.AllocatePool */
Status = gBS->LocateProtocol(&gEfiSimpleFileSystemProtocolGuid, NULL, (VOID**)&SimpleFileSystem);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): LocateProtocol\n");
return Status;
}
Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): OpenVolume \n");
return Status;
}
Status = Root->Open(Root, &NewHandle, (CHAR16*)L"Logo.bmp", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Image Open \n");
return Status;
}
Status = gBS->AllocatePool(AllocateAnyPages, infosize, (VOID **)&fileinfo);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): AllocatePool \n");
return Status;
}
Status = NewHandle->GetInfo(NewHandle, &info_type, &infosize, NULL);
Status = NewHandle->GetInfo(NewHandle, &info_type, &infosize, fileinfo);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Get Informations\n");
return Status;
}
/* Read Buffer&BufferSize for converting BMP to Blt */
BufferSize = fileinfo->FileSize;
gBS->AllocatePool(AllocateAnyPages, BufferSize, (VOID **)&Buffer);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): AllocatePool\n");
return Status;
}
/* Buffer處理 */
Status = NewHandle->Read(NewHandle, &BufferSize, Buffer);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Read Buffer\n");
return Status;
}
/* 關閉檔案 */
Status = NewHandle->Close(NewHandle);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): Close \n");
return Status;
}
/* Convert BMP to Blt */
Status = ConvertBmpToBlt(Buffer, BufferSize, &GopBlt, &GopBltSize, &BmpHeight, &BmpWidth);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): ConvertBmpToBlt\n");
return Status;
}
/* GraphicsProtocol */
Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID**)&GraphicsProtocol);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): LocateProtocol\n");
return Status;
}
while(1)
{
/* Show Logo.bmp */
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): GraphicsProtocol\n");
return Status;
}
ShowHomeMenu();
Status = GraphicsProtocol->Blt(GraphicsProtocol, GopBlt, EfiBltBufferToVideo, 0, 0, x, y, BmpWidth, BmpHeight, 0); // x,y是欲顯示之Logo.bmp左上角座標
MicroSecondDelay(100000*10000); // Force to stay
Status = gBS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &KeyEvent);
if (EFI_ERROR(Status))
{
Print(L"%r", Status);
Print(L"ERROR (Entry.c): Create Timer Event\n");
return Status;
}
Status = gBS->SetTimer(KeyEvent, TimerRelative, 1000000);
if (EFI_ERROR(Status))
{
Print(L"%r", Status);
Print(L"ERROR (Entry.c): SetTimer\n");
return Status;
}
WaitList[0] = gST->ConIn->WaitForKey;
WaitList[1] = KeyEvent;
Status = gBS->WaitForEvent(2, WaitList, &EventIndex);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): WaitForEvent\n");
return Status;
}
Status = gST->ConIn->ReadKeyStroke(gST->ConIn, &Key);
if (EFI_ERROR(Status))
{
Print(L"ERROR (Entry.c): ReadKeyStroke\n");
return Status;
}
switch (Key.UnicodeChar)
{
case 'B': // BOOT MENU
Print(L"ENTER BOOT MENU...\n");
break;
case 'S': // SETUP
Print(L"SETUP\n");
break;
case SCAN_ESC: // ESC
Print(L"SHUTDOWN\n");
goto SHUTDOWN;
break;
}
MicroSecondDelay(10000*10000); // Force to stay
// Key.UnicodeChar = SCAN_ESC; // force to escape do-while loop
}
SHUTDOWN:
gBS->CloseEvent(KeyEvent);
Print(L"--- SHUTDOWN: User press ESC ---\n");
// Free File&Buffer
gBS->FreePool(fileinfo);
gBS->FreePool(Buffer);
return EFI_SUCCESS;
}
Entry.c
含有完整的程式碼,只要將dsc與inf([sources])
設定好,照著做就可執行如本篇開頭畫面。未來幾天會著重在將過去寫過的螢幕分辨率與選擇Kernel(OS)加入此Entry.c與封裝模組,我們明天見!