目标:写一个小程序来枚举当前系统中的盘符。比如: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

























