Step to UEFI (123)NOOPT 编译问题

之前我们在介绍 Source Level Debug 的文章中使用过 Noopt 的选项。后来找人请教了一下这个编译目标的含义:关闭一切编译优化。我猜测这样做的目的是为了让编译结果很容易实现汇编和 Source Code的一一对应。但是,很多情况下,编写EDK2代码的人都会忘记测试这个选项,于是,直接下载的代码会碰到X64 IA32 一切正常,唯独=在这个编译目标上发生问题。
比如,最近我又碰到了关于这个编译目标的问题,下面是简化之后的代码,可以帮助展示这个问题:

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

/**
  The user Entry Point for Application. The user code starts with this function
  as the real entry point for the application.

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.  
  @param[in] SystemTable    A pointer to the EFI System Table.
  
  @retval EFI_SUCCESS       The entry point is executed successfully.
  @retval other             Some error occurs when executing this entry point.

**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
        UINTN  *z;
        UINT64 x;
  z=(UINTN  *)1024;  //取内存 1024处的值,这里只是为了演示没有实际意义
                                     //如果直接赋值常数,在编译过程中会被优化掉
  x=*z;

  Print (L"%d",x / 1024);
  return EFI_SUCCESS;
}

 

上面的代码在下面的编译命令时

Build –a IA32 –p MdeModulePkg/MdeModulePkg.dsc –b NOOPT 

 

会出现这样的报错

“C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\link.exe” /OUT:c:\buildbs\test\Build\MdeModule\NOOPT_VS2015x86\IA32\MdeModulePkg\Application\NooptTest\NooptTest\DEBUG\NooptTest.dll /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL /ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG @c:\buildbs\test\Build\MdeModule\NOOPT_VS2015x86\IA32\MdeModulePkg\Application\NooptTest\NooptTest\OUTPUT\static_library_files.lst
NooptTest.lib(NooptTest.obj) : error LNK2001: unresolved external symbol __aulldiv
c:\buildbs\test\Build\MdeModule\NOOPT_VS2015x86\IA32\MdeModulePkg\Application\NooptTest\NooptTest\DEBUG\NooptTest.dll : fatal error LNK1120: 1 unresolved externals
NMAKE : fatal error U1077: ‘”C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\link.exe”‘ : return code ‘0x460’
Stop.

build…
: error 7000: Failed to execute command
C:\Program Files (x86)\Microsoft Visual Studio 14.0\Vc\bin\nmake.exe /nologo tbuild [c:\buildbs\test\Build\MdeModule\NOOPT_VS2015x86\IA32\MdeModulePkg\Application\NooptTest\NooptTest]

具体原因我不清楚,-b Release 以及 –b Debug 都不存在问题,可以确定和 NOOPT 模式有关。
标准的解决方法是使用 BaseLib.h 中的DivU64x32函数来代替直接除法运算避免这个问题。同样的,如果针对64位变量进行左移右移也会遇到类似的问题,同样在
BaseLib.h 中可以找到替代的函数。

Step to UEFI Tips :打印 256 个字节

一个很简单的例子,打印256个字节

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

static UINT32 next = 1;
/** Expands to an integer constant expression that is the maximum value
    returned by the rand function.
**/
#define RAND_MAX  0x7fffffff

/** 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.
**/
int
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));
}

void
srand(unsigned int seed)
{
  next = (UINT32)seed;
}


int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	UINT32	Buffer[256];
	UINT32	i;
	UINT32	j;
	
	srand(0);
	for (i=0;i<256;i++)
	{
		Buffer[i]=(UINT8)(rand()%256);
	}
	Print(L" \\|");
	for (i=0;i<16;i++)
		{
			Print(L" %2X|",i);
		}	
	Print(L"\n");
	for (j=0;j<16;j++)
	{
		Print(L"%2X| ",j);
		for (i=0;i<16;i++)
		{
			Print(L"%2X  ",Buffer[i*16+j]);
		}
		Print(L"\n");
	}
  
  return EFI_SUCCESS;
}

运行结果:

simpletest

完整的程序

 

ShowData

介绍一个好用的 ACPIDUMP 工具

之前我一直使用 RW 来 DUMP ACPI TABLE,但是最近一段时间发现这个工具不好用了。应该是和 MicroSoft RS2 之类的升级有关系。退而求其次,只好使用 UEFI 下面的工具。 在 【参考1】 可以下载到 UEFI Shell 下的DUMP工具,经过我的实验挺好用的。 有 32 和 64 的版本,但是似乎没有 Source Code。

使用方法很简单:

acpidump

即可。但是千万注意最好让他输入到文件而不是屏幕上否则会滚屏很久(譬如说2MB的内容)。 方法是 acpidump 文件名.txt

下载: acpica-efi

参考:
1.https://acpica.org/downloads/uefi-support

CurieNano 直接播放声音

前一段拿到了 DFRobot的CurieNano控制板。除了控制器,还有两个徽章以及多功能便携工具卡:

220537naqkmwaaihumzhak

可以看到这次的主角 Curie Nano 非常小巧,但是功能和全尺寸的 Arduino 101 相比毫不逊色。接下来我就使用这块小板子完成直接播放声音的功能,之前我在【参考1】中介绍过。 唯一的问题是直接寄存器仿佛古文一般面目可憎,又好比英文小说,每一个字母都认识,但是放在一起变成了天书。 仔细捋了一下原理,重新编写了一次。
从原理出发,声音播放就是在特定的频率下(通常大于8000Hz),将数字量化后的信号按照模拟的方式输出。而对于Arduino 来说,最简单的模拟输出就是用PWM。参考1的代码用了一个定时器,8000Hz触发一次,这样整体的代码非常复杂。我们修改为循环,输出之后做足够的延时(1/8000=125us),实际上的输出也是8000Hz,但是整体变得直观和容易理解多了。此外,默认的 PWM输出频率过小(关于 Curie的PWM我会另外介绍),这会导致analogWrite输出的PWM还没有来得及输出完整的一个周期下面的数据就过来。因此,需要升高PWM的频率。对于101 来说,使用analogWriteFrequency(Pin,Frequency);即可指定输出的 PWM 频率(意想不到的简单)。
首先,我们要大概估计一下空间,编写下面这样的简单代码:

void setup() {
analogWriteFrequency(OUTPIN, 64000);
}
void loop() {
}

 

剩余空间差不多有 155648-48016=107632 Bytes,大概可以存放107632/8000=13.4秒的声音(实际上还有额外库函数的开销,实际写代码根据编译结果删除了很多数据)。
220537ll4xtkt883x45pxn

硬件连接上非常简单:Uno 的地接在喇叭负极标志处上,然后随便选一个 支持PWM的引脚作为输出,这里我选择的是Pin9接在喇叭的正极标记处。

220538gm9mnu44d8f4440p

播放的内容是罗大佑的《恋曲1990》,首先用工具将音频转化为 8000Hz,8Bits,然后用【参考4】的工具转化数据为 C的格式,放置在代码中即可。
不完整的程序如下:

#define OUTPIN 9

const unsigned char sample[] PROGMEM =
{
        0x80, 0x80, 0x84, 0x85, 0x85, 0x83, 0x83, 0x81, 0x82, 0x83, 0x85, 0x82, 0x80, 0x7F, 0x7F, 0x80, 0x83, 0x81, 0x81, 0x80, 0x7F, 0x85, 0x87, 0x87, 0x86, 0x84, 0x84, 0x87, 0x84, 0x82, 0x81, 0x80,
        0x82, 0x87, 0x88, 0x88, 0x89, 0x84, 0x80, 0x7F, 0x80, 0x81, 0x82, 0x82, 0x83, 0x82, 0x83, 0x84, 0x83, 0x82, 0x80, 0x7F, 0x7D, 0x7A, 0x78, 0x75, 0x74, 0x72, 0x72, 0x74, 0x75, 0x73, 0x70, 0x70,
//很多很多数据
        0x7C, 0x76, 0x76, 0x7C, 0x76, 0x72, 0x77, 0x79, 0x77, 0x7B, 0x7F, 0x84, 0x87, 0x85, 0x7F, 0x85, 0x88, 0x86, 0x82, 0x81, 0x83, 0x82, 0x80, 0x7F, 0x82, 0x80, 0x7A, 0x74, 0x73, 0x74, 0x77, 0x7A,
        0x84, 0x88, 0x87, 0x83, 0x85, 0x8B, 0x90, 0x91, 0x8F, 0x8A, 0x8A, 0x88, 0x88, 0x82, 0x7F, 0x7D, 0x74, 0x6D, 0x6A, 0x67, 0x6B, 0x6E, 0x74, 0x75, 0x74, 0x6F, 0x70, 0x73, 0x77, 0x7B, 0x7B, 0x7B,
        }
        void setup()
        {
                analogWriteFrequency(OUTPIN, 64000);
        }

        void loop()
        {
                for (unsigned int i = 0; i < sizeof(sample); i++)
                {
                        analogWrite(OUTPIN, pgm_read_byte(&sample));
                        delayMicroseconds(125);
                }
        }

 

整个程序源代码500K。

220538s16xtttm8ltsusqu

完整代码下载:

1990s

针对这种代码的调试上有一些建议:

1. 最好使用歌曲作为素材,理由是单纯的音乐通常无法分清楚当前的播放是否正常。当然,如果你的喇叭质量不好,使用<<月亮之上>>这样的也可以掩盖破音(这也是为什么山寨机最喜欢放这首曲子的原因);
2. 为了安全起见,建议在喇叭上串联一个电阻,防止电流超过40ma;
3. 因为数据被定义在 Flash中,所以一定要使用pgm_read_byte(&sample)这种形式读取出来,切忌直接使用sample.这个问题让我头晕了很久.
4.
有了这样的方法, 你可以尝试在自己的作品中加入声音的功能。 后面我还会尝试使用板载SPI芯片来扩展存储更多的音频。

参考:
1. http://www.lab-z.com/arduinosound/使用Arduino直接发声
2. http://www.diy-robots.com/?p=852Arduino系列教程之 – PWM的秘密(下)
3. http://www.diy-robots.com/?p=814Arduino系列教程之 – PWM的秘密(上)
4. http://www.arduino.cn/thread-46025-1-1.html一个wave 转h 文件的工具

Step to UEFI (122)System Table 哪里来的

很多年前,有个笑话,讲的是一个领导,水平不高,发言只能照着秘书写的稿子念。有一次开大会,传达精神,他念:“正确的思想是天上掉下来的!”此言一出,台下昏昏欲睡的人登时来了精神。只见领导翻了一页,又大声的念到“吗?”

我们编写 Shell Application 最常用的 System Table 当然也不是天上掉下来的,最近研究了一下 System Table 的来源。在【参考1】中,给出来这个是在DxeMain.c 中创建的。

以OVMF 代码为例, 在 \MdeModulePkg\Core\Dxe\DxeMain\DxeMain.c 中可以查到。为了验证,我修改代码,在整个 Table 的最后面加上一个标记。

  // Initialize Memory Services
  //
  CoreInitializeMemoryServices (&HobStart, &MemoryBaseAddress, &MemoryLength);

  MemoryProfileInit (HobStart);

  //
  // Allocate the EFI System Table and EFI Runtime Service Table from EfiRuntimeServicesData
  // Use the templates to initialize the contents of the EFI System Table and EFI Runtime Services Table
  //
  //LABZDebug gDxeCoreST = AllocateRuntimeCopyPool (sizeof (EFI_SYSTEM_TABLE), &mEfiSystemTableTemplate);
  //LABZDebug_Start
  gDxeCoreST = AllocateRuntimeCopyPool (sizeof (EFI_SYSTEM_TABLE)+4, &mEfiSystemTableTemplate);
  p=(UINT8 *)gDxeCoreST;
  *(p+sizeof (EFI_SYSTEM_TABLE))='L';
  *(p+sizeof (EFI_SYSTEM_TABLE)+1)='A';
  *(p+sizeof (EFI_SYSTEM_TABLE)+2)='B';
  *(p+sizeof (EFI_SYSTEM_TABLE)+3)='Z';
   
   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "DBGMark\n"));
   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "0x%x\n", p));
   DEBUG ((DEBUG_INFO | DEBUG_LOAD, "0x%x\n", p+sizeof (EFI_SYSTEM_TABLE)));	
  //LABZDebug_End
  ASSERT (gDxeCoreST != NULL);

  gDxeCoreRT = AllocateRuntimeCopyPool (sizeof (EFI_RUNTIME_SERVICES), &mEfiRuntimeServicesTableTemplate);
  ASSERT (gDxeCoreRT != NULL);

 

编译BIOS 然后在 QEMU 上运行,先是查看串口输出:

st1

Install PPI: 605EA650-C65C-42E1-BA80-91A52AB618C6
CoreInitializeMemoryServices:
BaseAddress – 0x4022000 Length – 0x3F5F000 MinimalMemorySizeNeeded – 0x10F4000
DBGMark
0x7F6F010
0x7F6F058
InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 7FBA1B0
HOBLIST address in DXE = 0x7D98010

可以看到给出来的 System Table的内存地址是 0x7F6F010。我们启动到 Shell 使用 mm 命令可以看到内存的信息,其中包括当前的 SystemTable 的地址

st2

可以看到SystemTable 的地址就是Log中输出的地址,因此上面的位置就是生成的代码。
接下来再用mem 命令直接查看内存,同样可以看到我们在内存中的标记。

st3

参考:
1. http://blog.csdn.net/jiangwei0512

解析 DevicePath 的例子

krishnaLee(sssky307)为我们提供了一个解析DevicePath 的例子:

#include <Uefi.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>  //global gST gBS gImageHandle

#include <Protocol/LoadedImage.h> //EFI_LOADED_IMAGE_PROTOCOL
#include <Protocol/DevicePath.h> //EFI_DEVICE_PATH_PROTOCOL
#include <Protocol/DevicePathToText.h> //EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
#include <Library/DevicePathLib.h> //link

//reference:http://www.cppblog.com/djxzh/archive/2012/03/06/167106.aspx
//reference:http://www.lab-z.com/getcurd/


//My custom struct defined by UEFI 2.6 Spec
typedef struct
{
        UINT8 Type;
        UINT8 SubType;
        UINT16 Length;
        UINT32 PartitionNumber;
        UINT64 PartitionStart;
        UINT64 PartitionSize;
        GUID PartitionSig;
        UINT8 PartitionFormat;
        UINT8 SignatureType;
} HardDriveMediaDevicePath;

EFI_STATUS
EFIAPI
UefiMain (
        IN EFI_HANDLE        ImageHandle,
        IN EFI_SYSTEM_TABLE  *SystemTable
)
{
        EFI_STATUS                Status;
        EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
        EFI_DEVICE_PATH_PROTOCOL  *ImageDevicePath=NULL;
        UINT8                     *path;

        EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *Device2TextProtocol;
        CHAR16  *TextDevicePath;
        Print(L"Print Nodes:\n");


        //open the Loaded image protocol,which is binded on the imageHandle,to get the device handle.
        Status = gBS->OpenProtocol (
                         gImageHandle,
                         &gEfiLoadedImageProtocolGuid,
                         (VOID**)&LoadedImage,
                         gImageHandle,
                         NULL,
                         EFI_OPEN_PROTOCOL_GET_PROTOCOL
                 );

//get the device path protocol for the device handle.
        if (!EFI_ERROR (Status))
        {
                Status = gBS->OpenProtocol (
                                 LoadedImage->DeviceHandle,
                                 &gEfiDevicePathProtocolGuid,
                                 (VOID**)&ImageDevicePath,    //get the path protocol
                                 gImageHandle,
                                 NULL,
                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL
                         );
        }

//parse the device path
        path=(UINT8 *)ImageDevicePath;

        while(1)
        {
                if(((EFI_DEVICE_PATH_PROTOCOL  *)path)->Type==0x7F||((EFI_DEVICE_PATH_PROTOCOL  *)path)->SubType==0xFF)
                {
                        //if it is end node:
                        Print(L"type:%d,subType:%d,length:%d\n",\
                              ((EFI_DEVICE_PATH_PROTOCOL  *)path)->Type,\
                              ((EFI_DEVICE_PATH_PROTOCOL  *)path)->SubType,\
                              ((EFI_DEVICE_PATH_PROTOCOL  *)path)->Length[0]+((EFI_DEVICE_PATH_PROTOCOL  *)path)->Length[1]*0xff);
                        break;
                }
                else
                {
                        UINT16 len=((EFI_DEVICE_PATH_PROTOCOL  *)path)->Length[0]+((EFI_DEVICE_PATH_PROTOCOL  *)path)->Length[1]*0xff;
                        Print(L"type:%d,subType:%d,length:%d\n",\
                              ((EFI_DEVICE_PATH_PROTOCOL  *)path)->Type,\
                              ((EFI_DEVICE_PATH_PROTOCOL  *)path)->SubType,\
                              ((EFI_DEVICE_PATH_PROTOCOL  *)path)->Length[0]+((EFI_DEVICE_PATH_PROTOCOL  *)path)->Length[1]*0xff);

                        //print my concern node
                        if(((EFI_DEVICE_PATH_PROTOCOL  *)path)->Type==0x4&&((EFI_DEVICE_PATH_PROTOCOL  *)path)->SubType==0x1)
                        {
                                HardDriveMediaDevicePath *path2=(HardDriveMediaDevicePath *)path;
                                Print(L"    PartitionNumber:%d \n    PartitionStart:0x%lx \n    PartitionSize:0x%lx \n    PartitionSig:%g \n    PartitionFormat:%d \n    SignatureType:%d \n",\
                                      path2->PartitionNumber,path2->PartitionStart,path2->PartitionSize,path2->PartitionSig,path2->PartitionFormat,path2->SignatureType);
                        }

                        //go to next node;
                        path+=len;
                }
        }//while end

//get a converter.
        Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid,
                                     NULL,
                                     (VOID**)&Device2TextProtocol
                                    );

//convert device path to text.
        if (!EFI_ERROR (Status))
        {
                TextDevicePath= Device2TextProtocol->ConvertDevicePathToText(ImageDevicePath, 1, 1);
                Print(L"%s\n",TextDevicePath);
                gBS->FreePool(TextDevicePath);
        }

//clear
        gBS->CloseProtocol(
                LoadedImage->DeviceHandle,
                &gEfiDevicePathProtocolGuid,
                gImageHandle,
                NULL);

        gBS->CloseProtocol(
                gImageHandle,
                &gEfiLoadedImageProtocolGuid,
                gImageHandle,
                NULL);

        return EFI_SUCCESS;
}

 

运行结果如下:

ptdf

X64 的 EFI: mytestpathX64
完整的代码下载: mytestpath

CD74HC4067 用法

CD74HC4067 作用是选通一路对十六路模拟信号,更详细的说,根据芯片上 S0-S3 四个不同管脚的组合,让SIG管脚和C0-C15导通。因此,最常见的用法是用来测试模拟信号。比如,Arduino Uno上面只有6个模拟输入,用一个CD74HC4067可以多扩展出来16个,于是可以支持 6+16-1=21个模拟引脚。

image001

这个芯片的使用方法非常简单,例如: S0-S3 分别是 0 0 0 0时,SIG就和 C0是导通的。因此,这里我做一个实验,将一些电阻串联起来,分别接在 C1 C3 C5 C9 C11 上面,然后测量换算每个Pin的电压.

//Mux control pins

int s0 = 7;

int s1 = 6;

int s2 = 5;

int s3 = 4;

 

//Mux in "SIG" pin

int SIG_pin = 0;

 

 

void setup(){

  pinMode(s0, OUTPUT);

  pinMode(s1, OUTPUT);

  pinMode(s2, OUTPUT);

  pinMode(s3, OUTPUT);

 

  digitalWrite(s0, LOW);

  digitalWrite(s1, LOW);

  digitalWrite(s2, LOW);

  digitalWrite(s3, LOW);

 

  Serial.begin(9600);

}

 

 

void loop(){

  int v;

 

  //Loop through and read all 16 values

  //Reports back Value at channel 6 is: 346

  for(int i = 0; i < 16; i ++){

    Serial.print("Value at channel ");

    Serial.print(i);

    Serial.print(" is : ");

    v=readMux(i);

    Serial.println(v * 5.0 / 1024);

  }

    Serial.println(" ");

    delay(3000);

}

 

 

int readMux(int channel){

  int controlPin[] = {s0, s1, s2, s3};

 

  int muxChannel[16][4]={

    {0,0,0,0}, //channel 0

    {1,0,0,0}, //channel 1

    {0,1,0,0}, //channel 2

    {1,1,0,0}, //channel 3

    {0,0,1,0}, //channel 4

    {1,0,1,0}, //channel 5

    {0,1,1,0}, //channel 6

    {1,1,1,0}, //channel 7

    {0,0,0,1}, //channel 8

    {1,0,0,1}, //channel 9

    {0,1,0,1}, //channel 10

    {1,1,0,1}, //channel 11

    {0,0,1,1}, //channel 12

    {1,0,1,1}, //channel 13

    {0,1,1,1}, //channel 14

    {1,1,1,1}  //channel 15

  };

 

  //loop through the 4 sig

  for(int i = 0; i < 4; i ++){

 

    digitalWrite(controlPin[i], muxChannel[channel][i]);

  }

 

  //read the value at the SIG pin

  int val = analogRead(SIG_pin);

 

  //return the value

  return val;

}

 

最终运行结果如下: 同时我使用万用表测量,两者相差在 0.02V左右,证明还是非常准确的.

image002

除了当作模拟的扩展之外,这个芯片还可以用来控制 LED。在电路的世界里,即便最简单的芯片都可以玩出让人匪夷所思的效果。

参考:

1. http://bildr.org/2011/02/cd74hc4067-arduino/#
2. http://www.tigoe.com/pcomp/code/arduinowiring/540/

UDK2017 来了

UDK 最近放出了 2017 【参考1】,这种正式的Release能够保证一定能通过编译,是稳定的版本(五月份的时候我抓过一次 EDK2 , 连编译都无法通过)。印象中前一个UDK2015 似乎并没有使用多长时间,看起来 EDK2 仍然在努力发展壮大。

首先下载 Source Code,我选择的是直接通过这个链接下载而不是 GIT 方式:

https://github.com/tianocore/edk2/archive/vUDK2017.zip

解压之后,和之前的一样,需要装一下编译必须的工具才能正常使用。在【参考2】下载edk2-BaseTools-win32。解压之后放在 BaseTools\Bin\Win32目录下。
Conf 目录中还缺少必要的 Target.TXT 之类的配置文件, 我们运行一次 BaseTools下的 Toolsetup.bat 即可。
然后,还需要nasm 这个汇编语言编译器。找到之后,将 nasm丢在 BaseTools\Bin\Win32目录下即可.
最后可以开始编译过程,和之前的命令相同,依然是:

Edksetup.bat
Build.bat

我是用的是 VS2013 X64的环境,编译命令为 build –a X64
ud2017

之后使用 build –a X64 run 启动 NT32 虚拟机:
udk20172

经过实验, 64Bit Application能够正常运行。看起来这是一个重要的改进。我们可以方便的在虚拟机中验证X64的Application了。

为了便于使用,这里放一个配置好的完整代码:

链接: http://pan.baidu.com/s/1boMd6c3 密码: ymxe

后面的程序和代码都会基于 UDK2017 环境.

参考:
1. https://github.com/tianocore/tianocore.github.io/wiki/UDK2017
2. https://github.com/tianocore/tianocore.github.io/wiki/UDK2017-How-to-Build
3. https://github.com/tianocore/edk2-BaseTools-win32

淘宝购物一定要催

DIY 免不了要在淘宝之类的买乱七八糟的东西。一般的时候我都会非常有耐心的等待。比如,定制 PCB 通常要等待一周左右。但是最近我买东西,然后一直等……结果等到了最后居然自动从等待收货状态变成了让我评价。

我在一家叫做”pcb工作室”的店铺买了几个固定块打算试试

1

10号下单,过了三天,状态改成了发货. 实际上是一直等待揽收.

2

途中我也问过回复是马上会发,结果等着等着,今天就变成了交易成功, 登录网页版, 没有办法请淘宝介入, 只有申请售后服务的选项, 最终我只得申请退货了.

后来问了一下朋友,他们告诉我如果卖家发货十四天后你还没有签收,那么就会自动切换成收获。

所以, taobao 买东西要记得催………

Step to UEFI (121)EFI_SMM_ACCESS2_PROTOCOL 的使用

有时候我们需要得知当前系统中SMRAM的分配情况,这时候需可以使用 EFI_SMM_ACCESS2_PROTOCOL。关于这个 PROTOCOL,可以在PI Spec Vol4 找到。这个 PROTOCOL 接口如下:
sm1
其中我们需要关注的是 GetCapabilities。
sm2
首先通过 SmramMapSize,获得下面的 EFI_SMRAM_DESCRIPTOR的数量,之后一个个进行枚举即可。
代码中用到的一些结构体定义可以在下面这个文件中找到:
\EdkCompatibilityPkg\Foundation\Framework\Guid\SmramMemoryReserve\SmramMemoryReserve.h
例如:

//
// *******************************************************
//  EFI_SMRAM_DESCRIPTOR
// *******************************************************
//
typedef struct {
  EFI_PHYSICAL_ADDRESS  PhysicalStart;  // Phsyical location in DRAM
  EFI_PHYSICAL_ADDRESS  CpuStart;       // Address CPU uses to access the SMI handler
  // May or may not match PhysicalStart
  //
  UINT64                PhysicalSize;
  UINT64                RegionState;
} EFI_SMRAM_DESCRIPTOR;

//
// *******************************************************
//  EFI_SMRAM_STATE
// *******************************************************
//
#define EFI_SMRAM_OPEN                0x00000001
#define EFI_SMRAM_CLOSED              0x00000002
#define EFI_SMRAM_LOCKED              0x00000004
#define EFI_CACHEABLE                 0x00000008
#define EFI_ALLOCATED                 0x00000010
#define EFI_NEEDS_TESTING             0x00000020
#define EFI_NEEDS_ECC_INITIALIZATION  0x00000040
源代码如下:
/** @file
  Dump Capsule image information.

  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

 

根据上面的资料,编写代码如下:

#include <PiDxe.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/Shell.h>
#include <Protocol/ShellParameters.h>
#include <Protocol/SmmAccess2.h>

EFI_SMRAM_DESCRIPTOR  *mSmramRanges;
UINTN                 mSmramRangeCount;
  
/**
  Print APP usage.
**/
VOID
PrintUsage (
  VOID
  )
{
  Print(L"FVBDemo:  usage\n");
  Print(L"  FVBDemo <FileName>\n");
}
/**
  @param[in]  ImageHandle     The image handle.
  @param[in]  SystemTable     The system table.

  @retval EFI_SUCCESS            Command completed successfully.
  @retval EFI_INVALID_PARAMETER  Command usage error.
  @retval EFI_NOT_FOUND          The input file can't be found.
**/
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                Status=EFI_SUCCESS;
  EFI_SMM_ACCESS2_PROTOCOL  *SmmAccess;
  UINTN                     Size;
  UINTN                     Index;  
  
  //
  // Locate SMM Access2 Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiSmmAccess2ProtocolGuid, 
                  NULL, 
                  (VOID **)&SmmAccess
                  );
  if (EFI_ERROR(Status)) {
        Print(L"Can't find SmmAccess2Protocol\n");
  };

  //
  // Get SMRAM range information
  //
  Size = 0;
  Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);

  mSmramRanges = (EFI_SMRAM_DESCRIPTOR *) AllocatePool (Size);
  if (mSmramRanges == NULL) {
          Print(L"Allocate Memory Error!\n");
          return EFI_SUCCESS;
  }

  Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);
  if (EFI_ERROR(Status)) {
          Print(L"GetCapabilities Error!\n");
          return EFI_SUCCESS;
  }

  mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);

        Print(L"Index PhysicalStart CpuStart PhysicalSize RegionState\n");  
  for (Index = 0; Index < mSmramRangeCount; Index ++) {
        Print(L"[%d]   %8X      %8X %8X     %8X\n",
                Index,
                mSmramRanges[Index].PhysicalStart,
                mSmramRanges[Index].CpuStart,
                mSmramRanges[Index].PhysicalSize,
                mSmramRanges[Index].RegionState);
    }   //for 
    
  FreePool (mSmramRanges);  
  
  return Status;
}

 

最终运行结果如下(测试平台是 KBL-R HDK):
sm3
完整的代码下载
SMMAccess