LinuxInitrdDynamicShellCommand.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /** @file
  2. Provides 'initrd' dynamic UEFI shell command to load a Linux initrd
  3. via its GUIDed vendor media path
  4. Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include <Uefi.h>
  8. #include <Library/DebugLib.h>
  9. #include <Library/DevicePathLib.h>
  10. #include <Library/HiiLib.h>
  11. #include <Library/MemoryAllocationLib.h>
  12. #include <Library/ShellLib.h>
  13. #include <Library/UefiBootServicesTableLib.h>
  14. #include <Library/UefiHiiServicesLib.h>
  15. #include <Guid/LinuxEfiInitrdMedia.h>
  16. #include <Protocol/DevicePath.h>
  17. #include <Protocol/HiiPackageList.h>
  18. #include <Protocol/LoadFile2.h>
  19. #include <Protocol/ShellDynamicCommand.h>
  20. #pragma pack (1)
  21. typedef struct {
  22. VENDOR_DEVICE_PATH VenMediaNode;
  23. EFI_DEVICE_PATH_PROTOCOL EndNode;
  24. } SINGLE_NODE_VENDOR_MEDIA_DEVPATH;
  25. #pragma pack ()
  26. STATIC EFI_HII_HANDLE mLinuxInitrdShellCommandHiiHandle;
  27. STATIC EFI_PHYSICAL_ADDRESS mInitrdFileAddress;
  28. STATIC UINTN mInitrdFileSize;
  29. STATIC EFI_HANDLE mInitrdLoadFile2Handle;
  30. STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
  31. { L"-u", TypeFlag },
  32. { NULL, TypeMax }
  33. };
  34. STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath = {
  35. {
  36. {
  37. MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,{ sizeof (VENDOR_DEVICE_PATH) }
  38. },
  39. LINUX_EFI_INITRD_MEDIA_GUID
  40. },{
  41. END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
  42. { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
  43. }
  44. };
  45. STATIC
  46. BOOLEAN
  47. IsOtherInitrdDevicePathAlreadyInstalled (
  48. VOID
  49. )
  50. {
  51. EFI_STATUS Status;
  52. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  53. EFI_HANDLE Handle;
  54. DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mInitrdDevicePath;
  55. Status = gBS->LocateDevicePath (
  56. &gEfiLoadFile2ProtocolGuid,
  57. &DevicePath,
  58. &Handle
  59. );
  60. if (EFI_ERROR (Status)) {
  61. return FALSE;
  62. }
  63. //
  64. // Check whether the existing instance is one that we installed during
  65. // a previous invocation.
  66. //
  67. if (Handle == mInitrdLoadFile2Handle) {
  68. return FALSE;
  69. }
  70. return TRUE;
  71. }
  72. STATIC
  73. EFI_STATUS
  74. EFIAPI
  75. InitrdLoadFile2 (
  76. IN EFI_LOAD_FILE2_PROTOCOL *This,
  77. IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
  78. IN BOOLEAN BootPolicy,
  79. IN OUT UINTN *BufferSize,
  80. OUT VOID *Buffer OPTIONAL
  81. )
  82. {
  83. if (BootPolicy) {
  84. return EFI_UNSUPPORTED;
  85. }
  86. if ((BufferSize == NULL) || !IsDevicePathValid (FilePath, 0)) {
  87. return EFI_INVALID_PARAMETER;
  88. }
  89. if ((FilePath->Type != END_DEVICE_PATH_TYPE) ||
  90. (FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) ||
  91. (mInitrdFileSize == 0))
  92. {
  93. return EFI_NOT_FOUND;
  94. }
  95. if ((Buffer == NULL) || (*BufferSize < mInitrdFileSize)) {
  96. *BufferSize = mInitrdFileSize;
  97. return EFI_BUFFER_TOO_SMALL;
  98. }
  99. ASSERT (mInitrdFileAddress != 0);
  100. gBS->CopyMem (Buffer, (VOID *)(UINTN)mInitrdFileAddress, mInitrdFileSize);
  101. *BufferSize = mInitrdFileSize;
  102. return EFI_SUCCESS;
  103. }
  104. STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {
  105. InitrdLoadFile2,
  106. };
  107. STATIC
  108. EFI_STATUS
  109. UninstallLoadFile2Protocol (
  110. VOID
  111. )
  112. {
  113. EFI_STATUS Status;
  114. if (mInitrdLoadFile2Handle != NULL) {
  115. Status = gBS->UninstallMultipleProtocolInterfaces (
  116. mInitrdLoadFile2Handle,
  117. &gEfiDevicePathProtocolGuid,
  118. &mInitrdDevicePath,
  119. &gEfiLoadFile2ProtocolGuid,
  120. &mInitrdLoadFile2,
  121. NULL
  122. );
  123. if (!EFI_ERROR (Status)) {
  124. mInitrdLoadFile2Handle = NULL;
  125. }
  126. return Status;
  127. }
  128. return EFI_SUCCESS;
  129. }
  130. STATIC
  131. VOID
  132. FreeInitrdFile (
  133. VOID
  134. )
  135. {
  136. if (mInitrdFileSize != 0) {
  137. gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));
  138. mInitrdFileSize = 0;
  139. }
  140. }
  141. STATIC
  142. EFI_STATUS
  143. CacheInitrdFile (
  144. IN SHELL_FILE_HANDLE FileHandle
  145. )
  146. {
  147. EFI_STATUS Status;
  148. UINT64 FileSize;
  149. UINTN ReadSize;
  150. Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
  151. if (EFI_ERROR (Status)) {
  152. return Status;
  153. }
  154. if ((FileSize == 0) || (FileSize > MAX_UINTN)) {
  155. return EFI_UNSUPPORTED;
  156. }
  157. Status = gBS->AllocatePages (
  158. AllocateAnyPages,
  159. EfiLoaderData,
  160. EFI_SIZE_TO_PAGES ((UINTN)FileSize),
  161. &mInitrdFileAddress
  162. );
  163. if (EFI_ERROR (Status)) {
  164. return Status;
  165. }
  166. ReadSize = (UINTN)FileSize;
  167. Status = gEfiShellProtocol->ReadFile (
  168. FileHandle,
  169. &ReadSize,
  170. (VOID *)(UINTN)mInitrdFileAddress
  171. );
  172. if (EFI_ERROR (Status) || (ReadSize < FileSize)) {
  173. DEBUG ((
  174. DEBUG_WARN,
  175. "%a: failed to read initrd file - %r 0x%lx 0x%lx\n",
  176. __FUNCTION__,
  177. Status,
  178. (UINT64)ReadSize,
  179. FileSize
  180. ));
  181. goto FreeMemory;
  182. }
  183. if (mInitrdLoadFile2Handle == NULL) {
  184. Status = gBS->InstallMultipleProtocolInterfaces (
  185. &mInitrdLoadFile2Handle,
  186. &gEfiDevicePathProtocolGuid,
  187. &mInitrdDevicePath,
  188. &gEfiLoadFile2ProtocolGuid,
  189. &mInitrdLoadFile2,
  190. NULL
  191. );
  192. ASSERT_EFI_ERROR (Status);
  193. }
  194. mInitrdFileSize = (UINTN)FileSize;
  195. return EFI_SUCCESS;
  196. FreeMemory:
  197. gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));
  198. return Status;
  199. }
  200. /**
  201. Function for 'initrd' command.
  202. @param[in] ImageHandle Handle to the Image (NULL if Internal).
  203. @param[in] SystemTable Pointer to the System Table (NULL if Internal).
  204. **/
  205. STATIC
  206. SHELL_STATUS
  207. EFIAPI
  208. RunInitrd (
  209. IN EFI_HANDLE ImageHandle,
  210. IN EFI_SYSTEM_TABLE *SystemTable
  211. )
  212. {
  213. EFI_STATUS Status;
  214. LIST_ENTRY *Package;
  215. CHAR16 *ProblemParam;
  216. CONST CHAR16 *Param;
  217. CHAR16 *Filename;
  218. SHELL_STATUS ShellStatus;
  219. SHELL_FILE_HANDLE FileHandle;
  220. ProblemParam = NULL;
  221. ShellStatus = SHELL_SUCCESS;
  222. Status = ShellInitialize ();
  223. ASSERT_EFI_ERROR (Status);
  224. //
  225. // parse the command line
  226. //
  227. Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
  228. if (EFI_ERROR (Status)) {
  229. if ((Status == EFI_VOLUME_CORRUPTED) && (ProblemParam != NULL)) {
  230. ShellPrintHiiEx (
  231. -1,
  232. -1,
  233. NULL,
  234. STRING_TOKEN (STR_GEN_PROBLEM),
  235. mLinuxInitrdShellCommandHiiHandle,
  236. L"initrd",
  237. ProblemParam
  238. );
  239. FreePool (ProblemParam);
  240. ShellStatus = SHELL_INVALID_PARAMETER;
  241. } else {
  242. ASSERT (FALSE);
  243. }
  244. } else if (IsOtherInitrdDevicePathAlreadyInstalled ()) {
  245. ShellPrintHiiEx (
  246. -1,
  247. -1,
  248. NULL,
  249. STRING_TOKEN (STR_GEN_ALREADY_INSTALLED),
  250. mLinuxInitrdShellCommandHiiHandle,
  251. L"initrd"
  252. );
  253. ShellStatus = SHELL_UNSUPPORTED;
  254. } else {
  255. if (ShellCommandLineGetCount (Package) > 2) {
  256. ShellPrintHiiEx (
  257. -1,
  258. -1,
  259. NULL,
  260. STRING_TOKEN (STR_GEN_TOO_MANY),
  261. mLinuxInitrdShellCommandHiiHandle,
  262. L"initrd"
  263. );
  264. ShellStatus = SHELL_INVALID_PARAMETER;
  265. } else if (ShellCommandLineGetCount (Package) < 2) {
  266. if (ShellCommandLineGetFlag (Package, L"-u")) {
  267. FreeInitrdFile ();
  268. UninstallLoadFile2Protocol ();
  269. } else {
  270. ShellPrintHiiEx (
  271. -1,
  272. -1,
  273. NULL,
  274. STRING_TOKEN (STR_GEN_TOO_FEW),
  275. mLinuxInitrdShellCommandHiiHandle,
  276. L"initrd"
  277. );
  278. ShellStatus = SHELL_INVALID_PARAMETER;
  279. }
  280. } else {
  281. Param = ShellCommandLineGetRawValue (Package, 1);
  282. ASSERT (Param != NULL);
  283. Filename = ShellFindFilePath (Param);
  284. if (Filename == NULL) {
  285. ShellPrintHiiEx (
  286. -1,
  287. -1,
  288. NULL,
  289. STRING_TOKEN (STR_GEN_FIND_FAIL),
  290. mLinuxInitrdShellCommandHiiHandle,
  291. L"initrd",
  292. Param
  293. );
  294. ShellStatus = SHELL_NOT_FOUND;
  295. } else {
  296. Status = ShellOpenFileByName (
  297. Filename,
  298. &FileHandle,
  299. EFI_FILE_MODE_READ,
  300. 0
  301. );
  302. if (!EFI_ERROR (Status)) {
  303. FreeInitrdFile ();
  304. Status = CacheInitrdFile (FileHandle);
  305. ShellCloseFile (&FileHandle);
  306. }
  307. if (EFI_ERROR (Status)) {
  308. ShellPrintHiiEx (
  309. -1,
  310. -1,
  311. NULL,
  312. STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),
  313. mLinuxInitrdShellCommandHiiHandle,
  314. L"initrd",
  315. Param
  316. );
  317. ShellStatus = SHELL_NOT_FOUND;
  318. }
  319. FreePool (Filename);
  320. }
  321. }
  322. }
  323. return ShellStatus;
  324. }
  325. /**
  326. This is the shell command handler function pointer callback type. This
  327. function handles the command when it is invoked in the shell.
  328. @param[in] This The instance of the
  329. EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
  330. @param[in] SystemTable The pointer to the system table.
  331. @param[in] ShellParameters The parameters associated with the command.
  332. @param[in] Shell The instance of the shell protocol used in
  333. the context of processing this command.
  334. @return EFI_SUCCESS the operation was successful
  335. @return other the operation failed.
  336. **/
  337. SHELL_STATUS
  338. EFIAPI
  339. LinuxInitrdCommandHandler (
  340. IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
  341. IN EFI_SYSTEM_TABLE *SystemTable,
  342. IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
  343. IN EFI_SHELL_PROTOCOL *Shell
  344. )
  345. {
  346. gEfiShellParametersProtocol = ShellParameters;
  347. gEfiShellProtocol = Shell;
  348. return RunInitrd (gImageHandle, SystemTable);
  349. }
  350. /**
  351. This is the command help handler function pointer callback type. This
  352. function is responsible for displaying help information for the associated
  353. command.
  354. @param[in] This The instance of the
  355. EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
  356. @param[in] Language The pointer to the language string to use.
  357. @return string Pool allocated help string, must be freed
  358. by caller
  359. **/
  360. STATIC
  361. CHAR16 *
  362. EFIAPI
  363. LinuxInitrdGetHelp (
  364. IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
  365. IN CONST CHAR8 *Language
  366. )
  367. {
  368. return HiiGetString (
  369. mLinuxInitrdShellCommandHiiHandle,
  370. STRING_TOKEN (STR_GET_HELP_INITRD),
  371. Language
  372. );
  373. }
  374. STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {
  375. L"initrd",
  376. LinuxInitrdCommandHandler,
  377. LinuxInitrdGetHelp
  378. };
  379. /**
  380. Retrieve HII package list from ImageHandle and publish to HII database.
  381. @param ImageHandle The image handle of the process.
  382. @return HII handle.
  383. **/
  384. STATIC
  385. EFI_HII_HANDLE
  386. InitializeHiiPackage (
  387. EFI_HANDLE ImageHandle
  388. )
  389. {
  390. EFI_STATUS Status;
  391. EFI_HII_PACKAGE_LIST_HEADER *PackageList;
  392. EFI_HII_HANDLE HiiHandle;
  393. //
  394. // Retrieve HII package list from ImageHandle
  395. //
  396. Status = gBS->OpenProtocol (
  397. ImageHandle,
  398. &gEfiHiiPackageListProtocolGuid,
  399. (VOID **)&PackageList,
  400. ImageHandle,
  401. NULL,
  402. EFI_OPEN_PROTOCOL_GET_PROTOCOL
  403. );
  404. ASSERT_EFI_ERROR (Status);
  405. if (EFI_ERROR (Status)) {
  406. return NULL;
  407. }
  408. //
  409. // Publish HII package list to HII Database.
  410. //
  411. Status = gHiiDatabase->NewPackageList (
  412. gHiiDatabase,
  413. PackageList,
  414. NULL,
  415. &HiiHandle
  416. );
  417. ASSERT_EFI_ERROR (Status);
  418. if (EFI_ERROR (Status)) {
  419. return NULL;
  420. }
  421. return HiiHandle;
  422. }
  423. /**
  424. Entry point of Linux Initrd dynamic UEFI Shell command.
  425. Produce the DynamicCommand protocol to handle "initrd" command.
  426. @param ImageHandle The image handle of the process.
  427. @param SystemTable The EFI System Table pointer.
  428. @retval EFI_SUCCESS Initrd command is executed successfully.
  429. @retval EFI_ABORTED HII package was failed to initialize.
  430. @retval others Other errors when executing Initrd command.
  431. **/
  432. EFI_STATUS
  433. EFIAPI
  434. LinuxInitrdDynamicShellCommandEntryPoint (
  435. IN EFI_HANDLE ImageHandle,
  436. IN EFI_SYSTEM_TABLE *SystemTable
  437. )
  438. {
  439. EFI_STATUS Status;
  440. mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);
  441. if (mLinuxInitrdShellCommandHiiHandle == NULL) {
  442. return EFI_ABORTED;
  443. }
  444. Status = gBS->InstallProtocolInterface (
  445. &ImageHandle,
  446. &gEfiShellDynamicCommandProtocolGuid,
  447. EFI_NATIVE_INTERFACE,
  448. &mLinuxInitrdDynamicCommand
  449. );
  450. ASSERT_EFI_ERROR (Status);
  451. return Status;
  452. }
  453. /**
  454. Unload the dynamic UEFI Shell command.
  455. @param ImageHandle The image handle of the process.
  456. @retval EFI_SUCCESS The image is unloaded.
  457. @retval Others Failed to unload the image.
  458. **/
  459. EFI_STATUS
  460. EFIAPI
  461. LinuxInitrdDynamicShellCommandUnload (
  462. IN EFI_HANDLE ImageHandle
  463. )
  464. {
  465. EFI_STATUS Status;
  466. FreeInitrdFile ();
  467. Status = UninstallLoadFile2Protocol ();
  468. if (EFI_ERROR (Status)) {
  469. return Status;
  470. }
  471. Status = gBS->UninstallProtocolInterface (
  472. ImageHandle,
  473. &gEfiShellDynamicCommandProtocolGuid,
  474. &mLinuxInitrdDynamicCommand
  475. );
  476. if (EFI_ERROR (Status)) {
  477. return Status;
  478. }
  479. HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);
  480. return EFI_SUCCESS;
  481. }