iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0

嗨我是k66,今天是系列文Day24,我們將之前的幾項功能整併,終於能呈現有模有樣的開機畫面了!
https://ithelp.ithome.com.tw/upload/images/20230930/201618284lg2ce3RXd.png

設計時按照Day11規劃,目前能顯示系統時間、系統版本、作者等基本資訊及Logo畫面。按鍵功能目前有小錯誤,但仍可呈現以上畫面~

放碼上來!程式碼連結


[Sources]
  Entry.c
  Entry.h
  • 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
  • Entry.c
#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與封裝模組,我們明天見!


上一篇
【Day 23】自己寫UEFI Boot Menu (下)
下一篇
【Day 25】自己寫的UEFI開機畫面成功進入Boot Menu!
系列文
世界第一簡單的UEFI,實作打造自己的開機畫面31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言