ACPI 工具的编译

ACPI 已经成为 x86 Firmware 密不可分的一部分,这里介绍一下对应工具组的编译。

编译工具
1. vs2008 (我使用的是能够编译UDK2014的环境)
2. Bison 可以在http://gnuwin32.sourceforge.net/packages/bison.htm 下载
3. Flex 可以在 http://gnuwin32.sourceforge.net/packages/flex.htm 下载
4. ACPICA 的 Windows Source Code https://www.acpica.org/downloads/windows-source 下载

安装 1 2 3,特别注意: 2 3 需要安装在 c:\GnuWin32目录下。然后在“我的电脑”“属性”“高级”“环境变量”的 Path 中加入 c:\GnuWin32\bin。之后打开一个控制台窗口,输入Path 要确保能找到这个目录。
asl1

之后解压4的内容到 acpica 目录下
asl2
打开源程序下面的 generate\msvc9中的 AcpiComponents.sln 即可编译(我用的是 VS2008)

asl4

直接编译,即可在对应目录中找到重新编译后的结果
asl3

个人建议根据需要下载你需要的版本(不同版本 ASL 编译工具之间差别很大,容易出现彼此之间无法识别的问题)。

本文提到的工具和源代码下载 链接: http://pan.baidu.com/s/1o7zRo2A 密码: jhbd

Step to UEFI Tips

有一句话, 是福尔摩斯的名言“排除一切不可能的,剩下的即使再不可能,那也是真相”。我很多次在《名侦探柯南》上看到这句话,在 Debug方面也快成为我的座右铭了,只是中文读起来逻辑上不是很严谨,原文是: When you have eliminated the impossibles,whatever remains,however improbable,must be the truth.

这次,在调试中又遇到了这样的事情。

我编写一个程序,只是简单的把1K大小的内存值写入到一个文件中,但是我惊奇的发现,对内存写入不同值,打开之后的结果和期望会有不同。

先上源代码

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

#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.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
  )
{
	UINTN 							FileSize=1024;
    EFI_STATUS          			Status;	
	EFI_FILE_PROTOCOL    			*Root;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	EFI_FILE_PROTOCOL 				*FileHandle=0;	
	UINTN							*HandleBuffer;

	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"wre.bin",
			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;	
	}	

	HandleBuffer = AllocateZeroPool(FileSize);
    if (HandleBuffer == NULL) {
		Print(L"Not enough memory!\n");
		return Status;
    }

	*(HandleBuffer)  =1;
	*(HandleBuffer+1)=2;
	*(HandleBuffer+2)=3;
	*(HandleBuffer+3)=4;

	Print(L"%d\n",(FileSize-4)/4);	
	Print(L"%d\n",sizeof(UINTN));	
	Print(L"%d\n",sizeof(UINT32));		
	Print(L"%x\n",HandleBuffer);		
	Print(L"%x\n",HandleBuffer+1);		

	Status = FileHandle -> Write(FileHandle, &FileSize, HandleBuffer);

	Print(L"Write Done \r\n");	
	FreePool(HandleBuffer);
	Status  = FileHandle -> Close (FileHandle);

  return EFI_SUCCESS;
}

运行结果会生成 wre.bin, 我用 Notepad++ 打开之后看到的结果是下面,看起来是按照 WORD 写入的而不是我期望的UINT32

wrA

而如果把 *(HandleBuffer) =1; 这行修改为 *(HandleBuffer) =0x1111 1111;

打开同样的文件看到的是下面的结果,很明显这才是我期望的:

wrb

<此处空白,有兴趣的朋友可以猜测一下,真相到底是什么>
kenan

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

真正的原因是我用的 Notepad++ 有问题,如果我换一款十六进制编辑软件即可看到正确的结果。比如, HHD Hex Editor Noe。

下面是用 Beyond Compare 比较两次生成文件,查看是否有差别

wreR

由此完全可以看出,二者没有差别,我遇到的奇怪现象完全是查看工具导致的。

看到这里,你完全可以和我一起默念“排除一切不可能的,剩下的即使再不可能,那也是真相”。

Intel FMMT 工具的编译

AMI 有 MMTOOL 这样的可以替换模块的工具,Intel有自己的BIOS,也有自己的工具: FMMT ,全称是 Firmware Module Management Tools。可以在 http://firmware.intel.com/develop 页面下载到。

(特别注意:编译必须在 UDK2015下面进行,http://sourceforge.net/projects/edk2/files/UDK2015_Releases/)

编译 FMMT 对于其他工具有依赖,需要把 UDK2015中的 Basetools(Windows)解压出来。这里我直接解压在 c: 下面的UDK2015目录中。

fmmt1

将FMMT的source code解压到 BaseTools\Source\C 下面

fmmt3

打开 Vistual Studio VS2008 的命令行窗口,进入 UDK2015 目录,运行 EdkSetup.bat

fmmt2

进入 BaseTools 运行 toolsetup.bat 。然后运行 nmake (因为 FMMT对于其他模块有依赖,所以这里需要先Build一下)

fmmt4

再进入 source\c\fmmt 目录下, 再次运行 Nmake

fmmt5

编译结果在 BaseTools\Bin\Win32 下面

fmmt6

本文相关内容下载:

1.FMMT WW48 v0.19 fMMT
2.完整的编译环境下载,主要是 UDK2015 BaseTools 和 FMMT 的 Source Code 链接: http://pan.baidu.com/s/1c0Ur2cK 密码: srp4
3.XP下面可以运行的 FMMT (不知道为什么,2 中的 FMMT 无法在XP下直接运行,但是用上面的方法在 XP 下编译一次的结果可以 官方工具在发布时,选择使用 vs2013或者更高的版本编译,并且没有选择 XP 兼容模式造成的。)
2015-WW48-FMMT.19

编写一个APP教孩子认东西

最近学习了一下 MIT App Inventor 2 ,这是一种非常简单的可以方便编写 Android APP 的程序。

原理很简单,首先设计自己的二维码(比如:字符串 men2 表示“门”;deng1表示灯),打印出来之后贴在物品上,然后用自己编写的APP去扫描这些二维码,播放事先录制好的声音。

yHotaMFUwXU3OMcbJIRjJTF7g-HwZb0iSEhqVoA7ZLQeAgAACgMAAFBO

工作的视频:
http://www.tudou.com/programs/view/-r_TY3ll7dc/?resourceId=0_06_02_99

本文提到的二维码和AIA工程文件下载

app1

Step to UEFI (81) —–测试文件生成器

一些情况下,我们需要在 Shell 下面使用文件进行测试,这次编写一个工具,生成使用随机数填充的文件。为了校验方便,文件的末尾有一个 checksum,按照 32Bits 的 UINTN ,整个文件的和应该是 0 .

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

#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>

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

//copied from \StdLib\Include\stdlib.h
/** Expands to an integer constant expression that is the maximum value
    returned by the rand function.

    The value of the RAND_MAX macro shall be at least 32767.
**/
#define RAND_MAX  0x7fffffff

//Copied from \StdLib\LibC\StdLib\Rand.c
static UINT32 next = 1;

/** Compute a pseudo-random number.
  *
  * Compute x = (7^5 * x) mod (2^31 - 1)
  * without overflowing 31 bits:
  *      (2^31 - 1) = 127773 * (7^5) + 2836
  * From "Random number generators: good ones are hard to find",
  * Park and Miller, Communications of the ACM, vol. 31, no. 10,
  * October 1988, p. 1195.
**/
UINT32
rand()
{
  INT32 hi, lo, x;

  /* Can't be initialized with 0, so use another value. */
  if (next == 0)
    next = 123459876;
  hi = next / 127773;
  lo = next % 127773;
  x = 16807 * lo - 2836 * hi;
  if (x < 0)
    x += 0x7fffffff;
  return ((next = x) % ((UINT32)RAND_MAX + 1));
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	UINTN 							FileSize=0;
    EFI_STATUS          			Status;	
	EFI_FILE_PROTOCOL    			*Root;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	EFI_FILE_PROTOCOL 				*FileHandle=0;	
	UINTN		*HandleBuffer;
	UINT32		i,t,sum=0;

	if (Argc>1) {
		FileSize=StrDecimalToUintn(Argv[1])*1024;
		//Print(L"%d",FileSize);	
	}
	else
	{
		FileSize=1024*1024*10;
	}
	
	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"ztest.bin",
			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;	
	}	
	
	HandleBuffer = AllocateZeroPool(FileSize);
    if (HandleBuffer == NULL) {
		Print(L"Not enough memory!\n");
		return Status;
    }
	
	for (i=0;i<(FileSize-4)/4;i++)
		{
			t=rand();
			*(HandleBuffer+i)=t;
			sum=sum+t;
		}

	*(HandleBuffer+(FileSize-4)/4)=(UINT32)(0-sum);

	Status = FileHandle -> Write(FileHandle, &FileSize, HandleBuffer);
	
	Print(L"Write Done \r\n");	
	FreePool(HandleBuffer);
	Status  = FileHandle -> Close (FileHandle);
	
  return EFI_SUCCESS;
}

 

除了生成文件的工具,还有一个用来校验文件的工具,他的作用是将文件全部内容相加,查看结果是否为0.

#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>

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_FILE_HANDLE   FileHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  UINT32	        *HandleBuffer=NULL;
  UINTN  			ReadSize;
  UINT32			i,sum=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);	

  //if the file size is not the multiple of 4, we don't check it!
  if ((UINTN) FileInfo-> FileSize % 4 !=0 ) {
	Print(L"We can't check this file as the filesize is wrong!\n");
  }
  
  //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);
  
  for (i=0;i< FileInfo-> FileSize / 4;i++)
	{
		sum=sum+*(HandleBuffer+i);
	}
  if (sum==0) {
	Print(L"Pass!\n");
  }
  else
	{
		Print(L"Fail!\n");
	}
  
  FreePool(HandleBuffer);	
  FileHandle -> Close (FileHandle);  
  
  return EFI_SUCCESS;
}

 

完整的代码和 EFI 文件下载:
TestFileGen
Chker

Step to UEFI (80) —–取得汉字的字形

EFI 在设计之初就考虑了多语言的支持,使用HII可以轻松的实现汉字的显示。本篇文章介绍获得汉字字形的其他方法,掌握这种方法之后可以在没有HII支持的情况下显示汉字。当然,程序只是为了演示原理,介绍如何读取16×16的汉字字形信息,没有转为图形。
比如:“宋”字查询到的区位码是4346 【参考3】,意思是区码为43,位码是46。计算这个字在字库中的方法是:((43-1)*94+(46-1))*32=6828。之后,在字库文件的 6828偏移处连续读取32个字节即可。

shz

代码如下:

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

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

extern EFI_BOOT_SERVICES *gBS;

#define FONT_SIZE (16)
#define HZ_INDEX(hz) ((hz[0] - 1) * 94 + (hz[1] -1))*32
#define DOTS_BYTES (FONT_SIZE * FONT_SIZE / 8)

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;
UINTN i,j;
UINT8 HZChar[2] = {43,46};
CHAR8 *c;
CHAR8 k;

//Open the file given by the parameter
Status = ShellOpenFileByName(L"HZK16K.BIN",
(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);
if(Status != RETURN_SUCCESS) {
Print(L"ReadFile failed!\n");
return EFI_SUCCESS;
}

for (i=0;i<DOTS_BYTES;i++)
{
c=((UINT8*)HandleBuffer)+HZ_INDEX(HZChar)+i;
k=*c;
for (j=0;j<8;j++)
{
if (0 == (k & 0x80))
{
Print(L" ");
}
else
{
Print(L"OO");
}
k=k<<1;
}
if ((i+1)%2==0) {Print(L"\n");}
}

FreePool(HandleBuffer);
ShellCloseFile((SHELL_FILE_HANDLE *)&FileHandle);

}
 

 

运行结果(特别注意要把字库文件放在Fsnt0:这样的目录下):

image002

更换一下区位码,我们还可以取得“我”的字形。
image004

完整的代码下载:
HZ

最后,关于【参考1】的代码多说两句。其中有unsigned char word[3] = “我”; 这样直接的定义,这是因为很久很久之前,为了编便于 PC处理汉字定义一个汉字由两个大于127的ASCII码组成。组成的规则是:区码+A0,位码+A0。比如,我在中文环境下定义一个“宋”,
image008

然后切换到英文环境下打开,看到的是2个ASCII码,

image010

如果再切换到十六进制编辑,会看到 CB CE (前提是保存为 ANSI格式,如果你存为unicode,看到的又是另外的东西)

image012

时代已经变了,对于 Windows 编程来说上述的知识都已经过时,如果你需要搞嵌入式开发,还是值得认真学习和理解。
另外,PC刚开始流行的时候,很长一段时间都有汉字不适合PC处理等等的言论,对于普通用户来说,汉字的输入也是很大的困扰。而最终的解决,我认为是人们强烈的交流的需求使得这样的问题很快被克服掉了。时至今日,我仍然能记得同一个寝室的胖子在他的 Nokia手机上,在十几个按键上运指如飞和各种MM聊得火热。很快,没人再认为汉字在PC的普及上是一个问题。

参考 :
1.https://blog.twofei.com/embedded/hzk.html HZK16汉字16*16点阵字库的使用及示例程序
2.http://blog.csdn.net/turingo/article/details/8191712 图灵狗的专栏
3.http://www.jscj.com/index/gb2312.php 汉字区位码查询系统 (具体)

==============================================================
2018年12月30日 补充: 在【参考1】的文章中提供了一个取得字模的代码,我在 Win10 下实验过,很好用:

代码如下:

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

int main(void)
{
	FILE* fphzk = NULL;
	int i, j, k, offset;
	int flag;
	unsigned char buffer[32];
	unsigned char word[5];
	unsigned char key[8] = {
		0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01
	};
	fphzk = fopen("hzk16", "rb");
	if(fphzk == NULL){
		fprintf(stderr, "error hzk16\n");
		return 1;
	}
	while(1){
		printf("输入要生成字模的汉字(多个):");
		for(;;){
			fgets((char*)word, 3, stdin);
			if(*word == '\n') 
				break;
			offset = (94*(unsigned int)(word[0]-0xa0-1)+(word[1]-0xa0-1))*32;
			fseek(fphzk, offset, SEEK_SET);
			fread(buffer, 1, 32, fphzk);
			for(k=0; k<16; k++){
				for(j=0; j<2; j++){
					for(i=0; i<8; i++){
						flag = buffer[k*2+j]&key[i];
						printf("%s", flag?"●":"○");
					}
				}
				printf("\n");
			}
			printf("uchar code key[32] = {");
			for(k=0; k<31; k++){
				printf("0x%02X,", buffer[k]);
			}
			printf("0x%02X};\n", buffer[31]);
			printf("\n");
		}
	}
	fclose(fphzk);
	fphzk = NULL;
	return 0;
}

 

如果你是英文的OS,需要先切换内码为 CP936

Step to UEFI (79) —–取得字形

我们可以通过EFI_HII_FONT_PROTOCOL【参考1】 中的 GetGlyph来取得一些字符的字形定义。

gly1

GetGlyph 的原型可以在 \MdePkg\Include\Protocol\HiiFont.h 中找到:

/**

  Convert the glyph for a single character into a bitmap.

  @param This       A pointer to the EFI_HII_FONT_PROTOCOL instance.

  @param Char       The character to retrieve.

  @param StringInfo Points to the string font and color
                    information or NULL if the string should use
                    the default system font and color.

  @param Blt        This must point to a NULL on entry. A buffer will
                    be allocated to hold the output and the pointer
                    updated on exit. It is the caller's responsibility
                    to free this buffer.

  @param Baseline   The number of pixels from the bottom of the bitmap
                    to the baseline.


  @retval EFI_SUCCESS             The glyph bitmap created.

  @retval EFI_OUT_OF_RESOURCES    Unable to allocate the output buffer Blt.

  @retval EFI_WARN_UNKNOWN_GLYPH  The glyph was unknown and was
                                  replaced with the glyph for
                                  Unicode character code 0xFFFD.

  @retval EFI_INVALID_PARAMETER   Blt is NULL, or Width is NULL, or
                                  Height is NULL


**/
typedef
EFI_STATUS
(EFIAPI *EFI_HII_GET_GLYPH)(
  IN CONST  EFI_HII_FONT_PROTOCOL *This,
  IN CONST  CHAR16                Char,
  IN CONST  EFI_FONT_DISPLAY_INFO *StringInfo,
  OUT       EFI_IMAGE_OUTPUT      **Blt,
  OUT       UINTN                 *Baseline OPTIONAL
);

 

其中 Char 是你要取对应字形的文字(特别注意是单个的CHAR16),StringInfo 为 NULL时取得的是系统默认的字体,输出结果在 Blt 中。最后一项含义我不清楚……
再看一下输出结果是 EFI_IMAGE_OUTPUT。它的定义在 \MdePkg\Include\Protocol\HiiImage.h中。其中含有一个Union的定义,在我们这里使用时,会按照 EFI_GRAPHICS_OUTPUT_BLT_PIXEL 给出。

最后写一个简单的测试程序如下,功能是取得“z”字符的字形。

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

#include <Protocol/HiiFont.h>

extern EFI_BOOT_SERVICES         *gBS;
 
int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
    EFI_STATUS	Status = 0;
	UINTN		BaseLine;
	UINTN		i,j;	
    EFI_HII_FONT_PROTOCOL	*HiiFont = 0;
	EFI_IMAGE_OUTPUT		*Blt=NULL;
	CHAR8		*c,*p;
	
	Status = gBS->LocateProtocol (&gEfiHiiFontProtocolGuid, NULL, (VOID **) &HiiFont);
	if (Status!=EFI_SUCCESS) {
		Print(L"Error when LocateProtocol gEfiHiiFontProtocolGuid. Code[%r]\n",Status);
		return EFI_SUCCESS;
	}
   Status = HiiFont->GetGlyph (
				HiiFont,	//Protocol instance
				L'Z',		//Show char 'Z'
				NULL,		//Use the defualt system font and color
				&Blt,		//GLYPH information
				&BaseLine); //I don't know
				
	Print(L"This is [%d]x[%d]\n",Blt->Width,Blt->Height);
	
	c=(CHAR8*) Blt->Image.Bitmap;			
	for (j=0;j<Blt->Height;j++)
		{
			p=c;
			for (i=0;i<Blt->Width;i++)
				{
					Print(L"%2X",(*c)&0xFF);
					c=c+4;
				}
			Print(L"   ");	
			c=p+1;	
			for (i=0;i<Blt->Width;i++)
				{
					Print(L"%2X",(*c)&0xFF);
					c=c+4;
				}
			Print(L"   ");					
			c=p+2;	
			for (i=0;i<Blt->Width;i++)
				{
					Print(L"%2X",(*c)&0xFF);
					c=c+4;
				}
			c=p+(Blt->Width*4);
			Print(L"\n");	
		}	
		
	c=(CHAR8*) Blt->Image.Bitmap;			
	for (j=0;j<Blt->Height;j++)
		{
			for (i=0;i<Blt->Width;i++)
				{
					if (*c!=0) {
						Print(L"*");
					}
					else
					  {
						Print(L" ");
					  }	
					c=c+4;
				}
			Print(L"\n");	
		}			
		
	return EFI_SUCCESS;
}

 

运行结果,首先输出的是 R G B 数组,隐隐约约能看到其中有一个形状

gly2

程序后面有一个判断,直接输出星号和空格,结果如下,这样就看到非常清楚了。

gly3

完整的代码下载
GetGlyph

唯一的问题是:我还不知道取得这个东西能有什么用途…….

参考:
1.UEFI spec 2.4 P1711

Step to UEFI —– TIPs

最近调试程序的时候遇到一个奇怪的 Warning ,查了一会才找到原因:

c:\edk\AppPkg\Applications\C4066\C4066.c(17) : warning C4066: characters beyond first in wide-character constant ignored

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  Print(L'[%d]',2015);
  return EFI_SUCCESS;
}

 

错误产生的原因是:把双引号写成了单引号,编译器以为你要定义一个 CHAR16 的字符,所以要忽略一些东西。修改的方法是,改成双引号即可。

参考:

1.https://msdn.microsoft.com/en-us/library/aa748819(v=vs.60).aspx
Compiler Warning (level 3) C4066

Visual Studio 6.0
characters beyond first in wide-character constant ignored
The compiler processes only the first character of a wide-character constant.

Step to UEFI (78) —–SERIAL_IO_PROTOCOL

串口是非常有效和廉价的Debug手段,在开发中,几乎所有的UEFI 主板都会支持串口,本文介绍如何在Shell下面实现 串口通讯。
与之相关的是 EFI_SERIAL_IO_PROTOCOL,这个 Protocol 的定义可以在 UEFI Spec【参考1】中看到:

image001

代码如下:

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

#include  <Protocol/SerialIo.h>

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

EFI_GUID gEfiSerialIoProtocolGuid = { 0xBB25CF6F, 0xF1D4, 0x11D2, { 0x9A, 0x0C, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0xFD }};

EFI_STATUS
DumpSetting( 
  IN UINT64    BaudRate,
  IN UINT32    ReceiveFifoDepth,
  IN UINT32    Timeout,
  IN EFI_PARITY_TYPE    Parity,
  IN UINT8              DataBits,
  IN EFI_STOP_BITS_TYPE StopBits)
{
	Print(L"   Timeout: [%d]\n",Timeout);
	Print(L"   BaudRate:[%ld]\n",BaudRate);
	Print(L"   DataBits:[%d]\n",DataBits);
	Print(L"   Parity:  [%d]\n",Parity);
	Print(L"   StopBits:[%d]\n",StopBits);	
	Print(L"   ReceiveFifoDepth:[%d]\n",ReceiveFifoDepth);
	
	return EFI_SUCCESS;
}

  
int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS          			Status;
	EFI_SERIAL_IO_PROTOCOL 			*Serial;
	CHAR8 							*Textbuf1 = "www.lab-z.com  waiting.........\n";
	CHAR8 							*Textbuf2 = "Continue............\n";
	CHAR8 							*Textbuf3 = "12345678";	
	CHAR16 							*Textbuf4 = L"                               ";	
	UINTN							BufferSize;
	EFI_TIME						TimeStart,TimeEnd;	
	
    Status = gBS->LocateProtocol(
						&gEfiSerialIoProtocolGuid, 
						NULL,
						(VOID **)&Serial);
				
	
    if (EFI_ERROR(Status)) {
		    Print(L"Cannot find EFI_SERIAL_IO_PROTOCOL \r\n");
            return Status;	
	}

	Print(L"Current Settings:\n");
	DumpSetting(
			Serial->Mode->BaudRate,
			Serial->Mode->ReceiveFifoDepth,
			Serial->Mode->Timeout,
			Serial->Mode->Parity,
			Serial->Mode->DataBits & 0xFF,			
			Serial->Mode->StopBits);

	// Baudrate 115200,Data Bits=8,Parity=None,Stop Bits=1,Flow Type= None
	Status=Serial->SetAttributes(  Serial,
							115200,
							Serial->Mode->ReceiveFifoDepth,
							Serial->Mode->Timeout,
							NoParity,
							8,
							OneStopBit);
	if (Status!=EFI_SUCCESS) {
		Print(L"[%d], %r",Status,Status);
	}
							
	Print(L"New Settings:\n");
	DumpSetting(
			Serial->Mode->BaudRate,
			Serial->Mode->ReceiveFifoDepth,
			Serial->Mode->Timeout,
			Serial->Mode->Parity,
			Serial->Mode->DataBits & 0xFF,			
			Serial->Mode->StopBits);							
	
	BufferSize=AsciiStrLen(Textbuf1);
	Serial->Write(Serial,&BufferSize,Textbuf1);
	
	gRT->GetTime(&TimeStart,NULL);
	TimeEnd=TimeStart;

	while ((TimeEnd.Hour - TimeStart.Hour) * 60 * 60 
			+ (TimeEnd.Minute - TimeStart.Minute)*60 
			+ (TimeEnd.Second - TimeStart.Second) < 30)
	  {
		 BufferSize=AsciiStrLen(Textbuf3);	  
		 Status=Serial->Read(Serial,&BufferSize,Textbuf3);
		 if ((Status==EFI_SUCCESS) && (BufferSize!=0))
			{
				Print(L"read [%d] %s\n",BufferSize,AsciiStrToUnicodeStr(Textbuf3,Textbuf4));
			}
			gRT->GetTime(&TimeEnd,NULL);	
	  }	

	BufferSize=AsciiStrLen(Textbuf2);	  
	Serial->Write(Serial,&BufferSize,Textbuf2);
	
    return EFI_SUCCESS;
}

 

上面程序的基本流程:首先检查一下串口设置,打印在屏幕上,然后设置为我们通常使用的 115200。最后测试用 Write从 Shell 下发送数据出来,再尝试用Read接收数据。

程序运行结果:

image002

完整代码下载:

SerialTest

特别注意:Shell 使用 Read 只能接收固定长度的数据。比如: Read(Serial,8,Textbuf) 那么只能接收8个字符,如果你只输入了7个bytes,不会有反应;如果输入了9个bytes,那么只能收到前面8个。目前不清楚为什么有这样的限制。

另外,对于普通的串口,使用GetControl 获得的当前的状态中并没有当前串口的发送接收状态。定义的 EFI_SERIAL_INPUT_BUFFER_EMPTY和EFI_SERIAL_OUTPUT_BUFFER_EMPTY 应该是给存在对应线路的串口使用的,是一种硬件线路的标志。如果你只用了 TX RX GND , 这里的状态是没有意义的。

参考:
1. UEFI Spec 2.4 P476

UDK2015来了

UDK2015 发布已经有一段时间了,这几天正好有需要,所以研究了一下。安装和使用上与之前的版本没什么差别,都是解压 MyWorkSpace到需要的目录中,然后再放入 BaseTools(Windows).zip 中的内容。

但是,发布这个版本的人一定没有在 Windows XP 下次试过,编译BaseTool的工具也一定不是 VS2008。于是,按照上述方法配置后, Win32 下面的 GenFv.exe GetBootSector.exe 等等工具都无法运行,错误信息是“不是有效的 Win32 应用程序”。解决方法是:重新编译一次 C版本的工具,具体做法请参考之前的文章。

udk21015a

udk21015b

这里下载 UDK2015 还有我重新编译过的 BaseTools ,可以在 XP中运行

其中还有一个配置好的 VirtualBox 的虚拟机镜像,有需要的朋友可以直接下载到本地运行。

链接:http://pan.baidu.com/s/1eQNvkR4 密码:eks0