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

 

Step to UEFI Shell (5)—-获得Shell下内存分配状况

在Shell下面可以使用 memmap 命令查看到当前的内存状况:

step51

UEFI下面没有了E820 Table。取而代之的是GetMemoryMap()。UEFI spec Version 2.4 中 P143有如下的描述,实际上是一个长得和E820很像的东西。

step52

在网上可以搜索到一个示例[参考1],原文是用Toolkit 写成的,经过一些改造成为可以直接在EDK2下面的编译的代码。

GetMap.c:

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

#define PAGE_SIZE 4096 

const CHAR16 *memory_types[] = { 
    L"EfiReservedMemoryType", 
    L"EfiLoaderCode", 
    L"EfiLoaderData", 
    L"EfiBootServicesCode", 
    L"EfiBootServicesData", 
    L"EfiRuntimeServicesCode", 
    L"EfiRuntimeServicesData", 
    L"EfiConventionalMemory", 
    L"EfiUnusableMemory", 
    L"EfiACPIReclaimMemory", 
    L"EfiACPIMemoryNVS", 
    L"EfiMemoryMappedIO", 
    L"EfiMemoryMappedIOPortSpace", 
    L"EfiPalCode", 
    L"EfiMaxMemoryType"
}; 

EFI_BOOT_SERVICES *gBS;  
const CHAR16 * 
memory_type_to_str(UINT32 type) 
{ 
    if (type > sizeof(memory_types)/sizeof(CHAR16 *)) 
        return L"Unknown"; 

    return memory_types[type]; 
} 

EFI_STATUS 
memory_map(EFI_MEMORY_DESCRIPTOR **map_buf, UINTN *map_size, 
           UINTN *map_key, UINTN *desc_size, UINT32 *desc_version) 
{ 
    EFI_STATUS err = EFI_SUCCESS; 

    *map_size = sizeof(**map_buf) * 31; 

get_map: 
    *map_size += sizeof(**map_buf); 

    err = gBS->AllocatePool(EfiLoaderData, *map_size, (void **)map_buf); 
    if (err != EFI_SUCCESS) { 
        Print(L"ERROR: Failed to allocate pool for memory map"); 
        return err; 
    } 

    err = gBS->GetMemoryMap(map_size, *map_buf, map_key, desc_size, desc_version); 
    if (err != EFI_SUCCESS) { 
        if (err == EFI_BUFFER_TOO_SMALL) { 
            gBS->FreePool((void *)*map_buf); 
            goto get_map; 
        } 
        Print(L"ERROR: Failed to get memory map"); 
    } 
    return err; 
} 

GetMap.inf

[Defines]
  INF_VERSION                    = 0x00010005
  BASE_NAME                      = GetMap
  FILE_GUID                      = 6987936E-ED34-44db-AE97-1FA5E4ED2318
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

#
# The following information is for reference only and not required by the build tools.
#
#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
#

[Sources]
  GetMap.c

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

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  PcdLib
  ShellCEntryLib
  ShellLib

[FeaturePcd]
  gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable

[Pcd]
  gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintString || gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable  ## Valid when gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable
  gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintTimes  || gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable  ## Valid when gEfiMdeModulePkgTokenSpaceGuid.PcdHelloWorldPrintEnable

EFI_STATUS  
print_memory_map(void) 
{ 
    EFI_MEMORY_DESCRIPTOR *buf; 
    UINTN desc_size; 
    UINT32 desc_version; 
    UINTN size, map_key, mapping_size; 
    EFI_MEMORY_DESCRIPTOR *desc; 
    EFI_STATUS err = EFI_SUCCESS; 
    int i = 0; 

    err = memory_map(&buf, &size, &map_key, &desc_size, &desc_version); 
    if (err != EFI_SUCCESS) 
        return err; 

    Print(L"Memory Map Size: %d\n", size); 
    Print(L"Map Key: %d\n", map_key); 
    Print(L"Descriptor Version: %d\n", desc_version); 
    Print(L"Descriptor Size: %d\n\n", desc_size); 
    Print(L"Descriptor Size: %d\n\n", sizeof(EFI_MEMORY_DESCRIPTOR)); 

    desc = buf; 
    while ((UINT8 *)desc <  (UINT8 *)buf + size) { 
        mapping_size =(UINTN) desc->NumberOfPages * PAGE_SIZE; 

        Print(L"[#%02d] Type: %s  Attr: 0x%x\n", i, memory_type_to_str(desc->Type), desc->Attribute); 
        Print(L"      Phys: %016llx-%016llx\n", desc->PhysicalStart, desc->PhysicalStart + mapping_size -1 ); 

        desc = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)desc + desc_size); 
        i++; 
    } 

    gBS->FreePool (buf); 
    return err; 
} 

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

    print_memory_map(); 

    return EFI_SUCCESS; 
}

 

运行结果如下:

step53

我们使用 GetMap >> result.txt 得到完整的输出结果如下:

Memory Map Size: 960
Map Key: 2841
Descriptor Version: 1
Descriptor Size: 48

Descriptor Size: 40

[#00] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000000CB0000-0000000000CCFFFF
[#01] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000000CD0000-000000000383FFFF
[#02] Type: EfiLoaderData Attr: 0xF
Phys: 0000000003840000-0000000003841FFF
[#03] Type: EfiLoaderCode Attr: 0xF
Phys: 0000000003842000-0000000003848FFF
[#04] Type: EfiLoaderData Attr: 0xF
Phys: 0000000003849000-000000000385EFFF
[#05] Type: EfiLoaderCode Attr: 0xF
Phys: 000000000385F000-00000000038ABFFF
[#06] Type: EfiConventionalMemory Attr: 0xF
Phys: 00000000038AC000-0000000003B09FFF
[#07] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000003B0A000-0000000003B56FFF
[#08] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000003B57000-0000000003B74FFF
[#09] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000003B75000-0000000003B75FFF
[#10] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000003B76000-0000000003B76FFF
[#11] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000003B77000-00000000048ABFFF
[#12] Type: EfiConventionalMemory Attr: 0xF
Phys: 00000000048AC000-0000000004990FFF
[#13] Type: EfiBootServicesCode Attr: 0xF
Phys: 0000000004991000-0000000004BABFFF
[#14] Type: EfiRuntimeServicesData Attr: 0xF
Phys: 0000000004BAC000-0000000004BEBFFF
[#15] Type: EfiRuntimeServicesCode Attr: 0xF
Phys: 0000000004BEC000-0000000004C2BFFF
[#16] Type: EfiReservedMemoryType Attr: 0xF
Phys: 0000000004C2C000-0000000004C2FFFF
[#17] Type: EfiBootServicesData Attr: 0xF
Phys: 0000000004C30000-0000000004CAFFFF
[#18] Type: EfiConventionalMemory Attr: 0xF
Phys: 0000000004CB0000-0000000008CAFFFF
[#19] Type: EfiMemoryMappedIO Attr: 0x0
Phys: 0000000000BE0000-0000000000BEBFFF

和直接使用memmap取得的结果相同。需要注意的是:

1. 返回的Descriptor Size 并不是 sizeof(EFI_MEMORY_DESCRIPTOR)
2. 不知道EFI_VIRTUAL_ADDRESS VirtualStart; 是干什么用的

GetMap

参考:
1. http://blog.fpmurphy.com/2012/08/uefi-memory-v-e820-memory.html
可以在附件中找到该网页。它使用的是EFI_Toolkit 我没有编译过

Step to UEFI Shell(4)—-清屏功能

下面的代码展示了在Shell下如何编写一个清屏功能

EFI_SYSTEM_TABLE	*gST;

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

  gST = SystemTable;

  gST->ConOut->ClearScreen(gST->ConOut);
  gST->ConOut->EnableCursor(gST->ConOut,TRUE);

  return EFI_SUCCESS;
}

 

运行之后,会清屏并将光标至于屏幕左上角。

完整的代码 HelloWorld3