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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注