Step to UEFI (62) —– 常用的字符串函数(上)

这里介绍一下常用的处理字符串的函数。这篇介绍 StrCpy/StrnCpy StrSize StrLen StrCmp/StrnCmp StrCat/StrnCat 。这些函数都是给 Unicode16 字符串设计的( CHAR16 定义的, 或者 L“www.lab-z.com” 这样定义的)。同样还有一组带有 Ascii 前缀的函数是给普通的字符串使用的。

函数定义原型可以在 \MdePkg\Include\Library\BaseLib.h 这个文件中找到

1.StrCpy (D<-S) 作用:将 Source 定义的字符串拷贝到 Destination 定义的字符串中(覆盖之)

//
// String Services
//

/**
  Copies one Null-terminated Unicode string to another Null-terminated Unicode
  string and returns the new Unicode string.

  This function copies the contents of the Unicode string Source to the Unicode
  string Destination, and returns Destination. If Source and Destination
  overlap, then the results are undefined.

  If Destination is NULL, then ASSERT().
  If Destination is not aligned on a 16-bit boundary, then ASSERT().
  If Source is NULL, then ASSERT().
  If Source is not aligned on a 16-bit boundary, then ASSERT().
  If Source and Destination overlap, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Source contains more than
  PcdMaximumUnicodeStringLength Unicode characters not including the
  Null-terminator, then ASSERT().

  @param  Destination The pointer to a Null-terminated Unicode string.
  @param  Source      The pointer to a Null-terminated Unicode string.

  @return Destination.

**/
CHAR16 *
EFIAPI
StrCpy (
  OUT     CHAR16                    *Destination,
  IN      CONST CHAR16              *Source
  );

 

实例:

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


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

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

  CHAR16 *s1=L"WWW.";
  CHAR16 *s2=L".LAB-Z.COM";
  CHAR16 *Result;
  
  Result=StrCpy(s1,s2);
  Print(L"%s\n",s1);  
  Print(L"%s\n",Result);

  return EFI_SUCCESS;
}

 

strtest1

换一种字符串定义方式

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


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

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

  CHAR16 s1[20]=L"WWW.";
  CHAR16 s2[20]=L".LAB-Z.COM";
  CHAR16 *Result;
  
  Result=StrCpy(s1,s2);
  Print(L"%s\n",s1);  
  Print(L"%s\n",Result);

  return EFI_SUCCESS;
}

 

运行结果和上面的相同。

2.StrnCpy 作用:将 Source 定义的字符串中,前n个拷贝到 Destination 定义的字符串中(覆盖之)

/**
  Copies up to a specified length from one Null-terminated Unicode string to 
  another Null-terminated Unicode string and returns the new Unicode string.

  This function copies the contents of the Unicode string Source to the Unicode
  string Destination, and returns Destination. At most, Length Unicode
  characters are copied from Source to Destination. If Length is 0, then
  Destination is returned unmodified. If Length is greater that the number of
  Unicode characters in Source, then Destination is padded with Null Unicode
  characters. If Source and Destination overlap, then the results are
  undefined.

  If Length > 0 and Destination is NULL, then ASSERT().
  If Length > 0 and Destination is not aligned on a 16-bit boundary, then ASSERT().
  If Length > 0 and Source is NULL, then ASSERT().
  If Length > 0 and Source is not aligned on a 16-bit boundary, then ASSERT().
  If Source and Destination overlap, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Length is greater than 
  PcdMaximumUnicodeStringLength, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Source contains more than
  PcdMaximumUnicodeStringLength Unicode characters, not including the Null-terminator,
  then ASSERT().

  @param  Destination The pointer to a Null-terminated Unicode string.
  @param  Source      The pointer to a Null-terminated Unicode string.
  @param  Length      The maximum number of Unicode characters to copy.

  @return Destination.

**/
CHAR16 *
EFIAPI
StrnCpy (
  OUT     CHAR16                    *Destination,
  IN      CONST CHAR16              *Source,
  IN      UINTN                     Length
  );

 

实例:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW.";
  CHAR16 *s2=L".LAB-Z.COM";
  CHAR16 *Result;
  
  Result=StrnCpy(s1,s2,4);
  Print(L"%s\n",s1);  
  Print(L"%s\n",Result);

  return EFI_SUCCESS;
}

 

strtest2

3.StrLen 作用:返回字符串中字符的个数,注意:数量不包括结尾的 NULL。

/**
  Returns the length of a Null-terminated Unicode string.

  This function returns the number of Unicode characters in the Null-terminated
  Unicode string specified by String.

  If String is NULL, then ASSERT().
  If String is not aligned on a 16-bit boundary, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and String contains more than
  PcdMaximumUnicodeStringLength Unicode characters not including the
  Null-terminator, then ASSERT().

  @param  String  Pointer to a Null-terminated Unicode string.

  @return The length of String.

**/
UINTN
EFIAPI
StrLen (
  IN      CONST CHAR16              *String
  );

 

实例:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW.";
  CHAR16 s2[20]=L".LAB-Z.COM";
  UINTN Result;
  
  Result=StrLen(s1);
  Print(L"%d\n",Result);

  Result=StrLen(s2);
  Print(L"%d\n",Result);
  
  return EFI_SUCCESS;
}

 

运行结果

strtest3

4. StrSize 作用:返回字符串占用的内存字节数,注意:包括 NULL。

/**
  Returns the size of a Null-terminated Unicode string in bytes, including the
  Null terminator.

  This function returns the size, in bytes, of the Null-terminated Unicode string 
  specified by String.

  If String is NULL, then ASSERT().
  If String is not aligned on a 16-bit boundary, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and String contains more than
  PcdMaximumUnicodeStringLength Unicode characters not including the
  Null-terminator, then ASSERT().

  @param  String  The pointer to a Null-terminated Unicode string.

  @return The size of String.

**/
UINTN
EFIAPI
StrSize (
  IN      CONST CHAR16              *String
  );

 

实例:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW";
  CHAR16 s2[40]=L"LAB-Z.COM";
  UINTN Result;
  
  Result=StrSize(s1);
  Print(L"%d\n",Result);

  Result=StrSize(s2);
  Print(L"%d\n",Result);
  
  return EFI_SUCCESS;
}

 

运行结果

strtest4

定义的字符串实际上是: “www\0” ,每个字符都是 CHAR16, 算下来需要占用 8个字节。

5. StrCmp 作用:比较字符串是否相同。相同的话返回 0

/**
  Compares two Null-terminated Unicode strings, and returns the difference
  between the first mismatched Unicode characters.

  This function compares the Null-terminated Unicode string FirstString to the
  Null-terminated Unicode string SecondString. If FirstString is identical to
  SecondString, then 0 is returned. Otherwise, the value returned is the first
  mismatched Unicode character in SecondString subtracted from the first
  mismatched Unicode character in FirstString.

  If FirstString is NULL, then ASSERT().
  If FirstString is not aligned on a 16-bit boundary, then ASSERT().
  If SecondString is NULL, then ASSERT().
  If SecondString is not aligned on a 16-bit boundary, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and FirstString contains more
  than PcdMaximumUnicodeStringLength Unicode characters not including the
  Null-terminator, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and SecondString contains more
  than PcdMaximumUnicodeStringLength Unicode characters, not including the
  Null-terminator, then ASSERT().

  @param  FirstString   The pointer to a Null-terminated Unicode string.
  @param  SecondString  The pointer to a Null-terminated Unicode string.

  @retval 0      FirstString is identical to SecondString.
  @return others FirstString is not identical to SecondString.

**/
INTN
EFIAPI
StrCmp (
  IN      CONST CHAR16              *FirstString,
  IN      CONST CHAR16              *SecondString
  );

 

实例代码:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW";
  CHAR16 s2[40]=L"LAB-Z.COM";
  CHAR16 s3[40]=L"LAB-Z";
  CHAR16 s4[40]=L"MAB-Z";  
  UINTN Result;
  
  Result=StrCmp(s1,s2);
  Print(L"%d\n",Result);

  Result=StrCmp(s2,s3);
  Print(L"%d\n",Result);

  Result=StrCmp(s3,s4);
  Print(L"%d\n",Result);
  
  return EFI_SUCCESS;
}

 

运行结果:

strtest5

6. StrnCmp 作用:和上面的比较函数类似,只是这个函数只比较指定的前n个字符

/**
  Compares up to a specified length the contents of two Null-terminated Unicode strings,
  and returns the difference between the first mismatched Unicode characters.
  
  This function compares the Null-terminated Unicode string FirstString to the
  Null-terminated Unicode string SecondString. At most, Length Unicode
  characters will be compared. If Length is 0, then 0 is returned. If
  FirstString is identical to SecondString, then 0 is returned. Otherwise, the
  value returned is the first mismatched Unicode character in SecondString
  subtracted from the first mismatched Unicode character in FirstString.

  If Length > 0 and FirstString is NULL, then ASSERT().
  If Length > 0 and FirstString is not aligned on a 16-bit boundary, then ASSERT().
  If Length > 0 and SecondString is NULL, then ASSERT().
  If Length > 0 and SecondString is not aligned on a 16-bit boundary, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Length is greater than
  PcdMaximumUnicodeStringLength, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and FirstString contains more than
  PcdMaximumUnicodeStringLength Unicode characters, not including the Null-terminator,
  then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and SecondString contains more than
  PcdMaximumUnicodeStringLength Unicode characters, not including the Null-terminator,
  then ASSERT().

  @param  FirstString   The pointer to a Null-terminated Unicode string.
  @param  SecondString  The pointer to a Null-terminated Unicode string.
  @param  Length        The maximum number of Unicode characters to compare.

  @retval 0      FirstString is identical to SecondString.
  @return others FirstString is not identical to SecondString.

**/
INTN
EFIAPI
StrnCmp (
  IN      CONST CHAR16              *FirstString,
  IN      CONST CHAR16              *SecondString,
  IN      UINTN                     Length
  );

 

实例:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW";
  CHAR16 s2[40]=L"LAB-Z.COM";
  CHAR16 s3[40]=L"LAB-Z";
  CHAR16 s4[40]=L"MAB-Z";  
  
  Result=StrnCmp(s1,s2,7);
  Print(L"%d\n",Result);

  Result=StrnCmp(s2,s3,5);
  Print(L"%d\n",Result);

  Result=StrnCmp(s3,s4,5);
  Print(L"%d\n",Result);
  
  return EFI_SUCCESS;
}

 

运行结果

strtest6

7. StrCat 作用:将 Source 字符串的内容拼接到 Destination 字符串上。

/**
  Concatenates one Null-terminated Unicode string to another Null-terminated
  Unicode string, and returns the concatenated Unicode string.

  This function concatenates two Null-terminated Unicode strings. The contents
  of Null-terminated Unicode string Source are concatenated to the end of
  Null-terminated Unicode string Destination. The Null-terminated concatenated
  Unicode String is returned. If Source and Destination overlap, then the
  results are undefined.

  If Destination is NULL, then ASSERT().
  If Destination is not aligned on a 16-bit boundary, then ASSERT().
  If Source is NULL, then ASSERT().
  If Source is not aligned on a 16-bit boundary, then ASSERT().
  If Source and Destination overlap, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Destination contains more
  than PcdMaximumUnicodeStringLength Unicode characters, not including the
  Null-terminator, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Source contains more than
  PcdMaximumUnicodeStringLength Unicode characters, not including the
  Null-terminator, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and concatenating Destination
  and Source results in a Unicode string with more than
  PcdMaximumUnicodeStringLength Unicode characters, not including the
  Null-terminator, then ASSERT().

  @param  Destination The pointer to a Null-terminated Unicode string.
  @param  Source      The pointer to a Null-terminated Unicode string.

  @return Destination.

**/
CHAR16 *
EFIAPI
StrCat (
  IN OUT  CHAR16                    *Destination,
  IN      CONST CHAR16              *Source
  );

 

实例:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW.";
  CHAR16 s2[40]=L"LAB-Z.COM";
  CHAR16 *Result;
  
  Result=StrCat(s1,s2);
  Print(L"%s\n",Result);
  
  return EFI_SUCCESS;
}

 

运行结果

strtest7

8.StrnCat 作用:将 Source 字符串前面n个字符的内容拼接到 Destination 字符串上。

/**
  Concatenates up to a specified length one Null-terminated Unicode to the end 
  of another Null-terminated Unicode string, and returns the concatenated 
  Unicode string.

  This function concatenates two Null-terminated Unicode strings. The contents
  of Null-terminated Unicode string Source are concatenated to the end of
  Null-terminated Unicode string Destination, and Destination is returned. At
  most, Length Unicode characters are concatenated from Source to the end of
  Destination, and Destination is always Null-terminated. If Length is 0, then
  Destination is returned unmodified. If Source and Destination overlap, then
  the results are undefined.

  If Destination is NULL, then ASSERT().
  If Length > 0 and Destination is not aligned on a 16-bit boundary, then ASSERT().
  If Length > 0 and Source is NULL, then ASSERT().
  If Length > 0 and Source is not aligned on a 16-bit boundary, then ASSERT().
  If Source and Destination overlap, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Length is greater than 
  PcdMaximumUnicodeStringLength, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Destination contains more
  than PcdMaximumUnicodeStringLength Unicode characters, not including the
  Null-terminator, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and Source contains more than
  PcdMaximumUnicodeStringLength Unicode characters, not including the
  Null-terminator, then ASSERT().
  If PcdMaximumUnicodeStringLength is not zero, and concatenating Destination
  and Source results in a Unicode string with more than PcdMaximumUnicodeStringLength
  Unicode characters, not including the Null-terminator, then ASSERT().

  @param  Destination The pointer to a Null-terminated Unicode string.
  @param  Source      The pointer to a Null-terminated Unicode string.
  @param  Length      The maximum number of Unicode characters to concatenate from
                      Source.

  @return Destination.

**/
CHAR16 *
EFIAPI
StrnCat (
  IN OUT  CHAR16                    *Destination,
  IN      CONST CHAR16              *Source,
  IN      UINTN                     Length
  );

 

实例:

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  CHAR16 *s1=L"WWW.";
  CHAR16 s2[40]=L"LAB-Z.COM";
  CHAR16 *Result;
  
  Result=StrnCat(s1,s2,3);
  Print(L"%s\n",Result);
  
  return EFI_SUCCESS;
}

 

运行结果

strtest9

本文参考 《UEFI 原理与编程》。

Step to UEFI (61) —– SHA-1的实现

前面介绍了 MD5 的实现,这里介绍一下 SHA-1 算法的实现。具体代码来自【参考1】。

和之前 MD5 程序有一些差别在于这个程序不是一次性将所有文件都放入内存中,而是放入一部分,计算一部分,再用中间结果继续计算下一部分。这样,再大的文件也可以正常处理。

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

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

#include "sha1.h"

#define BUFFERSIZE 2048

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

extern EFI_SHELL_PROTOCOL        *gEfiShellProtocol;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_FILE_HANDLE   FileHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  EFI_HANDLE        *HandleBuffer=NULL;
  UINTN  			ReadSize=BUFFERSIZE;
  SHA1Context 		sha;
  
  //Check if there is a parameter
  if (Argc == 1) {
	Print(L"Usage: crctest [filename]\n");
	return 0;
  }
  
  //Open the file given by the parameter
  Status = ShellOpenFileByName(Argv[1], (SHELL_FILE_HANDLE *)&FileHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }			

  //Get file size	  
  FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	

  /*
   *  Reset the SHA-1 context and process input
  */
  SHA1Reset(&sha);
		
  //Allocate a memory buffer
  HandleBuffer = AllocateZeroPool(BUFFERSIZE);
  if (HandleBuffer == NULL) {
      return (SHELL_OUT_OF_RESOURCES);   }

	while (BUFFERSIZE == ReadSize)		
    {
		ReadSize=BUFFERSIZE;
		//Load the whole file to the buffer
		Status = ShellReadFile(FileHandle,&ReadSize,HandleBuffer);
		SHA1Input(&sha,(unsigned char *)  HandleBuffer, ReadSize);
   } 
  
  //Output
  Print(L"File Name: %s\n",Argv[1]);
  Print(L"File Size: %d\n",(UINTN)FileInfo-> FileSize);

  if (!SHA1Result(&sha))
        {
            Print(L"sha: could not compute message digest\n");
        }
  else
        {
            Print(L"%08X %08X %08X %08X %08X\n",
                    sha.Message_Digest[0],
                    sha.Message_Digest[1],
                    sha.Message_Digest[2],
                    sha.Message_Digest[3],
                    sha.Message_Digest[4]);
        }
  
  FreePool(HandleBuffer);	
  return EFI_SUCCESS;
}

 

运行结果:

sha-1

完整的代码下载:

sha1test

参考:

1.https://www.packetizer.com/

Step to UEFI (60) —– GUID和UUID

了解了一下 UUID 和 GUID , 资料上来看,这两个是同一个东西。GUID 是 MS 定义的 UUID 的一种实现方法. 两者在写法上稍微有些差别【参考1】【参考2】:

GUID:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)

UUID:xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx (8-4-4-16)

在 UEFI 中,我们经常看到类似下面的定义

“cf8e16a5-c1e8-4e25-b712-4f54a96702c8”

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

UUID PadFileGuid;

对于 GUID ,可以在 \MdePkg\Include\Base.h 看到下面的定义

///
/// 128 bit buffer containing a unique identifier value.
/// Unless otherwise specified, aligned on a 64 bit boundary.
///
typedef struct {
  UINT32  Data1;
  UINT16  Data2;
  UINT16  Data3;
  UINT8   Data4[8];
} GUID;

 

同时,在 \MdePkg\Include\Uefi\UefiBaseType.h 有下面的定义

///
/// 128-bit buffer containing a unique identifier value.
///
typedef GUID                      EFI_GUID;

 

在 \BaseTools\Source\C\Common\CommonLib.c 有一个打印输出的例子可以直接拿出来用


EFI_STATUS
PrintGuid (
  IN EFI_GUID *Guid
  )
/*++

Routine Description:

  This function prints a GUID to STDOUT.

Arguments:

  Guid    Pointer to a GUID to print.

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
  if (Guid == NULL) {
    Error (NULL, 0, 2000, "Invalid parameter", "PrintGuidToBuffer() called with a NULL value");
    return EFI_INVALID_PARAMETER;
  }

  printf (
    "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
    (unsigned) Guid->Data1,
    Guid->Data2,
    Guid->Data3,
    Guid->Data4[0],
    Guid->Data4[1],
    Guid->Data4[2],
    Guid->Data4[3],
    Guid->Data4[4],
    Guid->Data4[5],
    Guid->Data4[6],
    Guid->Data4[7]
    );
  return EFI_SUCCESS;
}

 

最后,写一个输出的例子

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

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


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

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

#define GUID2 \
  { \
    0x33221100, 0x5544, 0x7766, {0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF } \
  }
  
EFI_STATUS
PrintGuid (
  IN EFI_GUID *Guid
  )
  
/*++

Routine Description:

  This function prints a GUID to STDOUT.

Arguments:

  Guid    Pointer to a GUID to print.

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
  if (Guid == NULL) {
	printf("Parameter error!\n");
    return EFI_INVALID_PARAMETER;
  }

  printf (
    "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
    (unsigned) Guid->Data1,
    Guid->Data2,
    Guid->Data3,
    Guid->Data4[0],
    Guid->Data4[1],
    Guid->Data4[2],
    Guid->Data4[3],
    Guid->Data4[4],
    Guid->Data4[5],
    Guid->Data4[6],
    Guid->Data4[7]
    );
  return EFI_SUCCESS;
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_GUID T1=GUID1;
  EFI_GUID T2=GUID2;
  
  PrintGuid(&T1);
  PrintGuid(&T2);
  
  return EFI_SUCCESS;
}

 

运行结果

guid

完整代码下载

GUIDTest

参考:
1.http://baike.baidu.com/view/1052579.htm UUID
2.http://blog.csdn.net/forlong401/article/details/7580147 UUID 和 GUID 的区别
3.https://en.wikipedia.org/wiki/Globally_unique_identifier Globally unique identifier
4.https://en.wikipedia.org/wiki/Universally_unique_identifier Universally unique identifier

Step to UEFI (58) —– 计算MD5

MD5 是目前常用的Hash算法,可以用来校验文件的完整性,或者比对密码等等。本文介绍如何在UEFI下实现计算一个文件MD5的方法。具体算法来自【参考1】,在编译过程中修正了几个关于类型转换的 Warning 。

程序结构很简单,和前一篇关于CRC32的没有多大差别。

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

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

#include "md5.h"

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

extern EFI_SHELL_PROTOCOL        *gEfiShellProtocol;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_FILE_HANDLE   FileHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  EFI_HANDLE        *HandleBuffer=NULL;
  UINTN  			ReadSize;
  MD5_CTX			ctx;
  char				R[16];
  
  //Check if there is a parameter
  if (Argc == 1) {
	Print(L"Usage: crctest [filename]\n");
	return 0;
  }
  
  //Open the file given by the parameter
  Status = ShellOpenFileByName(Argv[1], (SHELL_FILE_HANDLE *)&FileHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }			

  //Get file size	  
  FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	

  //Allocate a memory buffer
  HandleBuffer = AllocateZeroPool((UINTN) FileInfo-> FileSize);
  if (HandleBuffer == NULL) {
      return (SHELL_OUT_OF_RESOURCES);   }

  ReadSize=(UINTN) FileInfo-> FileSize;
  
  //Load the whole file to the buffer
  Status = ShellReadFile(FileHandle,&ReadSize,HandleBuffer);
  
  MD5_Init(&ctx);
  MD5_Update(&ctx, HandleBuffer, ReadSize);
  MD5_Final((unsigned char *)&R, &ctx);
  
  //Output
  Print(L"File Name: %s\n",Argv[1]);
  Print(L"File Size: %d\n",ReadSize);
  Print(L"File Size: %d\n",ReadSize);
  Print(L"MD5      : %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
					R[0]&0xFF  ,R[1]&0xFF  ,R[2]&0xFF  ,R[3]&0xFF  ,
					R[4]&0xFF  ,R[5]&0xFF  ,R[6]&0xFF  ,R[7]&0xFF  ,
					R[8]&0xFF  ,R[9]&0xFF  ,R[0xA]&0xFF,R[0xB]&0xFF,
					R[0xC]&0xFF,R[0xD]&0xFF,R[0xE]&0xFF,R[0xF]&0xFF);
  
  FreePool(HandleBuffer);	
  return EFI_SUCCESS;
}

 

运行结果

md51

可以看到,上面的例子能够在Shell下工作正常,但是需要特别注意,我没有验证过 X64 是否正常,如果你有这个需求,请自行验证。

完整的代码下载:

MD5Test

参考:

1.http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 A portable, fast, and free implementation of the MD5 Message-Digest Algorithm (RFC 1321)

Step to UEFI (57.5) —– 计算CRC32

这里介绍如何编写一个计算CRC32的工具。在EDK的代码中,BaseTools\Source\Common 中有Crc32的例子,这里直接引用了 CRC32.h 和 CRC32.C。

代码中有很多注释,因此不做解释了。

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

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

#include "Crc32.h"

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

extern EFI_SHELL_PROTOCOL        *gEfiShellProtocol;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_FILE_HANDLE   FileHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  EFI_HANDLE        *HandleBuffer=NULL;
  UINTN  			ReadSize;
  UINT32			CrcOut=0;
  
  //Check if there is a parameter
  if (Argc == 1) {
	Print(L"Usage: crctest [filename]\n");
	return 0;
  }
  
  //Open the file given by the parameter
  Status = ShellOpenFileByName(Argv[1], (SHELL_FILE_HANDLE *)&FileHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }			

  //Get file size	  
  FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	

  //Allocate a memory buffer
  HandleBuffer = AllocateZeroPool((UINTN) FileInfo-> FileSize);
  if (HandleBuffer == NULL) {
      return (SHELL_OUT_OF_RESOURCES);   }

  ReadSize=(UINTN) FileInfo-> FileSize;
  
  //Load the whole file to the buffer
  Status = ShellReadFile(FileHandle,&ReadSize,HandleBuffer);
  
  //Calculate the CRC32
  CalculateCrc32 ((UINT8*)HandleBuffer,ReadSize,&CrcOut);
  
  //Output
  Print(L"File Name: %s\n",Argv[1]);
  Print(L"File Size: %d\n",ReadSize);
  Print(L"CRC32    : %x\n",CrcOut);
  
  FreePool(HandleBuffer);	
  return EFI_SUCCESS;
}

 

运行结果 (计算自己的CRC32)

crc

为了保证正确,下载了一个CRC计算工具,可以看出,计算结果和上面的相同。

compare

完整代码下载

CRCTest

实际上除了上面的方法之外,还有更加简单的方法:直接使用 Boot Services 中的 CalculateCrc32 功能:

Capture

就是这样。

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 的代码