目标:写一个小程序来枚举当前系统中的盘符。比如: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
請問如果是參考mm這個指令,來寫一個讀取/修改serial port address的應用呢?
呃 问一下:怎么用 mm 修改serial port address?
mm [-adress] [-value]
我想參考mm來寫,但不太會啊
恩 回头我看一下
Serial Port 一般都是IO Port,只有特别的才会映射到内存中。具体访问的话没有统一的接口,换句话说,只有 Serial_Io_Protocol 这样的才有统一的方法。
我写的UEFI APP里,gEfiShellProtocol 这个Protocol 直接extern是NULL的,locateProtocol也不行,关键我还要用setEnv的子函数,mEfiShellEnvironment2 还没有setEnv,只有getEnv. 不知道该怎么办了。
要不你考虑换一个带有 mEfiShellEnvironment2 的 UEFI Shell ?
感谢回复!
我现在在用UDK2015和UDK2018写UEFI程序,这两个EKD2 source只有gEfiShellProtocol这个protocol有setEnv,没办法用mEfiShellEnvironment2。
我的APP要完成的功能是从文件中获取UUID的字符串作为参数传给第三方efi程序。
我是初学者,只想到把UUID SetEnv到一个var,然后xxx.efi %var%运行第三方efi。除此以外还有别的好办法吗? 谢谢!
要不试试用 gST->SetVariable() 看看传一下?
gST->SetVariable是操作NVRAM的,NVRAM在运行第三方efi程序前会被清掉,其实我做的功能就是要在更新BIOS之前做backup
有一种是设置易失的环境变量应该不是写道 nvram 中的。你可以参考一下: http://www.lab-z.com/stu97/
再不行你就自己加入一个 Protcol 用自定义的 Protocol 来传递参数试试。
我发现gEfiShellProtocol locate不到跟shell版本有关,在某个shell.efi环境中locateProtocol就是正常的。
直接运行efi不输出,源码在最新的EDK下会崩溃,
根据新的ShellPkg,\ShellPkg\Library\UefiShellLib\UefiShellLib.c
Status = gBS->OpenProtocol(
gImageHandle,
&gEfiShellProtocolGuid,
(VOID **)&gEfiShellProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
改为
Status = gBS->OpenProtocol(
ImageHandle,
&gEfiShellEnvironment2Guid,
(VOID **)&mEfiShellEnvironment2,
ImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
运行报Invalid Parameter
抽空我研究一下,估计是因为那个 Protocol 不再支持了。
好的,我自己先看看map.c代码。
根据map.c可以得到FS0:;F1:,想了几种办法解析不出来FS0。