重新编译EDK2工具的方法(Python部分)

之前在《重新编译EDK2工具的方法(C语言部分)》【参考1】中介绍了重新编译C语言编写的编译工具的方法。本文介绍重新编译Python工具的方法。

我们知道,每次编译时,先运行 edksetup.bat 然后使用 build 命令即可进行编译。下面先说build命令的来龙去脉。

我们运行的Build实际上是 BaseTools\bin\Win32 下面的 Build.exe 。运行 edksetup.bat 之后,会自动把这个目录加入到 Path 中。它的源代码可以在BaseTools\Source\Python下面找到。

根据BaseTools\Source\Python\MakeFile中的建议,编译需要使用 cx_Freeze 4.2.3 和 Python 2.7.2。Python是解释型语言,需要解释器才能正常执行程序,Cx_Freeze是将Python源程序转换为EXE的工具,转换之后即可脱离Python解析器单独运行(相当于把解释器打包到EXE中)。特别提醒,一定要使用上面说的这个版本, cx_Freeze版本之间差别很大,选择其他版本会有莫名的问题。这里【参考2】提供上述版本的下载,有需要的朋友可以直接抓取。

接下来是Python的安装,安装完之后,在命令行下手工输入 Python查看能否运行,如果无法运行,请在环境变量中加入Python的路径。如下图所示。

image001

加入Path后,可以在任意位置调用到Python。

image002

之后安装cx_Freeze。安装完成之后在Python27\Scripts下面会有cxfreeze.bat文件,可以运行这个批处理检查是否能正常工作。

image003

还可以编写一个简单的Python文件然后使用下面的命令生成EXE进行测试
cxfreeze hello.py –target-dir dist

最后,可以开始重新编译工具了。在toolsetup.bat 中加入下面语句指定 cxFreeze 和Python的路径。
Set PYTHON_HOME=c:\python27
Set PYTHON_FREEZE_PATH=c:\python27\Scripts

image004

编译的方法是:首先运行 edksetup.bat ,然后进入BaseTools目录,运行
toolsetup.bat ForceRebuild

image005

运行结果如下

image006

编译后生成的EXE 会直接放置到 BaseTools\Bin\Win32下面。

为了验证这个方法,我在 Build.py上加入输出字符串的语句,重新编译工具后再编译整个BIOS。可以看到执行了我加入的语句。

image007

如果你对 buid 的过程感兴趣,下面就可以慢慢分析了。

就是这样。

参考:

1. http://www.lab-z.com/%E9%87%8D%E6%96%B0%E7%BC%96%E8%AF%91edk2%E5%B7%A5%E5%85%B7%E7%9A%84%E6%96%B9%E6%B3%95%EF%BC%88c%E8%AF%AD%E8%A8%80%E9%83%A8%E5%88%86%EF%BC%89/ 重新编译EDK2工具的方法(C语言部分)》
2. Baidu 网盘链接: http://pan.baidu.com/s/1kTjDDf5 密码: 4wsf

Step to UEFI (57) —– 只在一个有显示?

之前的很多实验都有一个奇怪的显现,开出了2个模拟窗口,只有一个上面有显示。琢磨了一下,猜测是因为只打开了一个设备上面的Protocol,于是用命令查看了一下有Graphics 的 Protocol,发现有三个(模拟环境下)

handle

之前我们的代码都是用 gBS->LocateProtocol 打开的,这个函数的意思是“是从内核中找出指定Protocol的第一个实例。”【参考1】

gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);

 

我们遇到问题就是【参考1】中提到的:“UEFI内核中某个Protocol的实例可能不止一个,例如每个硬盘及每个分区都有一个EFI_DISK_IO_PROTOCOL实例。LocateProtocol顺序搜索HANDLE链表,返回找到的第一个该Protocol的实例。我们可以用BootServices提供的其它函数处理HANDLE和Protocol。”

编写代码如下,枚举系统中的所有实例,然后逐个打开并且调用:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>



extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    UINTN i;
    UINTN HandleIndex;	
	EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColor;
    UINTN HandleCount;
	EFI_HANDLE   *HandleBuffer;
    //
    // Search for the shell protocol
    //
    Status = gBS->LocateHandleBuffer(
	 ByProtocol,
      &GraphicsOutputProtocolGuid,
      NULL,
	  &HandleCount,
      &HandleBuffer
     );

    Print(L"[%d] \r\n",HandleCount);
	 
	for (HandleIndex=0; HandleIndex<HandleCount;HandleIndex++)
	{
		Status = gBS -> HandleProtocol (
							HandleBuffer[HandleIndex],
							&gEfiGraphicsOutputProtocolGuid,
							(void **)&GraphicsOutput);
		if (!(EFI_ERROR(Status))) {
			for (i=0;i<100;i++) {
				FillColor.Blue=rand() % 256;
				FillColor.Red=rand() % 256;
				FillColor.Green=rand() % 256;
	
				GraphicsOutput->Blt(GraphicsOutput, &FillColor, EfiBltVideoFill,
					0, 
					0 , 
					rand() % (GraphicsOutput->Mode->Info->HorizontalResolution-100), 
					rand() % (GraphicsOutput->Mode->Info->VerticalResolution-100), 
					100, 100, 0); 
				gBS->Stall(50000);
			} // For i
		}
		else {
		    Print(L"Error @[%d] \r\n",HandleCount);
		}
	} // For HandleIndex		

  return EFI_SUCCESS;
  
}

 

运行结果:

gfxtest6

可以看到现在两个窗口都会有显示。

完整代码下载:

GFXTest6

参考:

1.http://www.cppblog.com/djxzh/archive/2012/03/06/167106.html UEFI 实战(4) protocol

Step to UEFI (56) —– 在屏幕上显示一幅BMP

前面介绍了如何截图,如果把这个过程反过来,先是读取BMP文件到内存,然后重新排列RGB,再用Blt输出到Video上就是显示BMP的过程。

根据这个原理编写程序如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>

#define MAX_FILE_SIZE (1024*1024*1024)

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

#pragma pack(1)

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;

/* This should be compatible with EFI_UGA_PIXEL */
typedef struct {
    UINT8 b, g, r, a;
} EG_PIXEL;

typedef struct {
    CHAR8         CharB;
    CHAR8         CharM;
    UINT32        Size;
    UINT16        Reserved[2];
    UINT32        ImageOffset;
    UINT32        HeaderSize;
    UINT32        PixelWidth;
    UINT32        PixelHeight;
    UINT16        Planes;       // Must be 1
    UINT16        BitPerPixel;  // 1, 4, 8, or 24
    UINT32        CompressionType;
    UINT32        ImageSize;    // Compressed image size in bytes
    UINT32        XPixelsPerMeter;
    UINT32        YPixelsPerMeter;
    UINT32        NumberOfColors;
    UINT32        ImportantColors;
} BMP_IMAGE_HEADER;

typedef struct {
    UINTN       Width;
    UINTN       Height;
    BOOLEAN     HasAlpha;
    EG_PIXEL    *PixelData;
} EG_IMAGE;
#pragma pack()



VOID egFreeImage(IN EG_IMAGE *Image)
{
    if (Image != NULL) {
        if (Image->PixelData != NULL)
            FreePool(Image->PixelData);
        FreePool(Image);
    }
}




static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, 
				{ 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };

static EFI_STATUS egFindESP(OUT EFI_FILE_PROTOCOL *RootDir)
{
    EFI_STATUS          Status;

   

    return Status;
}

//
// Basic image handling
//

EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
{
    EG_IMAGE        *NewImage;
    
    NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
    if (NewImage == NULL)
        return NULL;
    NewImage->PixelData = (EG_PIXEL *) AllocatePool(
								Width * Height * sizeof(EG_PIXEL));
    if (NewImage->PixelData == NULL) {
        FreePool(NewImage);
        return NULL;
    }

    NewImage->Width = Width;
    NewImage->Height = Height;
    NewImage->HasAlpha = HasAlpha;
    return NewImage;
}

EFI_STATUS LoadFile(  IN CHAR16 *FileName,
                      IN UINT8 **FileData, 
					  IN UINTN *FileDataLength)
{
    EFI_STATUS          Status;
    EFI_FILE_HANDLE     FileHandle;
    EFI_FILE_PROTOCOL   *Root;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	EFI_FILE_INFO     *FileInfo = NULL;
	
    Status = gBS->LocateProtocol(
						&gEfiSimpleFileSystemProtocolGuid, 
						NULL,
						(VOID **)&SimpleFileSystem);
    if (EFI_ERROR(Status)) {
		    Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
            return Status;	
	}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
    if (EFI_ERROR(Status)) {
		    Print(L"OpenVolume error \r\n");
            return Status;	
	}

    Status = Root->Open(
							Root, 
							&FileHandle, 
							FileName,
							EFI_FILE_MODE_READ , 
							0);
    if (EFI_ERROR(Status))
	  {
        Print(L"Error Open NULL\n");
        return Status;
	  }	

	FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	
    Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize);
	*FileDataLength=(UINTN) FileInfo->FileSize;
	
	*FileData  = AllocatePool((UINTN) FileInfo->FileSize);
    Status = FileHandle->Read(FileHandle, FileDataLength, *FileData );
    if (EFI_ERROR(Status)) {
		Print(L"Loading file error! \n");
	}
	Print(L"Get file size [%d]!\n",*FileDataLength);
    

    FileHandle->Close(FileHandle);
    
    return Status;
}


//
// Load BMP image
//

EG_IMAGE * egDecodeBMP
	(
		IN UINT8 *FileData, 
		IN UINTN FileDataLength, 
		IN BOOLEAN WantAlpha)
{
    EG_IMAGE            *NewImage;
    BMP_IMAGE_HEADER    *BmpHeader;
    EFI_UGA_PIXEL       *BmpColorMap;
    UINTN               x, y;
    UINT8               *ImagePtr;
    UINT8               *ImagePtrBase;
    UINTN               ImageLineOffset;
    UINT8               ImageValue=0, AlphaValue;
    EG_PIXEL            *PixelPtr;
    UINTN               Index, BitIndex;

    // read and check header
    if (FileDataLength < sizeof(BMP_IMAGE_HEADER) || FileData == NULL)
        return NULL;

    BmpHeader = (BMP_IMAGE_HEADER *) FileData;
    if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M')
        return NULL;

    if (BmpHeader->CompressionType != 0)
        return NULL;

    if (BmpHeader->BitPerPixel != 1 && BmpHeader->BitPerPixel != 4 &&
        BmpHeader->BitPerPixel != 8 && BmpHeader->BitPerPixel != 24)
        return NULL;

    // calculate parameters
    ImageLineOffset = BmpHeader->PixelWidth;
    if (BmpHeader->BitPerPixel == 24)
        ImageLineOffset *= 3;
    else if (BmpHeader->BitPerPixel == 1)
        ImageLineOffset = (ImageLineOffset + 7) >> 3;
    else if (BmpHeader->BitPerPixel == 4)
        ImageLineOffset = (ImageLineOffset + 1) >> 1;
    if ((ImageLineOffset % 4) != 0)
        ImageLineOffset = ImageLineOffset + (4 - (ImageLineOffset % 4));
    // check bounds
    if (BmpHeader->ImageOffset + 
			ImageLineOffset * BmpHeader->PixelHeight > FileDataLength)
        return NULL;
    
    // allocate image structure and buffer
    NewImage = egCreateImage(BmpHeader->PixelWidth, 
								BmpHeader->PixelHeight, WantAlpha);
    if (NewImage == NULL)
        return NULL;
    AlphaValue = WantAlpha ? 255 : 0;
    
    // convert image
    BmpColorMap = (EFI_UGA_PIXEL *)(FileData + sizeof(BMP_IMAGE_HEADER));
    ImagePtrBase = FileData + BmpHeader->ImageOffset;
    for (y = 0; y < BmpHeader->PixelHeight; y++) {
        ImagePtr = ImagePtrBase;
        ImagePtrBase += ImageLineOffset;
        PixelPtr = NewImage->PixelData + 
				(BmpHeader->PixelHeight - 1 - y) * BmpHeader->PixelWidth;
        
        switch (BmpHeader->BitPerPixel) {
            
            case 1:
                for (x = 0; x < BmpHeader->PixelWidth; x++) {
                    BitIndex = x & 0x07;
                    if (BitIndex == 0)
                        ImageValue = *ImagePtr++;
                    
                    Index = (ImageValue >> (7 - BitIndex)) & 0x01;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
            case 4:
                for (x = 0; x <= BmpHeader->PixelWidth - 2; x += 2) {
                    ImageValue = *ImagePtr++;
                    
                    Index = ImageValue >> 4;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                    
                    Index = ImageValue & 0x0f;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                if (x < BmpHeader->PixelWidth) {
                    ImageValue = *ImagePtr++;
                    
                    Index = ImageValue >> 4;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
            case 8:
                for (x = 0; x < BmpHeader->PixelWidth; x++) {
                    Index = *ImagePtr++;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
            case 24:
                for (x = 0; x < BmpHeader->PixelWidth; x++) {
                    PixelPtr->b = *ImagePtr++;
                    PixelPtr->g = *ImagePtr++;
                    PixelPtr->r = *ImagePtr++;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
        }
    }
    
    return NewImage;
}


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    EG_IMAGE        *Image;	
    UINT8           *FileData=NULL;
    UINTN           FileDataLength;

	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, 
						NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	
    
	Status=LoadFile(L"test.bmp",&FileData,&FileDataLength);
    Print(L"Get file size [%d]!\n",FileDataLength);	
	
    Image=egDecodeBMP(FileData, FileDataLength, FALSE);

    Print(L"Image height [%d]:width[%d]",
					Image->Height,
					Image->Width);

	GraphicsOutput->Blt(
				GraphicsOutput, 
				(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) Image->PixelData,
				EfiBltBufferToVideo,
                0, 
				0 , 
				0x20, 
				0x0, 
				Image->Width, 
				Image->Height, 0); 					
	
  return EFI_SUCCESS;
  
}

 

运行结果:

showbmp

完整代码例子:

ShowBMP

本文同样参考了之前提到的 https://github.com/chengs/UEFI 的代码,再次感谢!

Step to UEFI (55) —– 截屏的代码

前面介绍了 EFI_GRAPHICS_OUTPUT_PROTOCOL 的各种用法,这里介绍一下如何使用这个 Protocol 进行截屏,将屏幕信息保存为 BMP 文件。

原理是:用 GraphicsOutput->Blt 将屏幕信息保存在指定的内存位置,然后加一个 BMP 的文件头,再重新排列一下颜色(BMP是直接颜色BGR排列下来),最后将这块内存保存下来就得到需要的BMP文件了。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>



extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

#pragma pack(1)

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;

/* This should be compatible with EFI_UGA_PIXEL */
typedef struct {
    UINT8 b, g, r, a;
} EG_PIXEL;

typedef struct {
    CHAR8         CharB;
    CHAR8         CharM;
    UINT32        Size;
    UINT16        Reserved[2];
    UINT32        ImageOffset;
    UINT32        HeaderSize;
    UINT32        PixelWidth;
    UINT32        PixelHeight;
    UINT16        Planes;       // Must be 1
    UINT16        BitPerPixel;  // 1, 4, 8, or 24
    UINT32        CompressionType;
    UINT32        ImageSize;    // Compressed image size in bytes
    UINT32        XPixelsPerMeter;
    UINT32        YPixelsPerMeter;
    UINT32        NumberOfColors;
    UINT32        ImportantColors;
} BMP_IMAGE_HEADER;

typedef struct {
    UINTN       Width;
    UINTN       Height;
    BOOLEAN     HasAlpha;
    EG_PIXEL    *PixelData;
} EG_IMAGE;
#pragma pack()

//
// Basic image handling
//

EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
{
    EG_IMAGE        *NewImage;
    
    NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
    if (NewImage == NULL)
        return NULL;
    NewImage->PixelData = (EG_PIXEL *) AllocatePool(
								Width * Height * sizeof(EG_PIXEL));
    if (NewImage->PixelData == NULL) {
        FreePool(NewImage);
        return NULL;
    }

    NewImage->Width = Width;
    NewImage->Height = Height;
    NewImage->HasAlpha = HasAlpha;
    return NewImage;
}

//
// Save BMP image
//

VOID egEncodeBMP(IN EG_IMAGE *Image, OUT UINT8 **FileDataReturn, 
											OUT UINTN *FileDataLengthReturn)
{
    BMP_IMAGE_HEADER    *BmpHeader;
    UINT8               *FileData;
    UINTN               FileDataLength;
    UINT8               *ImagePtr;
    UINT8               *ImagePtrBase;
    UINTN               ImageLineOffset;
    EG_PIXEL            *PixelPtr;
    UINTN               x, y;
    
    ImageLineOffset = Image->Width * 3;
    if ((ImageLineOffset % 4) != 0)
        ImageLineOffset = ImageLineOffset + (4 - (ImageLineOffset % 4));
    
    // allocate buffer for file data
    FileDataLength = sizeof(BMP_IMAGE_HEADER) + 
										Image->Height * ImageLineOffset;
    FileData = AllocateZeroPool(FileDataLength);
    if (FileData == NULL) {
        Print(L"Error allocate %d bytes\n", FileDataLength);
        *FileDataReturn = NULL;
        *FileDataLengthReturn = 0;
        return;
    }
	
    // fill header
    BmpHeader = (BMP_IMAGE_HEADER *)FileData;
    BmpHeader->CharB = 'B';
    BmpHeader->CharM = 'M';
    BmpHeader->Size = FileDataLength;
    BmpHeader->ImageOffset = sizeof(BMP_IMAGE_HEADER);
    BmpHeader->HeaderSize = 40;
    BmpHeader->PixelWidth = Image->Width;
    BmpHeader->PixelHeight = Image->Height;
    BmpHeader->Planes = 1;
    BmpHeader->BitPerPixel = 24;
    BmpHeader->CompressionType = 0;
    BmpHeader->XPixelsPerMeter = 0xb13;
    BmpHeader->YPixelsPerMeter = 0xb13;
 
    // fill pixel buffer
    ImagePtrBase = FileData + BmpHeader->ImageOffset;
    for (y = 0; y < Image->Height; y++) {
        ImagePtr = ImagePtrBase;
        ImagePtrBase += ImageLineOffset;
        PixelPtr = Image->PixelData + 
					(Image->Height - 1 - y) * Image->Width;
        
        for (x = 0; x < Image->Width; x++) {
            *ImagePtr++ = PixelPtr->b;
            *ImagePtr++ = PixelPtr->g;
            *ImagePtr++ = PixelPtr->r;
            PixelPtr++;
        }
    }
    
    *FileDataReturn = FileData;
    *FileDataLengthReturn = FileDataLength;
}

VOID egFreeImage(IN EG_IMAGE *Image)
{
    if (Image != NULL) {
        if (Image->PixelData != NULL)
            FreePool(Image->PixelData);
        FreePool(Image);
    }
}

/**

  Function opens and returns a file handle to the root directory of a volume.

  @param DeviceHandle    A handle for a device

  @return A valid file handle or NULL is returned

**/
EFI_FILE_HANDLE
EfiLibOpenRoot (
  IN EFI_HANDLE                   DeviceHandle
  )
{
  EFI_STATUS                      Status;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
  EFI_FILE_HANDLE                 File;

  File = NULL;

  //
  // File the file system interface to the device
  //
  Status = gBS->HandleProtocol (
                  DeviceHandle,
                  &gEfiSimpleFileSystemProtocolGuid,
                  (VOID *) &Volume
                  );

  //
  // Open the root directory of the volume
  //
  if (!EFI_ERROR (Status)) {
    Status = Volume->OpenVolume (
                      Volume,
                      &File
                      );
  }
  //
  // Done
  //
  return EFI_ERROR (Status) ? NULL : File;
}


static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, 
				{ 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };

static EFI_STATUS egFindESP(OUT EFI_FILE_PROTOCOL *RootDir)
{
    EFI_STATUS          Status;

   

    return Status;
}



EFI_STATUS egSaveFile(IN CHAR16 *FileName,
                      IN UINT8 *FileData, IN UINTN FileDataLength)
{
    EFI_STATUS          Status;
    EFI_FILE_HANDLE     FileHandle;
    UINTN               BufferSize;
    EFI_FILE_PROTOCOL   *Root;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	
    Status = gBS->LocateProtocol(
						&gEfiSimpleFileSystemProtocolGuid, 
						NULL,
						(VOID **)&SimpleFileSystem);
    if (EFI_ERROR(Status)) {
		    Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
            return Status;	
	}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
    if (EFI_ERROR(Status)) {
		    Print(L"OpenVolume error \r\n");
            return Status;	
	}

    Status = Root->Open(
							Root, 
							&FileHandle, 
							FileName,
							EFI_FILE_MODE_READ |
							EFI_FILE_MODE_WRITE | 
							EFI_FILE_MODE_CREATE, 
							0);
    if (EFI_ERROR(Status))
	  {
        Print(L"Error Open NULL\n");
        return Status;
	  }	
    
    BufferSize = FileDataLength;
    Status = FileHandle->Write(FileHandle, &BufferSize, FileData);
    FileHandle->Close(FileHandle);
    
    return Status;
}


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    EG_IMAGE        *Image;	
    UINT8           *FileData;
    UINTN           FileDataLength;
	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, 
								NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	
    
	// allocate a buffer for the whole screen
    Image = egCreateImage(GraphicsOutput->Mode->Info->HorizontalResolution, 
					      GraphicsOutput->Mode->Info->VerticalResolution, 
						  FALSE);
    if (Image == NULL) {
        Print(L"Error egCreateImage returned NULL\n");
        return EFI_SUCCESS;
    }		

	// get full screen image
    if (GraphicsOutput != NULL) {
        GraphicsOutput->Blt(GraphicsOutput, 
			(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) Image->PixelData, 
			EfiBltVideoToBltBuffer,
			0, 0, 0, 0, Image->Width, Image->Height, 0); }
  
    // encode as BMP
    egEncodeBMP(Image, &FileData, &FileDataLength);
    egFreeImage(Image);
	
    if (FileData == NULL) {
        Print(L"Error egEncodeBMP returned NULL\n");
        return EFI_SUCCESS;
    }	

    // save to file on the ESP
    Status = egSaveFile(L"screenshot.bmp", FileData, FileDataLength);
    FreePool(FileData);
    if (EFI_ERROR(Status)) { 
        Print(L"Error egSaveFile: %x\n", Status);
		return EFI_SUCCESS;
    }
    
	
  return EFI_SUCCESS;
  
}

 

运行结果:

scrcap

完整代码下载:

SCRCap

特别注意,本文使用的代码来自 https://github.com/chengs/UEFI 再次表示感谢。

参考:

1.http://biosengineer.blogspot.com/2011/09/uefi-screenshot-capture-screen.html UEFI Screenshot (Capture screen)
2.http://kurtqiao.blogspot.com/2013/03/how-to-take-screen-shot-under-uefi-shell.htmlHow to take screen shot under UEFI shell

Step to UEFI (54) —– EFI_SIMPLE_FILE_SYSTEM_PROTOCOL 写文件

通常,每个UEFI系统至少有一个 ESP (EFI System Partition)分区,在这个分区上存放启动文件。EFI内置了EFI_SIMPLE_FILE_SYSTEM_PROTOCOL(简称 FileSystemIo)可以用来操作FAT文件系统【参考1】。

相关的介绍:

\MdePkg\Include\Protocol\SimpleFileSystem.h

///
/// The EFI_FILE_PROTOCOL provides file IO access to supported file systems.
/// An EFI_FILE_PROTOCOL provides access to a file's or directory's contents, 
/// and is also a reference to a location in the directory tree of the file system 
/// in which the file resides. With any given file handle, other files may be opened 
/// relative to this file's location, yielding new file handles.
///
struct _EFI_FILE_PROTOCOL {
  ///
  /// The version of the EFI_FILE_PROTOCOL interface. The version specified 
  /// by this specification is EFI_FILE_PROTOCOL_LATEST_REVISION.
  /// Future versions are required to be backward compatible to version 1.0.
  ///
  UINT64                Revision;
  EFI_FILE_OPEN         Open;
  EFI_FILE_CLOSE        Close;
  EFI_FILE_DELETE       Delete;
  EFI_FILE_READ         Read;
  EFI_FILE_WRITE        Write;
  EFI_FILE_GET_POSITION GetPosition;
  EFI_FILE_SET_POSITION SetPosition;
  EFI_FILE_GET_INFO     GetInfo;
  EFI_FILE_SET_INFO     SetInfo;
  EFI_FILE_FLUSH        Flush;
  EFI_FILE_OPEN_EX      OpenEx;
  EFI_FILE_READ_EX      ReadEx;
  EFI_FILE_WRITE_EX     WriteEx;
  EFI_FILE_FLUSH_EX     FlushEx;
};

 

\MdePkg\Include\Protocol\SimpleFileSystem.h

#define EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID \
  { \
    0x964e5b22, 0x6459, 0x11d2, {0x8e, 0x39, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b } \
  }

 

\EdkCompatibilityPkg\Foundation\Efi\Protocol\SimpleFileSystem\SimpleFileSystem.c

EFI_GUID  gEfiSimpleFileSystemProtocolGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;

 

从原理上来说,先通过这个Protocol OpenVolume,即可获得FAT文件系统上的根目录句柄。目录句柄(EFI_FILE_PROTOCOL)包含了操作该目录里文件的文件操作接口。之后我们再用 Open打开这个目录,选定需要操作的文件即可写入。

\MdePkg\Include\Protocol\SimpleFileSystem.h

/**
  Writes data to a file.

  @param  This       A pointer to the EFI_FILE_PROTOCOL instance that is the file
                     handle to write data to.
  @param  BufferSize On input, the size of the Buffer. On output, the amount of data
                     actually written. In both cases, the size is measured in bytes.
  @param  Buffer     The buffer of data to write.

  @retval EFI_SUCCESS          Data was written.
  @retval EFI_UNSUPPORTED      Writes to open directory files are not supported.
  @retval EFI_NO_MEDIA         The device has no medium.
  @retval EFI_DEVICE_ERROR     The device reported an error.
  @retval EFI_DEVICE_ERROR     An attempt was made to write to a deleted file.
  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
  @retval EFI_WRITE_PROTECTED  The file or medium is write-protected.
  @retval EFI_ACCESS_DENIED    The file was opened read only.
  @retval EFI_VOLUME_FULL      The volume is full.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_FILE_WRITE)(
  IN EFI_FILE_PROTOCOL        *This,
  IN OUT UINTN                *BufferSize,
  IN VOID                     *Buffer
  );

 

完整代码:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS          			Status;
	EFI_FILE_PROTOCOL    			*Root;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	UINTN	BufSize;
	CHAR16 *Textbuf = (CHAR16*) L"www.lab-z.com";
	EFI_FILE_PROTOCOL *FileHandle=0;
	
    Status = gBS->LocateProtocol(
						&gEfiSimpleFileSystemProtocolGuid, 
						NULL,
						(VOID **)&SimpleFileSystem);
						
	
    if (EFI_ERROR(Status)) {
		    Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
            return Status;	
	}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem,&Root);
    if (EFI_ERROR(Status)) {
		    Print(L"OpenVolume error \r\n");
            return Status;	
	}
   
    Status = Root -> Open(Root,
			&FileHandle,
			(CHAR16 *) L"atest.txt",
			EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
			0);
			
    if (EFI_ERROR(Status) || (FileHandle==0)) {
		    Print(L"Open error \r\n");
            return Status;	
	}	
	
	BufSize = StrLen (Textbuf) * 2;
	Print(L"[%d]\r\n",BufSize);		
	Print(L"[%s]\r\n",Textbuf);			
	Status = FileHandle -> Write(FileHandle, &BufSize, Textbuf);
	
	Print(L"Write Done \r\n");	
	
	Status  = FileHandle -> Close (FileHandle);
	
    return EFI_SUCCESS;
}

 

运行结果:

esptest

比较有意思的是,我们按照上面的程序写入之后,使用 type 文件名 来显示文件内容和我们的预期有差别,原因是Type命令默认使用 ASCII 来显内容,而我们的文件中写入的是 Unicode。使用 type -u 强制使用 Unicode即可看到我们期望的内容。

根据【参考2】的提示,我们还可以通过给TXT文件加一个头来通知当前内容格式的方法克服这个问题。

关键代码:

	UINT16  TextHeader =0xFEFF;

 

	BufSize = 2;
	Status = FileHandle -> Write(FileHandle, &BufSize, &TextHeader);
	
	BufSize = StrLen (Textbuf) * 2;
	Print(L"[%d]\r\n",BufSize);		
	Print(L"[%s]\r\n",Textbuf);			
	Status = FileHandle -> Write(FileHandle, &BufSize, Textbuf);

 

运行结果:

esptest2

可以看到经过这样的处理,都可以正常显示内容了。

完整代码下载
ESPTest

参考:

1.UEFI 原理与编程 戴正华 著 P152页

2.http://www.biosren.com/thread-6541-2-1.html UEFI下使用EFI_FILE_PROTOCOL 檔案操作能多次讀寫嗎?

 

=============================================================

2024年9月20日

下面这个代码段能够实现将指定内存内容保存为文件,方便调试,有需要的朋友可以试试。

#include &lt;Library/BaseMemoryLib.h>
#include &lt;Library/MemoryAllocationLib.h>
#include &lt;Protocol/SimpleFileSystem.h>

extern EFI_HANDLE        			  gImageHandle;

EFI_STATUS
WriteMemoryToFile(
    IN EFI_HANDLE ImageHandle,
    IN CHAR16 *FileName,
    IN VOID *Buffer,
    IN UINTN BufferSize
)
{
    EFI_STATUS Status;
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
    EFI_FILE_PROTOCOL *Root;
    EFI_FILE_PROTOCOL *File;
    EFI_HANDLE *Handles = NULL;
    UINTN HandleCount = 0;

    // Locate all handles that support the Simple File System Protocol
    Status = gBS->LocateHandleBuffer(ByProtocol, &amp;gEfiSimpleFileSystemProtocolGuid, NULL, &amp;HandleCount, &amp;Handles);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to locate handles for Simple File System Protocol: %r\n", Status);
        return Status;
    }

    // Open the first Simple File System Protocol
    Status = gBS->HandleProtocol(Handles[0], &amp;gEfiSimpleFileSystemProtocolGuid, (VOID **)&amp;SimpleFileSystem);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to open Simple File System Protocol: %r\n", Status);
        return Status;
    }

    // Open the root directory
    Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &amp;Root);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to open root directory: %r\n", Status);
        return Status;
    }

    // Create or open the file
    Status = Root->Open(Root, &amp;File, FileName, EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to open or create file: %r\n", Status);
        return Status;
    }

    // Write the buffer to the file
    Status = File->Write(File, &amp;BufferSize, Buffer);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to write to file: %r\n", Status);
        File->Close(File);
        return Status;
    }

    // Close the file
    Status = File->Close(File);
    if (EFI_ERROR(Status)) {
        Print(L"Failed to close file: %r\n", Status);
        return Status;
    }

    Print(L"Memory successfully written to file: %s\n", FileName);
    return EFI_SUCCESS;
}

 

 

Step to UEFI (53) —– EFI_Graphics_Output_Protocol 屏幕拷贝的测试

前面实验了颜色填充,这里实验一下屏幕的拷贝。测试很简单,就是屏幕上一个区域的内容copy到另外的位置.

v2v

代码如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>



extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    UINTN i;
	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	

  for (i=0;i<100;i++) {
	GraphicsOutput->Blt(GraphicsOutput, NULL, EfiBltVideoToVideo,
                        rand() % (GraphicsOutput->Mode->Info->HorizontalResolution-100), 
						rand() % (GraphicsOutput->Mode->Info->VerticalResolution-100) , 
						rand() % (GraphicsOutput->Mode->Info->HorizontalResolution-100), 
						rand() % (GraphicsOutput->Mode->Info->VerticalResolution-100), 
						100, 100, 0); 
	gBS->Stall(50000);
  }				

  
  return EFI_SUCCESS;
  
}

 

运行结果

gfx3

工作视频:

http://www.tudou.com/programs/view/7gRxW6SqBKg/?resourceId=414535982_06_02_99

完整代码下载

GFXTest3

当然,这个实验有些让人看不清楚,修改上面的程序,直接在屏幕上画色块

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>



extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    UINTN i;
	EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColor;
	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	

  for (i=0;i<100;i++) {
    FillColor.Blue=rand() % 256;
	FillColor.Red=rand() % 256;
	FillColor.Green=rand() % 256;
	
	GraphicsOutput->Blt(GraphicsOutput, &FillColor, EfiBltVideoFill,
                0, 
				0 , 
				rand() % (GraphicsOutput->Mode->Info->HorizontalResolution-100), 
				rand() % (GraphicsOutput->Mode->Info->VerticalResolution-100), 
				100, 100, 0); 
	gBS->Stall(50000);
  }				

  
  return EFI_SUCCESS;
  
}

 

运行结果

gfxtest5

工作视频:

http://www.tudou.com/programs/view/VSbFaN1XBZc/?resourceId=414535982_06_02_99

完整代码下载

GFXTest5

参考:

1. UEFI SPEC 2.4 P498

2. 本文参考 https://github.com/chengs 的代码

Step to UEFI (52) —– EFI_Graphics_Output_Protocol 清屏幕

EFI_Graphics_Output_Protocol 中的 Blt 可以实现在屏幕上绘制图形的功能。

bvf

其中的一个参数 EfiBltVideoFill 可以用来填充整个屏幕的颜色,从而实现清屏的目的。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>



extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;

//
// Drawing to the screen
//
VOID egClearScreen(IN EFI_UGA_PIXEL *FillColor)
{
    
    if (GraphicsOutput != NULL) {
        // EFI_GRAPHICS_OUTPUT_BLT_PIXEL and EFI_UGA_PIXEL have the same
        // layout, and the header from TianoCore actually defines them
        // to be the same type.
       GraphicsOutput->Blt(GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)FillColor, EfiBltVideoFill,
                           0, 0, 0, 0, GraphicsOutput->Mode->Info->HorizontalResolution, 
						   GraphicsOutput->Mode->Info->VerticalResolution, 0);
    }
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    EFI_UGA_PIXEL color;
	UINTN i;
	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	

	for (i=0;i<255;i++)
	  {
		color.Blue  = i & 0xFF;	
		color.Green = i & 0xFF;
		color.Red   = i & 0xFF;
	
		egClearScreen(&color);
		gBS->Stall(5000);
	  }
  return EFI_SUCCESS;
  
}

 

工作视频:

http://www.tudou.com/programs/view/W6lvKHdeMX8/?resourceId=414535982_06_02_99

完整代码下载

GFXTest2

参考:

1. 本文参考 https://github.com/chengs 的代码 在此表示感谢!

Step to UEFI (51) —– EFI_Graphics_Output_Protocol获得基本信息

学习了一下如何获得 Shell 下当前的显示信息。通过 GraphicsOutputProtocol 来完成这个功能。这个 Protocol 在【参考1】 中有描述。

Capture

头定义在 \MdePkg\Include\Protocol\GraphicsOutput.h

typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL EFI_GRAPHICS_OUTPUT_PROTOCOL;

///
/// Provides a basic abstraction to set video modes and copy pixels to and from 
/// the graphics controller's frame buffer. The linear address of the hardware 
/// frame buffer is also exposed so software can write directly to the video hardware.
///
struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
  EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE  QueryMode;
  EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE    SetMode;
  EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT         Blt;
  ///
  /// Pointer to EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE data.
  ///
  EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE        *Mode;
};

 

从名称上来看,这个 Protocol 能够实现的功能是:查询/设置当前显示模式,将屏幕内容和内存互copy等。

这次实验的是查询功能。

查询的结果输出是 EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE 结构体

typedef struct {
  ///
  /// The number of modes supported by QueryMode() and SetMode().
  ///
  UINT32                                 MaxMode;
  ///
  /// Current Mode of the graphics device. Valid mode numbers are 0 to MaxMode -1.
  ///
  UINT32                                 Mode;
  ///
  /// Pointer to read-only EFI_GRAPHICS_OUTPUT_MODE_INFORMATION data.
  ///
  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION   *Info;
  ///
  /// Size of Info structure in bytes.
  ///
  UINTN                                  SizeOfInfo;
  ///
  /// Base address of graphics linear frame buffer.
  /// Offset zero in FrameBufferBase represents the upper left pixel of the display.
  ///
  EFI_PHYSICAL_ADDRESS                   FrameBufferBase;
  ///
  /// Amount of frame buffer needed to support the active mode as defined by 
  /// PixelsPerScanLine xVerticalResolution x PixelElementSize.
  ///
  UINTN                                  FrameBufferSize;
} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;

 

可以看到能够获得当前显示模式,屏幕分辨率和格式信息等。根据上面的信息,编写程序如下

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>



extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS Status;

    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	

	Print(L"Max mode     =[%d] \n",GraphicsOutput->Mode->MaxMode);
	Print(L"Current mode =[%d] \n",GraphicsOutput->Mode->Mode);
	Print(L"Version      =[%d] \n",GraphicsOutput->Mode->Info->Version);
	Print(L"Screen Width =[%d] \n",GraphicsOutput->Mode->Info->HorizontalResolution);
	Print(L"Screen height=[%d] \n",GraphicsOutput->Mode->Info->VerticalResolution);
	Print(L"Format       =[%d] \n",GraphicsOutput->Mode->Info->PixelFormat);
	Print(L"Num of pixel =[%d] \n",GraphicsOutput->Mode->Info->PixelsPerScanLine);
		
  return EFI_SUCCESS;
  
}

 

运行结果(还是NT32模拟器中)

gopinfo

完整代码下载:

GFXTest

参考:

1.UEFI Spec 2.4 P488 11.9 Graphics Output Protocol

==========================================================

2025年3月17日 额外同一个功能类似的代码

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/DebugLib.h>

INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
)
{
  EFI_STATUS Status;
  EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
  UINTN HandleCount;
  EFI_HANDLE *HandleBuffer;
  UINTN Index;
  UINT32 ModeIndex;
  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
  UINTN SizeOfInfo;

  // Locate the handles that support the Graphics Output Protocol
  Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &HandleCount, &HandleBuffer);
  if (EFI_ERROR(Status)) {
    Print(L"Failed to locate handles for Graphics Output Protocol\n");
    return Status;
  }

  // Iterate over all handles
  for (Index = 0; Index < HandleCount; Index++) {
    // Get the Graphics Output Protocol instance
    Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiGraphicsOutputProtocolGuid, (VOID**)&GraphicsOutput);
    if (EFI_ERROR(Status)) {
      Print(L"Failed to handle protocol for handle %d\n", Index);
      continue;
    }
	Print(L"Controler [%d]\n", Index);
    // Iterate over each mode
    for (ModeIndex = 0; ModeIndex < GraphicsOutput->Mode->MaxMode; ModeIndex++) {
      // Get mode information
      Status = GraphicsOutput->QueryMode(GraphicsOutput, ModeIndex, &SizeOfInfo, &Info);
      if (EFI_ERROR(Status)) {
        Print(L"Failed to query mode %d\n", ModeIndex);
        continue;
      }

      // Output the resolution and pixel format
      Print(L"Mode %d: Resolution: %ux%u, Pixels Per Scanline: %u\n",
            ModeIndex,
            Info->HorizontalResolution,
            Info->VerticalResolution,
            Info->PixelsPerScanLine);

      // Free the mode information
      gBS->FreePool(Info);
    }
  }

  // Free the handle buffer
  gBS->FreePool(HandleBuffer);

  return EFI_SUCCESS;
}
## @file
#  A simple, basic, EDK II native, "hello" application.
#
#   Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.&lt;BR>
#   SPDX-License-Identifier: BSD-2-Clause-Patent
#
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = gm
  FILE_GUID                      = a912f198-2025-0317-b908-b757b806ec83
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib

#
#  VALID_ARCHITECTURES           = IA32 X64
#

[Sources]
  GetModeInfo.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  UefiLib
  ShellCEntryLib

Step to UEFI (50) —– 实现一个简单的菜单功能

屏幕显示功能搭配取按键信息的功能可以实现一个简单的菜单,用户可以使用上下键移动后选项,回车确认,ESC退出。

代码本身不复杂

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

//
// EFI Scan codes 
// copied from \EdkCompatibilityPkg\Foundation\Efi\Protocol\SimpleTextIn\SimpleTextIn.h
//
#define SCAN_NULL       0x0000
#define SCAN_UP         0x0001
#define SCAN_DOWN       0x0002
#define SCAN_ESC        0x0017
#define CHAR_CARRIAGE_RETURN  0x000D

#define	POSX 7
#define	POSY 3
#define NUM  5
CHAR16 HELPSTR[40]=L"UP/DOWN, ENTER , ESC";
CHAR16 ITEM[NUM][20]= {
				L"Item1 MOVSXX",
				L"Item2 CPUID",
				L"Item3 RETF",
				L"Item4 PUSHF",
				L"Item5 SUB"
				};

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{

  EFI_INPUT_KEY	Key;
  EFI_STATUS		Status;
  int current=0;
  int i;
  
  gST -> ConOut ->ClearScreen(gST->ConOut);
 
  ShellPrintEx(POSX-3,POSY-1,L"%N%S",HELPSTR);
  ShellPrintEx(POSX,POSY,L"%H%S",ITEM[0]);
  for (i=1;i<NUM;i++) 
	{
    ShellPrintEx(POSX,POSY+i,L"%N%S",ITEM[i]);
	}
	
  Key.ScanCode=SCAN_NULL;
  while (SCAN_ESC!=Key.ScanCode)
    {
		Status= gST -> ConIn -> ReadKeyStroke(gST->ConIn,&Key);	
		if (Status == EFI_SUCCESS)	{
			ShellPrintEx(POSX,POSY+current,L"%N%S",ITEM[current]);
			if (SCAN_UP == Key.ScanCode) {current = (current-1+NUM)%NUM;}
			if (SCAN_DOWN == Key.ScanCode) {current = (current+1)%NUM;}
			ShellPrintEx(POSX,POSY+current,L"%H%S",ITEM[current]);			
			ShellPrintEx(POSX,POSY+NUM,L"Current[%d] Scancode[%d] UnicodeChar[%x] \n\r",current,Key.ScanCode,Key.UnicodeChar);		
		}
		if (CHAR_CARRIAGE_RETURN == Key.UnicodeChar) {
			ShellPrintEx(POSX,POSY+NUM+1,L"You have chosen: %N%S",ITEM[current]);
			break;
		}
	};	

  return EFI_SUCCESS;
}

 

运行结果:

smenu

工作视频:

http://www.tudou.com/programs/view/R_932wqGuYw/?resourceId=414535982_06_02_99

完整的代码下载:

Menu

Step to UEFI (49) —– 内存驻留程序

之前,我们通过修改Shell的代码,能够实现在右上角不断显示当前时间的功能。但是这个方法过于复杂。于是我请教了一下 HZZZ ,看看他是否有什么好办法。

经过他的研究,还是使用定时器的方式不断触发,困难点在于如果退出时没有对应的 Handle,再次触发会导致问题。解决这个问题的方法是:让程序在内存中重新加载自己,跳进去执行之后,最开始的部分就可以丢弃掉了。

当然,如果你想读懂这个程序,要先读懂前面几篇 《Step to UEFI>> 系列的文章。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE		 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE		  gImageHandle;

STATIC CONST UINTN SecondsToNanoSeconds = 500000;

/**
  The callback function for the timer event used to get map.

  @param[in] Event    The event this function is registered to.
  @param[in] Context  The context registered to the event.
**/
VOID
EFIAPI
Timeout (
  IN EFI_EVENT      Event,
  IN VOID           *Context
  )
{
	EFI_TIME   ET;
	UINTN		x;
	UINTN		y;
  

  //Get cursor postion
  x = gST->ConOut->Mode->CursorColumn;
  y = gST->ConOut->Mode->CursorRow;
  //Move cursor to Up-Left 
  gST -> ConOut -> SetCursorPosition(gST -> ConOut,70,0);  
  //Output current time
  gRT->GetTime(&ET, NULL);  
  Print(L"%2d:%2d:%2d",ET.Hour,ET.Minute,ET.Second);
  //Move cursor back
  gST -> ConOut -> SetCursorPosition(gST -> ConOut,x,y);
  
  return ;
}

typedef struct {
  UINTN                       Signature;
  /// Image handle
  EFI_HANDLE                  Handle;   
  /// Image type
  UINTN                       Type;           
  /// If entrypoint has been called
  BOOLEAN                     Started;        
  /// The image's entry point
  EFI_IMAGE_ENTRY_POINT       EntryPoint;     
  /// loaded image protocol
  EFI_LOADED_IMAGE_PROTOCOL   Info; 
} LOADED_IMAGE_PRIVATE_DATA_TEMP;

#define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))

#define LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(a) \
          _CR(a, LOADED_IMAGE_PRIVATE_DATA_TEMP, Info)



typedef void (*Fun)();

void function()
{
    EFI_STATUS                Status;
    EFI_HANDLE                TimerOne = NULL;
	
    printf("function called\n");
	
    Status  = gBS->CreateEvent (
                    EVT_NOTIFY_SIGNAL | EVT_TIMER,
                    TPL_CALLBACK,
                    Timeout,
                    NULL,
                    &TimerOne
                    );

    if (EFI_ERROR (Status)) {
        Print(L"Create Event Error! \r\n");
		return ;
    }

    Status = gBS->SetTimer (
                   TimerOne,
                   TimerPeriodic,
                   MultU64x32 (SecondsToNanoSeconds, 1)
                   );

    if (EFI_ERROR (Status)) {
        Print(L"Set Timer Error! \r\n");
		return ;
    }
}


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{


  EFI_STATUS                         Status = EFI_SUCCESS;
  EFI_LOADED_IMAGE_PROTOCOL          *ImageInfo = NULL;
  EFI_HANDLE                         Handle = 0;
  EFI_GUID                           gEfiLoadedImageProtocolGuid = 
                                     { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }};
  LOADED_IMAGE_PRIVATE_DATA_TEMP      *private = NULL;
  Fun                                fun;
  UINTN                              FunOffset;
  UINTN                              FunAddr;

  Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo);
  // function offset in the old image
  FunOffset = (UINTN)function - (UINTN)ImageInfo->ImageBase;

  // load the image in memory again
  Status = gBS->LoadImage(FALSE, gImageHandle, NULL, ImageInfo->ImageBase, (UINTN)ImageInfo->ImageSize, &Handle);  

  // get the newer imageinfo
  Status = gBS->HandleProtocol (Handle, &gEfiLoadedImageProtocolGuid, &ImageInfo);

  private = LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(ImageInfo);
  FunAddr = (UINTN)FunOffset + (UINTN)ImageInfo->ImageBase;
  
  fun = (Fun)((UINTN)FunOffset + (UINTN)ImageInfo->ImageBase);
  // called the newer function in new image,the new image will be always in memory because it will not be free
  fun();
  return EFI_SUCCESS;
}

 

工作的视频,看得出来这样的方法适应性更好

完整代码下载

ShowTime2