目标:写一个小程序来枚举当前系统中的盘符。比如:FS0: FS1: 等等。

和这个需求最相近的参考文件是 map 功能,每次启动shell的时候他都会展示一下当前系统中的全部盘符。这个功能的代码在 ShellPkg\Library\UefiShellLevel2CommandsLib\Map.c 。大概研究了一下,实现的方法是分别枚举有 Simple File Protocol 和 Block IO Protocol 的Handle (在 PerformMappingDisplay 函数中),然后取每的 Device Path Protocol(在 PerformSingleMappingDisplay 函数中),最后从这个Protocol中获取对应的盘符。

map.c中使用 gEfiShellProtocol->GetMapFromDevicePath 功能取得名称,但是在实际测试过程中我的程序中取得到的 gEfiShellProtocol 不知为何一直为0. 最后只得使用 mEfiShellEnvironment2 -> GetFsName 来完实现这个功能。

ShellPkg\Include\Protocol\EfiShellEnvironment2.h 有 EFI_SHELL_ENVIRONMENT2的定义

/// EFI_SHELL_ENVIRONMENT2 protocol structure.
typedef struct {
  SHELLENV_EXECUTE                        Execute;
  SHELLENV_GET_ENV                        GetEnv;
  SHELLENV_GET_MAP                        GetMap;
  SHELLENV_ADD_CMD                        AddCmd;
  SHELLENV_ADD_PROT                       AddProt;
  SHELLENV_GET_PROT                       GetProt;
                          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;

  //
  // GUID to differentiate ShellEnvironment2 from ShellEnvironment.
  //
  EFI_GUID                                SESGuid;
  //
  // Major Version grows if shell environment interface has been changes.
  //
  UINT32                                  MajorVersion;
  UINT32                                  MinorVersion;
  SHELLENV_ENABLE_PAGE_BREAK              EnablePageBreak;
  SHELLENV_DISABLE_PAGE_BREAK             DisablePageBreak;
  SHELLENV_GET_PAGE_BREAK                 GetPageBreak;

  SHELLENV_SET_KEY_FILTER                 SetKeyFilter;
  SHELLENV_GET_KEY_FILTER                 GetKeyFilter;

  SHELLENV_GET_EXECUTION_BREAK            GetExecutionBreak;
  SHELLENV_INCREMENT_SHELL_NESTING_LEVEL  IncrementShellNestingLevel;
  SHELLENV_DECREMENT_SHELL_NESTING_LEVEL  DecrementShellNestingLevel;
  SHELLENV_IS_ROOT_SHELL                  IsRootShell;

  SHELLENV_CLOSE_CONSOLE_PROXY            CloseConsoleProxy;
  HANDLE_ENUMERATOR                       HandleEnumerator;
  PROTOCOL_INFO_ENUMERATOR                ProtocolInfoEnumerator;
  GET_DEVICE_NAME                         GetDeviceName;
  GET_SHELL_MODE                          GetShellMode;
  SHELLENV_NAME_TO_PATH                   NameToPath;
  SHELLENV_GET_FS_NAME                    GetFsName;
  SHELLENV_FILE_META_ARG_NO_WILDCARD      FileMetaArgNoWildCard;
  SHELLENV_DEL_DUP_FILE                   DelDupFileArg;
  SHELLENV_GET_FS_DEVICE_PATH             GetFsDevicePath;
} EFI_SHELL_ENVIRONMENT2;

 

同一个文件中

/**
  Converts a device path into a file system map name.

  If DevPath is NULL, then ASSERT.

  This function looks through the shell environment map for a map whose device
  path matches the DevPath parameter.  If one is found the Name is returned via
  Name parameter.  If sucessful the caller must free the memory allocated for
  Name.

  This function will use the internal lock to prevent changes to the map during
  the lookup operation.

  @param[in] DevPath                The device path to search for a name for.
  @param[in] ConsistMapping         What state to verify map flag VAR_ID_CONSIST.
  @param[out] Name                  On sucessful return the name of that device path.

  @retval EFI_SUCCESS           The DevPath was found and the name returned
                                in Name.
  @retval EFI_OUT_OF_RESOURCES  A required memory allocation failed.
  @retval EFI_UNSUPPORTED       The DevPath was not found in the map.
**/
typedef
EFI_STATUS
(EFIAPI *SHELLENV_GET_FS_NAME) (
  IN EFI_DEVICE_PATH_PROTOCOL     * DevPath,
  IN BOOLEAN                      ConsistMapping,
  OUT CHAR16                      **Name
  );

 

根据上面的函数,编写程序如下

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

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

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

EFI_STATUS
EFIAPI
PerformSingleMappingDisplay(
  IN CONST EFI_HANDLE Handle
  )
{
  EFI_DEVICE_PATH_PROTOCOL  *DevPath;
  EFI_DEVICE_PATH_PROTOCOL  *DevPathCopy;
  CHAR16                    *CurrentName;

  CurrentName = NULL;
  DevPath = DevicePathFromHandle(Handle);
  DevPathCopy = DevPath;
  mEfiShellEnvironment2->GetFsName(DevPathCopy,FALSE,&CurrentName);

  Print (L"%s \r\n", CurrentName);  
	
  if ((CurrentName) != NULL) { FreePool((CurrentName)); CurrentName = NULL; }

  return EFI_SUCCESS;
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
  EFI_STATUS                Status;
  EFI_HANDLE                *HandleBuffer=NULL;
  UINTN                     BufferSize=0;
  UINTN                     LoopVar;
  BOOLEAN                   Found;
  //Copy from ShellLibConstructorWorker in \ShellPkg\Library\UefiShellLib\UefiShellLib.c
  //
  // UEFI 2.0 shell interfaces (used preferentially)
  //
  Status = gBS->OpenProtocol(
    gImageHandle,
    &gEfiShellProtocolGuid,
    (VOID **)&gEfiShellProtocol,
    gImageHandle,
    NULL,
    EFI_OPEN_PROTOCOL_GET_PROTOCOL
   );
   
  if (EFI_ERROR(Status)) {
    //
    // Search for the shell protocol
    //
    Status = gBS->LocateProtocol(
      &gEfiShellProtocolGuid,
      NULL,
      (VOID **)&gEfiShellProtocol
     );
    if (EFI_ERROR(Status)) {
      gEfiShellProtocol = NULL;
     }
  }
  
  //
  // Look up all SimpleFileSystems in the platform
  //
  Status = gBS->LocateHandle(
    ByProtocol,
    &gEfiSimpleFileSystemProtocolGuid,
    NULL,
    &BufferSize,
    HandleBuffer);
	
  if (Status == EFI_BUFFER_TOO_SMALL) {
		HandleBuffer = AllocateZeroPool(BufferSize);
		if (HandleBuffer == NULL) {
			return (SHELL_OUT_OF_RESOURCES);
		}
		Status = gBS->LocateHandle(
			ByProtocol,
			&gEfiSimpleFileSystemProtocolGuid,
			NULL,
			&BufferSize,
			HandleBuffer);
   }

  //
  // Get the map name(s) for each one.
  //
  for ( LoopVar = 0, Found = FALSE
      ; LoopVar < (BufferSize / sizeof(EFI_HANDLE)) && HandleBuffer != NULL
      ; LoopVar ++
     ) {
    Status = PerformSingleMappingDisplay(HandleBuffer[LoopVar]);
    if (!EFI_ERROR(Status)) {
      Found = TRUE;
    }
  }
  
  FreePool(HandleBuffer);
	
  return EFI_SUCCESS;
}

 

运行结果,和 Map命令的对比

ShowMap

完整的代码下载
ShowMap

Leave a Reply

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

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>