关于前面提到的 VOL 命令的问题

前面介绍过一些Shell下常用的命令,提到了查看当前 volume 大小的命令 VOL. 在使用中,会遇到一个奇怪的问题,感觉上有时候它能输出当前盘的大小,而有时候输出的是Partition的大小,具体的原因是什么呢?下载 VOL 的代码,进行查看注意到下面的位置:

Status = RootFs->GetInfo (RootFs, &gEfiFileSystemInfoGuid, &Size, VolumeInfo);

就是说VOL命令是基于File System的,显示出来的是文件系统能够识别出来的大小。

做一个简单的实验,将U盘进行分区,下面是只有一个分区的情况

Capture

实验的结果如下:

Volume NEW VOLUME (rw)
1,003,487,232 bytes total disk space
959,131,648 bytes available on disk
4,096 bytes in each allocation unit

再使用工具将这个U盘分区

Capture2

实验的结果如下:

Volume has no label (rw)
411,009,024 bytes total disk space
411,000,832 bytes available on disk
8,192 bytes in each allocation unit

显示出来的只是有文件系统的那个分区的大小。

因此,之前实验遇到的问题是:当查看的盘上只有一个分区,并且这个分区占满了全部空间的时候,显示的也是盘的大小;如果上面的分区没有占满盘的话,显示出来的只有分区大小了。

vol.c 可以在这里下载 vol

分离GOP和VBT文件的工具

一个用于分离GOP和VBT文件的工具

这个工具是用来分离Intel Baytrail公版BIOS中的VBT和GOP文件的。其中的GOP是传统意义上的VBIOS,VBT文件是他的配置文件。在之前,配置

信息通常会写入到VBIOS中,但是在BayTrail上他们是分开独立的。

用法:

BTYGVS [文件名]

比如:BTYGVS IFWI_2013_10_01 即可在当前目录下生成这个BIOS中

包括的GOP和VBT。

gvs

下载

bytgvs

Step to UEFI (9)—-使用RDTSC计算当前CPU 频率

很早之前Intel曾经提供过一个使用RDTSC在DOS下计算CPU频率的程序(在CPUID的Datasheet中)。简单的说就是延时一个固定的时间,然后看这个时间内经过了多少个指令周期,经过计算就能得到CPU的频率。下面的程序实现了在UEFI环境下计算频率的功能,同时也可以作为插入汇编指令的参考。需要注意,这是32位UEFI环境下,如果在64位的Shell环境下这样做是不行的。

//
// FreqCalc.C
//

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

EFI_SYSTEM_TABLE	*gST;
EFI_BOOT_SERVICES  	*gBS;

UINT64 rdtsc()
{
  UINT64 value;
  __asm
  {
     rdtsc
     mov dword ptr value,eax
     mov dword ptr [value + 4],edx
  } 

  return  value;
}

//
// Entry point function - ShowVersion
//
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

  UINT64  elsp;

  gST = SystemTable;
  gBS = SystemTable->BootServices;

  elsp=rdtsc();
  gBS -> Stall(1000000);  
  Print(L"CPU Frequency: %ld \n", rdtsc()-elsp);

  return EFI_SUCCESS;
}

 

程序运行的结果:

cpuz

虚拟机中运行的CPUZ读取的结果:

freqcalc

代码下载

FreqCalc

特别的,有资料指出在当前的多核环境下,这样的方法并不准确,具体的原因请参考下面的文章

1. http://blog.csdn.net/solstice/article/details/5196544 “多核时代不宜再用 x86 的 RDTSC 指令测试指令周期和时间”
2. http://blog.chinaunix.net/uid-24774106-id-2779245.html “使用rdtsc指令,测量程序的运行速度”

介绍几个 UEFI Shell下的命令 (下)

继续介绍 Shell 下面的命令。

ECHO 和DOS下的这个命令相同,这是用来回显“字符串的”,比如下面的例子

echo

MAP 这是一个“定义用户名和设备handle映射关系”的命令。最常见的用途就是给支持文件protocol的设备分配一个盘符,比如: fs0:

另外,最常见的用法是当你进入shell之后发现忘记插入U盘,插入之后U盘的盘符不会马上可以使用,这时候可以使用 map -r 一下,让他识别。

map1

SET 和DOS下的这个命令相同,设置环境变量。SPEC中提到,不加额外参数设定的变量是“易非失”的,这次启动在,断电再上电还是在(我试验过)。

如果使用 -v 可以设置一个“易失”的变量,断电一次就没了。前面有 “×” 表示这个变量是易失的。

set1

VOL 显示当前的硬盘/分区容量,如果你发现Windows无法正常安装不妨试试这个命令检查一下是否使用了错误的eMMC. (后面会查看一下这个命令的代码确定究竟查看的是硬盘容量还是分区容量)

vol

VER 显示当前使用的UEFI版本信息

ver

TIME 显示和设置当前的时间信息(不知道如何设置时区?)

time

TYPE 和DOS下的这个命令相同,显示文件的内容,支持普通的ASCII和UNICODE编码。

type

介绍几个 UEFI Shell下的命令 (上)

这里只介绍常用的,因为不常用的我也搞不明白。第一个需要了解的是:很多命令可以在后面加入 -b 来做分屏显示。这个参数在查看help的时候非常有用。

比如下面就是运行 help -b 的结果

x1

下面是简单介绍一些命令。

alias 可以用来给一些命令设置“别名”。比如:我们在Shell下可以用 dir 和ls一样列出当前目录就是因为设置 dir 为 ls的别名。

alias1

实验一下,我们设置 alias dog ls,那么输入 dog 也和ls一样

alias2

attribute 更改文件属性,和DOS下面的那个命令一样

直接运行可以列出文件的属性

attributea

支持通配符,比如下面给这几个文件加上只读属性之后就无法删除了

attributeb

cls 清屏幕,它后面可以跟着一个参数,清屏之后设置背景的颜色

color – New background color
0 – Black
1 – Blue
2 – Green
3 – Cyan
4 – Red
5 – Magenta
6 – Yellow
7 – Light gray

例如:

cls

cp 拷贝,和DOS下面著名的copy命令一样。它提供了一个参数 -r, 可以用来递归进入目录进行拷贝。简单的说就是你可以用这个参数来copy目录

cpr

Step to UEFI (8)—-显示版本号

UEFI 2.4 SPEC 第四章介绍 EFI System Table 如下

getvendora

看到其中 Vendor 和 Revision信息便着手写了一个小程序来验证,核心代码如下

EFI_SYSTEM_TABLE	*gST;

//
// Entry point function - ShowVersion
//
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

  gST = SystemTable;

  Print(L"Firmware Vendor  :  %s \n", gST->FirmwareVendor);
  Print(L"Firmware Revision:  %d \n", gST->FirmwareRevision);

  return EFI_SUCCESS;
}

编译运行之后得到如下结果:

ShowVersion

手边暂时没有其他EFI的主板所以没有进一步实验。

下载 ShowVersion

 

Step to UEFI (7)—-加速 UEFI 模拟环境的启动

前面的例子都是在 NT32 模拟环境中运行的,在启动这个环境的时候会有一个5秒的滚动条。为了更爽快一些,可以将这个滚动条设置为一闪而过,马上进入Shell环境。“以文找文”,搜索“Start showing progress bar”字样,确定下面的代码显示这个字符串:

\IntelFrameworkModulePkg\Universal\BdsDxe\FrontPage.c (Experiment)

/**

  Function show progress bar to wait for user input.

  @param TimeoutDefault  - The fault time out value before the system

                         continue to boot.

  @retval  EFI_SUCCESS       User pressed some key except "Enter"

  @retval  EFI_TIME_OUT      Timout expired or user press "Enter"

**/

EFI_STATUS

ShowProgress (

  IN UINT16                       TimeoutDefault

  )

{

  EFI_STATUS                    Status;

  CHAR16                        *TmpStr;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Foreground;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background;

  EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color;

  EFI_INPUT_KEY                 Key;

  UINT16                        TimeoutRemain;

  if (TimeoutDefault == 0) {

    return EFI_TIMEOUT;

  }

  DEBUG ((EFI_D_INFO, "\n\nStart showing progress bar... Press any key to stop it! .\n"));

  SetMem (&Foreground, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);

  SetMem (&Background, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);

  SetMem (&Color, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0xff);

  //

  // Clear the progress status bar first

  //

  TmpStr = GetStringById (STRING_TOKEN (STR_START_BOOT_OPTION));

  if (TmpStr != NULL) {

    PlatformBdsShowProgress (Foreground, Background, TmpStr, Color, 0, 0);

  }

 

 

追踪发现是下面的代码调用到上面的  \IntelFrameworkModulePkg\Universal\BdsDxe\FrontPage.c (Experiment)

/**

  This function is the main entry of the platform setup entry.

  The function will present the main menu of the system setup,

  this is the platform reference part and can be customize.

  @param TimeoutDefault     The fault time out value before the system

                            continue to boot.

  @param ConnectAllHappened The indicater to check if the connect all have

                            already happened.

**/

VOID

PlatformBdsEnterFrontPage (

  IN UINT16                       TimeoutDefault,

  IN BOOLEAN                      ConnectAllHappened

  )

…………………………

…………………………

  HotkeyBoot ();

  if (TimeoutDefault != 0xffff) {

    Status = ShowProgress (TimeoutDefault);

    StatusHotkey = HotkeyBoot ();

 

上面的代码又是下面调用到的

  //

  // Init the time out value

  //

  Timeout = PcdGet16 (PcdPlatformBootTimeOut);

 

 

最后确定参数是来自 PCD ,修改 \Nt32Pkg\Nt32Pkg.dsc

[PcdsDynamicHii.common.DEFAULT]

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdSetupConOutColumn|L”SetupConsoleConfig”|gEfiGlobalVariableGuid|0x0|80

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdSetupConOutRow|L”SetupConsoleConfig”|gEfiGlobalVariableGuid|0x4|25

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdPlatformBootTimeOut|L”Timeout”|gEfiGlobalVariableGuid|0x0|10

gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdHardwareErrorRecordLevel|L”HwErrRecSupport”|gEfiGlobalVariableGuid|0x0|1

将上面红色标记的修改为你希望的即可.

这里是一个修改为20秒的例子

Step2UEFI7

Step to UEFI Shell (6) —- Shell 中使用 Float

如果你在Shell下直接使用 Float 或者 Double 在编译的时候会遇到这样的问题

Architecture(s) = IA32
Build target = DEBUG
Toolchain = VS2008

Active Platform = c:\edk2\Nt32Pkg\Nt32Pkg.dsc
Flash Image Definition = c:\edk2\Nt32Pkg\Nt32Pkg.fdf

Processing meta-data … done!
“C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\link.exe” /out:”c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.exe” /base:0x10000000 /pdb:”c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.pdb” /LIBPATH:”C:\Program Files\Microsoft Visual Studio 9.0\VC\Lib” /LIBPATH:”C:\Program Files\Microsoft Visual Studio 9.0\VC\PlatformSdk\Lib” /NOLOGO /SUBSYSTEM:CONSOLE /NODEFAULTLIB /IGNORE:4086 /MAP /OPT:REF /DEBUG /MACHINE:I386 /LTCG Kernel32.lib MSVCRTD.lib Gdi32.lib User32.lib Winmm.lib Advapi32.lib /EXPORT:InitializeDriver=_ModuleEntryPoint /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE @c:\edk2\Build\NT32\DEBUG_VS2008\IA32\Nt32Pkg\Sec\SecMain\OUTPUT\static_library_files.lst
LINK : warning LNK4108: /ALIGN specified without /DRIVER; image may not run
LINK : warning LNK4001: no object files specified; libraries used
Creating library c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.lib and object c:\edk2\Build\NT32\DEBUG_VS2008\IA32\SecMain.exp
Generating code
Finished generating code
“C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\link.exe” /OUT:c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.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 /EXPORT:InitializeDriver=_ModuleEntryPoint /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE @c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\OUTPUT\static_library_files.lst
Creating library c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.lib and object c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.exp
Generating code
Finished generating code
c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.dll : warning LNK4086: entrypoint ‘__ModuleEntryPoint’ is not __stdcall with 12 bytes of arguments; image may not run
FloatTest.lib(floattest.obj) : error LNK2001: unresolved external symbol __fltused
c:\edk2\Build\NT32\DEBUG_VS2008\IA32\MdeModulePkg\Application\floattest\floattest\DEBUG\FloatTest.dll : fatal error LNK1120: 1 unresolved externals

我baidu和google之后得到的大概的结论是:UEFI 替换了Link中的默认库(CRT.LIB?),但是因为MS的编译器存在一个bug,所以导致仍然使用部分的默认库,所以导致这样的问题【这个解释存疑,不确定】。至于解决方法,经过我的实验确认就是使用 EADK 库【参考1】。我的理解是这个库是用来替代标准C库编写EDKII程序的,因此Float之类在其中也有重新定义。

加入方法:

1.解压EADK,然后将其中三个目录放到你EDK2的根目录下

2.修改 Nt32Pkg.dsc 加入红色部分

#
# Misc
#
DebugLib|IntelFrameworkModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
DebugPrintErrorLevelLib|MdeModulePkg/Library/DxeDebugPrintErrorLevelLib/DxeDebugPrintErrorLevelLib.inf
PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
#LABZ_Start
SortLib|ShellPkg/Library/UefiSortLib/UefiSortLib.inf
FileHandleLib|ShellPkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf
ShellCEntryLib|ShellPkg/Library/UefiShellCEntryLib/UefiShellCEntryLib.inf
LibC|StdLib/LibC/LibC.inf
LibStdLib|StdLib/LibC/StdLib/StdLib.inf
LibString|StdLib/LibC/String/String.inf
LibWchar|StdLib/LibC/Wchar/Wchar.inf
LibCType|StdLib/LibC/Ctype/Ctype.inf
LibTime|StdLib/LibC/Time/Time.inf
LibStdio|StdLib/LibC/Stdio/Stdio.inf
LibGdtoa|StdLib/LibC/gdtoa/gdtoa.inf
LibLocale|StdLib/LibC/Locale/Locale.inf
LibUefi|StdLib/LibC/Uefi/Uefi.inf
LibMath|StdLib/LibC/Math/Math.inf
LibSignal|StdLib/LibC/Signal/Signal.inf
LibNetUtil|StdLib/LibC/NetUtil/NetUtil.inf
#LABZ_End
[LibraryClasses.common.USER_DEFINED]
DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
ReportStatusCodeLib|MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
OemHookStatusCodeLib|Nt32Pkg/Library/PeiNt32OemHookStatusCodeLib/PeiNt32OemHookStatusCodeLib.inf
MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf

3. floattest.inf 中必须加入下面2个

[LibraryClasses]
UefiApplicationEntryPoint
UefiLib
PcdLib
ShellCEntryLib
ShellLib
LibMath
LibC|

下面就可以在你的代码中放心大胆的使用Float了。

一个计算 Pi 的Shell Application

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

//
// Based on code found at http://code.google.com/p/my-itoa/
//
int 
Integer2AsciiString(int val, char* buf)
{
    const unsigned int radix = 10;

    char* p = buf;
    unsigned int a; 
    int len;
    char* b;
    char temp;
    unsigned int u;

    if (val < 0) {
        *p++ = '-';
        val = 0 - val;
    }
    u = (unsigned int)val;
    b = p;

    do {
        a = u % radix;
        u /= radix;
        *p++ =(char) a + '0';
    } while (u > 0);

    len = (int)(p - buf);
    *p-- = 0;

    // swap 
    do {
       temp = *p; *p = *b; *b = temp;
       --p; ++b;
    } while (b < p);

    return len;
}

//
// Based on code found on the Internet (author unknown)
// Search for ftoa implementations
//
int 
Float2AsciiString(float f, char *buffer, int numdecimals)
{
    int status = 0;
    char *s = buffer;
    long mantissa, int_part, frac_part;
    short exp2;
    char m;

    typedef union {
        long L;
        float F;
    } LF_t;
    LF_t x;

    if (f == 0.0) {           // return 0.00
        *s++ = '0'; *s++ = '.'; *s++ = '0'; *s++ = '0'; 
        *s = 0;
       return status;
    }

    x.F = f;

    exp2 = (unsigned char)(x.L >> 23) - 127;
    mantissa = (x.L & 0xFFFFFF) | 0x800000;
    frac_part = 0;
    int_part = 0;

    if (exp2 >= 31 || exp2 < -23) {
        *s = 0;
        return 1;
    } 

    if (exp2 >= 0) {
        int_part = mantissa >> (23 - exp2);
        frac_part = (mantissa << (exp2 + 1)) & 0xFFFFFF;
    } else {
        frac_part = (mantissa & 0xFFFFFF) >> -(exp2 + 1);
    }

    if (int_part == 0)
       *s++ = '0';
    else {
        Integer2AsciiString(int_part, s);
        while (*s) s++;
    }
    *s++ = '.';

    if (frac_part == 0)
        *s++ = '0';
    else {
        for (m = 0; m < numdecimals; m++) {                       // print BCD
            frac_part = (frac_part << 3) + (frac_part << 1);      // frac_part *= 10
            *s++ = (frac_part >> 24) + '0';
            frac_part &= 0xFFFFFF;
        }
    }
    *s = 0;

    return status;
}

VOID
Ascii2UnicodeString(CHAR8 *String, CHAR16 *UniString)
{
    while (*String != '\0') {
        *(UniString++) = (CHAR16) *(String++);
    }
    *UniString = '\0';
}

//PI= 4- /3+4/5-4/7+4/9
EFI_STATUS 
EFIAPI
CalcPI()
{
    char str[8];
    static CHAR16 wstr[8];
    int i;

    float g1 = (float) 1;
    float g2 = (float) 0;

    for (i=0;i<100;i++)
     {
	if (i%2==0) {g2=g2+ 4/ g1;}
        else {g2=g2-4/ g1;}
	Float2AsciiString(g2, str, 4);         
	Ascii2UnicodeString(str, wstr);
	Print(L" PI: %s\n", wstr);
        g1=g1+2;
     }

    return EFI_SUCCESS;
}

EFI_STATUS 
EFIAPI
UefiMain(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
{

    CalcPI();

    return EFI_SUCCESS;
}

运行结果:

floattest

示例代码下载 floattest

参考:

1.http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=EDKII_EADK

EDK II Application Development Kit for include the Standard C Libraries in UEFI Shell Applications

 

重新编译EDK2工具的方法(C语言部分)

EDK2 在编译过程中会用到很多工具,比如编译处理Setup菜单的 VfrConmpiler.exe。部分工具是C编写的,部分是Python编写的。本文介绍如何重新编译Windows下面,C编写的这类工具。全部的工具存放在 BaseTools 目录下。Windows编译过程中用到的工具可以在 BaseTools\Bin\Win32下面找到。(实际上我只在Windows下编译过 EDK2)

编译的方法是:

1. 运行EDK2代码根目录下的 edksetup.bat
2. 进入BaseTools目录下运行 toolsetup.bat
3. 运行NMake即可全部重新编译

运行结果如下图,出现Error的原因是我们没有安装Python Freeze 这个工具(这个工具是用来将Python编写的程序封装成Windows EXE的工具)。

computl

全部编译时间上会比较长,我们可以单独编译。比如,我们修改 VolInfo 的Source Code,之后进入 \basetools\Source\c\Volinfo 目录下,使用 nmake 即可编译 (前面提到的1 2两步还是要做的)

www.lab-z.com
Zoologist
2013-8-2