Step to UEFI (14) —– EADK clock()

除了前面介绍的tm struct 和 time_t定义,标准C的库还有clock_t的定义。相比前面两者,这个是一个更加单纯的“计时”定义,前两者更像是日期时间的定义。

对于 clock_t ,在 EadkPkg_A2\StdLib\Include\time.h 有如下定义

/** An arithmetic type capable of representing values returned by clock(); **/
#ifdef _EFI_CLOCK_T
  typedef _EFI_CLOCK_T  clock_t;
  #undef _EFI_CLOCK_T
#endif

 

在 StdLib\Include\sys\EfiCdefs.h 有下面的定义

#define _EFI_CLOCK_T      UINT64

 

和 clock_t 配合的还有CLOCKS_PER_SEC ,定义了一秒中的Clock数

EadkPkg_A2\StdLib\Include\time.h

#define CLOCKS_PER_SEC  __getCPS()

 

这个函数的具体实现在 EadkPkg_A2\StdLib\LibC\Time\Time.c

clock_t
EFIAPI
__getCPS(void)
{
  return gMD->ClocksPerSecond;
}

 

函数 clock() 的作用是取得从程序开始运行的处理器时钟数。特别需要注意:在NT32模拟环境中和实际环境中,这个函数是不同的。之前提到过,如果想在NT32环境中正常使用C Library,必须在AppPkg.dsc中做一些修改

[LibraryClasses.IA32]
  #TimerLib|PerformancePkg/Library/DxeTscTimerLib/DxeTscTimerLib.inf
  ## Comment out the above line and un-comment the line below for running under Nt32 emulation.
  TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf

 

当然,修改之后,能在NT32环境中运行了,但是无法取得clock值。输出始终为 0。

clock1

为了验证,我尝试在实际环境下测试。首先是要将上面的设定恢复原装。恢复完之后无法通过编译,需要修改成下面的样子(下面ORG给出的是原来的路径)。

[LibraryClasses.IA32]
  #ORG TimerLib|PerformancePkg/Library/DxeTscTimerLib/DxeTscTimerLib.inf
  TimerLib|PerformancePkg/Library/TscTimerLib/DxeTscTimerLib.inf
  ## Comment out the above line and un-comment the line below for running under Nt32 emulation.
  #TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf

 

之后,我用WinISO做了一个 ISO镜像,将main.efi放在iso文件中,直接挂接到virtualbox的虚拟机中(4.3.10 r93012),发现运行之后无任何输出.

clock2

拷贝到实际机器上运行,看起来结果正常:

clock

这次实验使用的代码如下,依然是从 Demo 的Main.c中修改而来

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, 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.

    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  <Uefi.h>
//#include  <Library/UefiLib.h>
//#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <time.h>

/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{
  int i;
  printf("Sizeof Clock_T %d\n",sizeof(clock_t));

  printf("CLOCKS_PER_SEC %d\n",CLOCKS_PER_SEC);

  printf("Start %d\n",clock());

  for (i=1;i<0xFFFF; i++)  
   {
   }

  printf("End %d\n",clock());

  return EFI_SUCCESS;
}

 

依旧是使用 AppPkg 的环境进行编译。

代码下载

Main

编译的可以在实际机器上运行的EFI文件

Main_Actual

根据上面的 CLOCKS_PER_SEC 我们同样能做出来一个测算当前CPU频率的程序。同时我们也完全可以写出一个 delay 函数(我在C 标准库中查看了一下,惊奇的发现这个函数并非标准函数……)

为了总结关于时间的库函数,总结下面这个图表

time lib

参考:
1.http://ganquan.info/standard-c/function/clock

Step to UEFI (13) —– EADK struct tm

EADK 中的时间,还有一种定义(更准确的说是C标准库中的定义),可以在 \EadkPkg_A2\StdLib\Include\time.h 看到

/** A structure holding the components of a calendar time, called the
    broken-down time.  The first nine (9) members are as mandated by the
    C95 standard.  Additional fields have been added for EFI support.
**/
struct tm {
  int     tm_year;      // years since 1900
  int     tm_mon;       // months since January ?[0, 11]
  int     tm_mday;      // day of the month ?[1, 31]
  int     tm_hour;      // hours since midnight ?[0, 23]
  int     tm_min;       // minutes after the hour ?[0, 59]
  int     tm_sec;       // seconds after the minute ?[0, 60]
  int     tm_wday;      // days since Sunday ?[0, 6]
  int     tm_yday;      // days since January 1 ?[0, 365]
  int     tm_isdst;     // Daylight Saving Time flag
  int     tm_zoneoff;   // EFI TimeZone offset, -1440 to 1440 or 2047
  int     tm_daylight;  // EFI Daylight flags
  UINT32  tm_Nano;      // EFI Nanosecond value
};

 

有两个函数 localtime 和 mktime 可以将时间在 time_t 和 struct tm之间进行转换。

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, 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.

    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  <Uefi.h>
//#include  <Library/UefiLib.h>
//#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <time.h>

/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{

  time_t t,y;
  struct tm *tb;
  
  time(&t);

  printf("%ld\n",t);

  tb=localtime(&t);
  
  printf("localtime: %s",asctime(tb));

  y=mktime(tb); 
  
  printf("mktime   : %s",ctime(&y));
  
  return 0;
}

 

运行结果

time2

参考:

1.http://ganquan.info/standard-c/function/localtime
2.http://ganquan.info/standard-c/function/mktime

Step to UEFI (12) —- EADK 中的 Time 函数

这次的工作是基于 EADK 的,编译借用EADK的环境这样做有编译速度快,测试方便的优点,具体配置请参考 ”http://www.lab-z.com/how-to-use-eadk/“

关于 time_t 的定义可以在 \EadkPkg_A2\StdLib\Include\time.h 中看到。测试了一下,sizeof(time_t) == 4。

time()函数取得的结果是当前系统的时间,具体的定义是:返回从GMT1970年1月1日 0:0:0 开始经过的秒数表示的当前时间和日期。

下面可以看到 time() 有两种用法。

代码如下,很简单

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, 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.

    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  <Uefi.h>
//#include  <Library/UefiLib.h>
//#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <time.h>

/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{

  time_t t;

  time(&t);

  printf("%ld\n",t);

  t=time(NULL);

  printf("%s\n",ctime(&t));

  return 0;
}

 

运行结果

time

下载

main

参考:

1.http://ganquan.info/standard-c/function/time Standard C 语言标准函数库速查 (Cheat Sheet)

2.书籍 《C函数实用手册》 个人感觉这本书不错,蛮实用,实例详尽。缺点是缺少一些新的C函数,可能是因为这本书目标是TC,并且是2003年的书籍。当初是为了用 Turbo C 所以才买的.

Step to UEFI (11)—- 让程序中断运行的方法

前面介绍了 Pasue 一下的方法,这里再介绍一下让程序停止执行的方法。 和 DOS 不同,Shell中的程序通常都像吃过炫迈口香糖那样—-根本停不下来…..要想能够中止运行,可以通过 Shell Environment 2 特别设置一下。

下面的代码设置了 ESC 为Break 键,运行中可以使用它来终止运行,这里的代码和前面 PauseTest的长得几乎一样:

//
// PauseTest.c
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/BaseMemoryLib.h>

#define SHELL_INTERFACE_PROTOCOL \
  { \
    0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellInterfaceProtocol  = SHELL_INTERFACE_PROTOCOL;
EFI_GUID		SEGuid = EFI_SE_EXT_SIGNATURE_GUID;
//
// The shell environment is provided by a driver.  The shell links to the
// shell environment for services.  In addition, other drivers may connect
// to the shell environment and add new internal command handlers, or
// internal protocol handlers.
//
#define SHELL_ENVIRONMENT_INTERFACE_PROTOCOL \
  { \
    0x47c7b221, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellEnvProtocol = SHELL_ENVIRONMENT_INTERFACE_PROTOCOL;

#define EFI_OUTPUT_PAUSE    0x00000002

typedef struct {
  SHELLENV_EXECUTE          Execute;  // Execute a command line
  SHELLENV_GET_ENV          GetEnv;   // Get an environment variable
  SHELLENV_GET_MAP          GetMap;   // Get mapping tables
  SHELLENV_ADD_CMD          AddCmd;   // Add an internal command handler
  SHELLENV_ADD_PROT         AddProt;  // Add protocol info handler
  SHELLENV_GET_PROT         GetProt;  // Get the protocol ID
  SHELLENV_CUR_DIR          CurDir;
  SHELLENV_FILE_META_ARG    FileMetaArg;
  SHELLENV_FREE_FILE_LIST   FreeFileList;

  //
  // The following services are only used by the shell itself
  //
  SHELLENV_NEW_SHELL        NewShell;
  SHELLENV_BATCH_IS_ACTIVE  BatchIsActive;

  SHELLENV_FREE_RESOURCES   FreeResources;
} EFI_SHELL_ENVIRONMENT;

EFI_SHELL_INTERFACE             *SI;
EFI_SHELL_ENVIRONMENT           *SE;
EFI_SHELL_ENVIRONMENT2          *SE2;

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

//Copy from \Shell\Library\Misc.c
BOOLEAN
GrowBuffer (
  IN OUT EFI_STATUS   *Status,
  IN OUT VOID         **Buffer,
  IN UINTN            BufferSize
  )
/*++

Routine Description:

  Helper function called as part of the code needed
  to allocate the proper sized buffer for various 
  EFI interfaces.

Arguments:

  Status      - Current status

  Buffer      - Current allocated buffer, or NULL

  BufferSize  - Current buffer size needed
    
Returns:
    
  TRUE - if the buffer was reallocated and the caller 
  should try the API again.

--*/
{
  BOOLEAN TryAgain;

  //
  // If this is an initial request, buffer will be null with a new buffer size
  //
  if (NULL == *Buffer && BufferSize) {
    *Status = EFI_BUFFER_TOO_SMALL;
  }
  //
  // If the status code is "buffer too small", resize the buffer
  //
  TryAgain = FALSE;
  if (*Status == EFI_BUFFER_TOO_SMALL) {

    if (*Buffer) {
      FreePool (*Buffer);
    }

    *Buffer = AllocateZeroPool (BufferSize);

    if (*Buffer) {
      TryAgain = TRUE;
    } else {
      *Status = EFI_OUT_OF_RESOURCES;
    }
  }
  //
  // If there's an error, free the buffer
  //
  if (!TryAgain && EFI_ERROR (*Status) && *Buffer) {
    FreePool (*Buffer);
    *Buffer = NULL;
  }

  return TryAgain;
}

//Copy from \Shell\Library\Handle.c
EFI_STATUS
LocateHandle (
  IN EFI_LOCATE_SEARCH_TYPE       SearchType,
  IN EFI_GUID                     * Protocol OPTIONAL,
  IN VOID                         *SearchKey OPTIONAL,
  IN OUT UINTN                    *NoHandles,
  OUT EFI_HANDLE                  **Buffer
  )
/*++

Routine Description:

  Function returns an array of handles that support the requested protocol 
  in a buffer allocated from pool.

Arguments:

  SearchType           - Specifies which handle(s) are to be returned.
  Protocol             - Provides the protocol to search by.   
                         This parameter is only valid for SearchType ByProtocol.
  SearchKey            - Supplies the search key depending on the SearchType.
  NoHandles            - The number of handles returned in Buffer.
  Buffer               - A pointer to the buffer to return the requested array of 
                         handles that support Protocol.

Returns:
  
  EFI_SUCCESS           - The result array of handles was returned.
  EFI_NOT_FOUND         - No handles match the search. 
  EFI_OUT_OF_RESOURCES - There is not enough pool memory to store the matching results.

--*/
{
  EFI_STATUS  Status;
  UINTN       BufferSize;

  //
  // Initialize for GrowBuffer loop
  //
  Status      = EFI_SUCCESS;
  *Buffer     = NULL;
  BufferSize  = 50 * sizeof (EFI_HANDLE);

  //
  // Call the real function
  //
  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {
    Status = gBS->LocateHandle (
                  SearchType,
                  Protocol,
                  SearchKey,
                  &BufferSize,
                  *Buffer
                  );
  }

  *NoHandles = BufferSize / sizeof (EFI_HANDLE);
  if (EFI_ERROR (Status)) {
    *NoHandles = 0;
  }

  return Status;
}

INTN
CompareGuidx (
  IN EFI_GUID     *Guid1,
  IN EFI_GUID     *Guid2
  )
/*++

Routine Description:

  Compares to GUIDs

Arguments:

  Guid1 - guid to compare
  Guid2 - guid to compare

Returns:
  =  0  if Guid1 == Guid2
  != 0  if Guid1 != Guid2 

--*/
{
  INT32 *g1;
  INT32 *g2;
  INT32 r;

  //
  // Compare 32 bits at a time
  //
  g1  = (INT32 *) Guid1;
  g2  = (INT32 *) Guid2;

  r   = g1[0] - g2[0];
  r |= g1[1] - g2[1];
  r |= g1[2] - g2[2];
  r |= g1[3] - g2[3];

  return r;
}
// Copy from \Shell\Library\Init.c
EFI_STATUS
LibInitializeShellApplication (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  *HandleBuffer;
  UINTN       HandleNum;
  UINTN       HandleIndex;
  EFI_GUID         SESGuid         = EFI_SE_EXT_SIGNATURE_GUID;
  
  //
  // Connect to the shell interface
  //
  Status = gBS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID *) &SI);
  if (EFI_ERROR (Status)) {
    Print (L"InitShellApp: Application not started from Shell\n");
    gBS->Exit (ImageHandle, Status, 0, NULL);
  }

  //
  // Connect to the shell environment
  //
  Status = gBS->HandleProtocol (
                ImageHandle,
                &ShellEnvProtocol,
                (VOID *) &SE2
                );
  if (EFI_ERROR (Status) || !(CompareGuid (&SE2->SESGuid, &SESGuid) == 0 &&
    (SE2->MajorVersion > EFI_SHELL_MAJOR_VER ||
      (SE2->MajorVersion == EFI_SHELL_MAJOR_VER && SE2->MinorVersion >= 
EFI_SHELL_MINOR_VER))
    )
  ) {
    Status = LocateHandle (
              ByProtocol,
              &ShellEnvProtocol,
              NULL,
              &HandleNum,
              &HandleBuffer
              );
    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 1Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, 0, NULL);
    }

    Status = EFI_NOT_FOUND;
    for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
      gBS->HandleProtocol (
           HandleBuffer[HandleIndex],
           &ShellEnvProtocol,
           (VOID *) &SE2
           );
      if (CompareGuidx (&SE2->SESGuid, &SESGuid) == 0)
	  {
        Status = EFI_SUCCESS;
        break;
      }
    }

    FreePool (HandleBuffer);

    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 2Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, Status, NULL);
    }
  }

  SE = (EFI_SHELL_ENVIRONMENT *) SE2;
  
  //
  // Done with init
  //
  return Status;
}

//
// Entry point function 
//
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   INTN	i;
   BOOLEAN ExitMark=FALSE;
   
   gST = SystemTable;
   gBS = SystemTable -> BootServices;
   
   LibInitializeShellApplication (ImageHandle,SystemTable);
   
   while (!ExitMark) 
    {
		//Clear Screen
		gST -> ConOut -> ClearScreen(gST->ConOut);
   	    Print(L"You can break by ESC key\n");
		for (i=0;i<80*(25-1);i++)
		{
			if (SE2 -> GetExecutionBreak() ) {ExitMark=TRUE; break;}
			Print(L".");
		} 
	}
	 
  return EFI_SUCCESS;
}

 

运行结果:

BreakTest

代码:

BreakTest

How to use EADK

If you want to use C library in your program such as: printf, scanf……. You should use EADK library in EDK2.

1.Decompress EADK_A2_Release. There are 3 directories. Put them to your EDK2 source directory. In fact you should make sure your EDK2 can build and run NT32 well.

Capture

2.Run edksetup.bat in your EDK directory.

edksetup

3.You can build EADK_A2_Release by using

build -a IA32 -p AppPkg\AppPkg.dsc

3.You well get the first error message.(AppPkg.dsc error 000E)

error1

4.You should modify AppPkg.dsc as below as we just want to test program under NT32:

[LibraryClasses.IA32]
# TimerLib|PerformancePkg/Library/DxeTscTimerLib/DxeTscTimerLib.inf
## Comment out the above line and un-comment the line below for running under Nt32 emulation.
TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf

5.Build again and your will get the second error message (time.h Error C2220)

error2

6.The solution is simple. Use the Notepad to open the time.h and save again.

7.At last all the demos can be build. You can get main.efi hello.efi and Enquire.efi. They can be run in NT32 emulation environment.

They will be placed at “\Build\AppPkg\DEBUG_VS2008\IA32” after compiling. And you can copy them to “\Build\NT32\DEBUG_VS2008\IA32”. After that they can be seen under fsnt0:

result

8.You can use printf in your program now. Ex.

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

puts(“Hello there fellow Programmer.”);
puts(“Welcome to the world of EDK II.”);

printf(“lab-z.com %x\n”,8212);

return 0;
}

result2

<2016> I find it seems to be hard to download an EADK package from internet, and I put one here EadkPkg_A2
reference:
1.http://www.lab-z.com/step-to-uefi-shell-6-shell-%E4%B8%AD%E4%BD%BF%E7%94%A8-float/

2.http://biosren.com/viewthread.php?tid=5651&fromuid=6339]http://biosren.com/viewthread.php?tid=5651&fromuid=6339 .UEFI 实战(1) 配置开发环境

UDK2014 发布了

今天刚刚注意到 UDK2014 发布了,可以在下面的网址看到

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

udk2014

从 Release Note 来看,和UDK2010相比变化不大,有如下变化

1. Support Unified Extensible Firmware(UEFI) specification 2.4.
2. Support Platform Initialization(PI) specification 1.3.
3. Support OpenSSL version 0.98w and whole X509v3 extension check.
4. Support TPM 2.0.
5. Support I2C. //这个主要是平板用的
6. Support NVM Express.
7. Update ACPI5.0 tables, PCI definitions and Atapi definitions.
8. Update BaseTools Python version to 2.7.3.

用前一段写的计算频率的程序测试了一下,能够正常编译和运行。

Step to UEFI (10) —- 让程序 Pause 一下的方法

很多时候我们编写的一些工具需要支持暂停的功能,比如:ls 列出的文件名时最好能够响应用户的按键,暂停一下以便用户查看结果。查看了一下Shell方面的代码,可以通过 Shell Environment 2 提供的函数来实现。

当然,我不愿意使用庞大的 Shell Library,选择性的提取一些代码就OK

//
// PauseTest.c
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/BaseMemoryLib.h>

#define SHELL_INTERFACE_PROTOCOL \
  { \
    0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellInterfaceProtocol  = SHELL_INTERFACE_PROTOCOL;
EFI_GUID		SEGuid = EFI_SE_EXT_SIGNATURE_GUID;
//
// The shell environment is provided by a driver.  The shell links to the
// shell environment for services.  In addition, other drivers may connect
// to the shell environment and add new internal command handlers, or
// internal protocol handlers.
//
#define SHELL_ENVIRONMENT_INTERFACE_PROTOCOL \
  { \
    0x47c7b221, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellEnvProtocol = SHELL_ENVIRONMENT_INTERFACE_PROTOCOL;

#define EFI_OUTPUT_PAUSE    0x00000002

typedef struct {
  SHELLENV_EXECUTE          Execute;  // Execute a command line
  SHELLENV_GET_ENV          GetEnv;   // Get an environment variable
  SHELLENV_GET_MAP          GetMap;   // Get mapping tables
  SHELLENV_ADD_CMD          AddCmd;   // Add an internal command handler
  SHELLENV_ADD_PROT         AddProt;  // Add protocol info handler
  SHELLENV_GET_PROT         GetProt;  // Get the protocol ID
  SHELLENV_CUR_DIR          CurDir;
  SHELLENV_FILE_META_ARG    FileMetaArg;
  SHELLENV_FREE_FILE_LIST   FreeFileList;

  //
  // The following services are only used by the shell itself
  //
  SHELLENV_NEW_SHELL        NewShell;
  SHELLENV_BATCH_IS_ACTIVE  BatchIsActive;

  SHELLENV_FREE_RESOURCES   FreeResources;
} EFI_SHELL_ENVIRONMENT;

EFI_SHELL_INTERFACE             *SI;
EFI_SHELL_ENVIRONMENT           *SE;
EFI_SHELL_ENVIRONMENT2          *SE2;

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

//Copy from \Shell\Library\Misc.c
BOOLEAN
GrowBuffer (
  IN OUT EFI_STATUS   *Status,
  IN OUT VOID         **Buffer,
  IN UINTN            BufferSize
  )
/*++

Routine Description:

  Helper function called as part of the code needed
  to allocate the proper sized buffer for various 
  EFI interfaces.

Arguments:

  Status      - Current status

  Buffer      - Current allocated buffer, or NULL

  BufferSize  - Current buffer size needed
    
Returns:
    
  TRUE - if the buffer was reallocated and the caller 
  should try the API again.

--*/
{
  BOOLEAN TryAgain;

  //
  // If this is an initial request, buffer will be null with a new buffer size
  //
  if (NULL == *Buffer && BufferSize) {
    *Status = EFI_BUFFER_TOO_SMALL;
  }
  //
  // If the status code is "buffer too small", resize the buffer
  //
  TryAgain = FALSE;
  if (*Status == EFI_BUFFER_TOO_SMALL) {

    if (*Buffer) {
      FreePool (*Buffer);
    }

    *Buffer = AllocateZeroPool (BufferSize);

    if (*Buffer) {
      TryAgain = TRUE;
    } else {
      *Status = EFI_OUT_OF_RESOURCES;
    }
  }
  //
  // If there's an error, free the buffer
  //
  if (!TryAgain && EFI_ERROR (*Status) && *Buffer) {
    FreePool (*Buffer);
    *Buffer = NULL;
  }

  return TryAgain;
}

//Copy from \Shell\Library\Handle.c
EFI_STATUS
LocateHandle (
  IN EFI_LOCATE_SEARCH_TYPE       SearchType,
  IN EFI_GUID                     * Protocol OPTIONAL,
  IN VOID                         *SearchKey OPTIONAL,
  IN OUT UINTN                    *NoHandles,
  OUT EFI_HANDLE                  **Buffer
  )
/*++

Routine Description:

  Function returns an array of handles that support the requested protocol 
  in a buffer allocated from pool.

Arguments:

  SearchType           - Specifies which handle(s) are to be returned.
  Protocol             - Provides the protocol to search by.   
                         This parameter is only valid for SearchType ByProtocol.
  SearchKey            - Supplies the search key depending on the SearchType.
  NoHandles            - The number of handles returned in Buffer.
  Buffer               - A pointer to the buffer to return the requested array of 
                         handles that support Protocol.

Returns:
  
  EFI_SUCCESS           - The result array of handles was returned.
  EFI_NOT_FOUND         - No handles match the search. 
  EFI_OUT_OF_RESOURCES - There is not enough pool memory to store the matching results.

--*/
{
  EFI_STATUS  Status;
  UINTN       BufferSize;

  //
  // Initialize for GrowBuffer loop
  //
  Status      = EFI_SUCCESS;
  *Buffer     = NULL;
  BufferSize  = 50 * sizeof (EFI_HANDLE);

  //
  // Call the real function
  //
  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {
    Status = gBS->LocateHandle (
                  SearchType,
                  Protocol,
                  SearchKey,
                  &BufferSize,
                  *Buffer
                  );
  }

  *NoHandles = BufferSize / sizeof (EFI_HANDLE);
  if (EFI_ERROR (Status)) {
    *NoHandles = 0;
  }

  return Status;
}

INTN
CompareGuidx (
  IN EFI_GUID     *Guid1,
  IN EFI_GUID     *Guid2
  )
/*++

Routine Description:

  Compares to GUIDs

Arguments:

  Guid1 - guid to compare
  Guid2 - guid to compare

Returns:
  =  0  if Guid1 == Guid2
  != 0  if Guid1 != Guid2 

--*/
{
  INT32 *g1;
  INT32 *g2;
  INT32 r;

  //
  // Compare 32 bits at a time
  //
  g1  = (INT32 *) Guid1;
  g2  = (INT32 *) Guid2;

  r   = g1[0] - g2[0];
  r |= g1[1] - g2[1];
  r |= g1[2] - g2[2];
  r |= g1[3] - g2[3];

  return r;
}
// Copy from \Shell\Library\Init.c
EFI_STATUS
LibInitializeShellApplication (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  *HandleBuffer;
  UINTN       HandleNum;
  UINTN       HandleIndex;
  EFI_GUID         SESGuid         = EFI_SE_EXT_SIGNATURE_GUID;
  
  //
  // Connect to the shell interface
  //
  Status = gBS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID *) &SI);
  if (EFI_ERROR (Status)) {
    Print (L"InitShellApp: Application not started from Shell\n");
    gBS->Exit (ImageHandle, Status, 0, NULL);
  }

  //
  // Connect to the shell environment
  //
  Status = gBS->HandleProtocol (
                ImageHandle,
                &ShellEnvProtocol,
                (VOID *) &SE2
                );
  if (EFI_ERROR (Status) || !(CompareGuid (&SE2->SESGuid, &SESGuid) == 0 &&
    (SE2->MajorVersion > EFI_SHELL_MAJOR_VER ||
      (SE2->MajorVersion == EFI_SHELL_MAJOR_VER && SE2->MinorVersion >= 
EFI_SHELL_MINOR_VER))
    )
  ) {
    Status = LocateHandle (
              ByProtocol,
              &ShellEnvProtocol,
              NULL,
              &HandleNum,
              &HandleBuffer
              );
    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 1Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, 0, NULL);
    }

    Status = EFI_NOT_FOUND;
    for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
      gBS->HandleProtocol (
           HandleBuffer[HandleIndex],
           &ShellEnvProtocol,
           (VOID *) &SE2
           );
      if (CompareGuidx (&SE2->SESGuid, &SESGuid) == 0)
	  {
        Status = EFI_SUCCESS;
        break;
      }
    }

    FreePool (HandleBuffer);

    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 2Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, Status, NULL);
    }
  }

  SE = (EFI_SHELL_ENVIRONMENT *) SE2;
  
  //
  // Done with init
  //
  return Status;
}

//
// Entry point function 
//
EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   INTN	i;

  Print(L"You can't Pause by Tab key\n");
  for (i=0;i<1000;i++)
    {
	Print(L".");
    }
  Print(L".\n");
  gBS = SystemTable -> BootServices;

  LibInitializeShellApplication (ImageHandle,SystemTable);
  SE2->SetKeyFilter(SE2->GetKeyFilter() | EFI_OUTPUT_PAUSE);
  
  Print(L"You can Pause by Tab key");
  for (i=0;i<1000;i++)
    {
  	Print(L".");
    }

  return EFI_SUCCESS;
}

 

上面的代码演示了使用 Pause Break键来暂停输出的功能。

PauseTest

代码在这里下载:

PauseTest

关于前面提到的 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

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