BmBootDescription.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. /** @file
  2. Library functions which relate with boot option description.
  3. Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
  4. (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
  5. SPDX-License-Identifier: BSD-2-Clause-Patent
  6. **/
  7. #include "InternalBm.h"
  8. #define VENDOR_IDENTIFICATION_OFFSET 3
  9. #define VENDOR_IDENTIFICATION_LENGTH 8
  10. #define PRODUCT_IDENTIFICATION_OFFSET 11
  11. #define PRODUCT_IDENTIFICATION_LENGTH 16
  12. CONST UINT16 mBmUsbLangId = 0x0409; // English
  13. CHAR16 mBmUefiPrefix[] = L"UEFI ";
  14. LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers);
  15. /**
  16. For a bootable Device path, return its boot type.
  17. @param DevicePath The bootable device Path to check
  18. @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node
  19. which HID is floppy device.
  20. @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
  21. and its last device path node's subtype is MSG_ATAPI_DP.
  22. @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
  23. and its last device path node's subtype is MSG_SATA_DP.
  24. @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
  25. and its last device path node's subtype is MSG_SCSI_DP.
  26. @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
  27. and its last device path node's subtype is MSG_USB_DP.
  28. @retval BmMiscBoot If tiven device path doesn't match the above condition.
  29. **/
  30. BM_BOOT_TYPE
  31. BmDevicePathType (
  32. IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
  33. )
  34. {
  35. EFI_DEVICE_PATH_PROTOCOL *Node;
  36. EFI_DEVICE_PATH_PROTOCOL *NextNode;
  37. ASSERT (DevicePath != NULL);
  38. for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
  39. switch (DevicePathType (Node)) {
  40. case ACPI_DEVICE_PATH:
  41. if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *)Node)->HID) == 0x0604) {
  42. return BmAcpiFloppyBoot;
  43. }
  44. break;
  45. case HARDWARE_DEVICE_PATH:
  46. if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
  47. return BmHardwareDeviceBoot;
  48. }
  49. break;
  50. case MESSAGING_DEVICE_PATH:
  51. //
  52. // Skip LUN device node
  53. //
  54. NextNode = Node;
  55. do {
  56. NextNode = NextDevicePathNode (NextNode);
  57. } while (
  58. (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
  59. (DevicePathSubType (NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
  60. );
  61. //
  62. // If the device path not only point to driver device, it is not a messaging device path,
  63. //
  64. if (!IsDevicePathEndType (NextNode)) {
  65. continue;
  66. }
  67. switch (DevicePathSubType (Node)) {
  68. case MSG_ATAPI_DP:
  69. return BmMessageAtapiBoot;
  70. break;
  71. case MSG_SATA_DP:
  72. return BmMessageSataBoot;
  73. break;
  74. case MSG_USB_DP:
  75. return BmMessageUsbBoot;
  76. break;
  77. case MSG_SCSI_DP:
  78. return BmMessageScsiBoot;
  79. break;
  80. }
  81. }
  82. }
  83. return BmMiscBoot;
  84. }
  85. /**
  86. Eliminate the extra spaces in the Str to one space.
  87. @param Str Input string info.
  88. **/
  89. VOID
  90. BmEliminateExtraSpaces (
  91. IN CHAR16 *Str
  92. )
  93. {
  94. UINTN Index;
  95. UINTN ActualIndex;
  96. for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
  97. if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
  98. Str[ActualIndex++] = Str[Index];
  99. }
  100. }
  101. Str[ActualIndex] = L'\0';
  102. }
  103. /**
  104. Try to get the controller's ATA/ATAPI description.
  105. @param Handle Controller handle.
  106. @return The description string.
  107. **/
  108. CHAR16 *
  109. BmGetDescriptionFromDiskInfo (
  110. IN EFI_HANDLE Handle
  111. )
  112. {
  113. UINTN Index;
  114. EFI_STATUS Status;
  115. EFI_DISK_INFO_PROTOCOL *DiskInfo;
  116. UINT32 BufferSize;
  117. EFI_ATAPI_IDENTIFY_DATA IdentifyData;
  118. EFI_SCSI_INQUIRY_DATA InquiryData;
  119. CHAR16 *Description;
  120. UINTN Length;
  121. CONST UINTN ModelNameLength = 40;
  122. CONST UINTN SerialNumberLength = 20;
  123. CHAR8 *StrPtr;
  124. UINT8 Temp;
  125. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  126. Description = NULL;
  127. Status = gBS->HandleProtocol (
  128. Handle,
  129. &gEfiDiskInfoProtocolGuid,
  130. (VOID **)&DiskInfo
  131. );
  132. if (EFI_ERROR (Status)) {
  133. return NULL;
  134. }
  135. if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
  136. CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid))
  137. {
  138. BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);
  139. Status = DiskInfo->Identify (
  140. DiskInfo,
  141. &IdentifyData,
  142. &BufferSize
  143. );
  144. if (!EFI_ERROR (Status)) {
  145. Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
  146. ASSERT (Description != NULL);
  147. for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
  148. Description[Index] = (CHAR16)IdentifyData.ModelName[Index + 1];
  149. Description[Index + 1] = (CHAR16)IdentifyData.ModelName[Index];
  150. }
  151. Length = Index;
  152. Description[Length++] = L' ';
  153. for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
  154. Description[Length + Index] = (CHAR16)IdentifyData.SerialNo[Index + 1];
  155. Description[Length + Index + 1] = (CHAR16)IdentifyData.SerialNo[Index];
  156. }
  157. Length += Index;
  158. Description[Length++] = L'\0';
  159. ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
  160. BmEliminateExtraSpaces (Description);
  161. }
  162. } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid) ||
  163. CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoUfsInterfaceGuid))
  164. {
  165. BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);
  166. Status = DiskInfo->Inquiry (
  167. DiskInfo,
  168. &InquiryData,
  169. &BufferSize
  170. );
  171. if (!EFI_ERROR (Status)) {
  172. Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
  173. ASSERT (Description != NULL);
  174. //
  175. // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
  176. // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
  177. // Here combine the vendor identification and product identification to the description.
  178. //
  179. StrPtr = (CHAR8 *)(&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
  180. Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
  181. StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
  182. AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1);
  183. StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
  184. //
  185. // Add one space at the middle of vendor information and product information.
  186. //
  187. Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
  188. StrPtr = (CHAR8 *)(&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
  189. StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
  190. AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1);
  191. BmEliminateExtraSpaces (Description);
  192. }
  193. } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoSdMmcInterfaceGuid)) {
  194. DevicePath = DevicePathFromHandle (Handle);
  195. if (DevicePath == NULL) {
  196. return NULL;
  197. }
  198. while (!IsDevicePathEnd (DevicePath) && (DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH)) {
  199. DevicePath = NextDevicePathNode (DevicePath);
  200. }
  201. if (IsDevicePathEnd (DevicePath)) {
  202. return NULL;
  203. }
  204. if (DevicePathSubType (DevicePath) == MSG_SD_DP) {
  205. Description = L"SD Device";
  206. } else if (DevicePathSubType (DevicePath) == MSG_EMMC_DP) {
  207. Description = L"eMMC Device";
  208. } else {
  209. return NULL;
  210. }
  211. Description = AllocateCopyPool (StrSize (Description), Description);
  212. }
  213. return Description;
  214. }
  215. /**
  216. Try to get the controller's USB description.
  217. @param Handle Controller handle.
  218. @return The description string.
  219. **/
  220. CHAR16 *
  221. BmGetUsbDescription (
  222. IN EFI_HANDLE Handle
  223. )
  224. {
  225. EFI_STATUS Status;
  226. EFI_USB_IO_PROTOCOL *UsbIo;
  227. CHAR16 NullChar;
  228. CHAR16 *Manufacturer;
  229. CHAR16 *Product;
  230. CHAR16 *SerialNumber;
  231. CHAR16 *Description;
  232. EFI_USB_DEVICE_DESCRIPTOR DevDesc;
  233. UINTN DescMaxSize;
  234. Status = gBS->HandleProtocol (
  235. Handle,
  236. &gEfiUsbIoProtocolGuid,
  237. (VOID **)&UsbIo
  238. );
  239. if (EFI_ERROR (Status)) {
  240. return NULL;
  241. }
  242. NullChar = L'\0';
  243. Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
  244. if (EFI_ERROR (Status)) {
  245. return NULL;
  246. }
  247. Status = UsbIo->UsbGetStringDescriptor (
  248. UsbIo,
  249. mBmUsbLangId,
  250. DevDesc.StrManufacturer,
  251. &Manufacturer
  252. );
  253. if (EFI_ERROR (Status)) {
  254. Manufacturer = &NullChar;
  255. }
  256. Status = UsbIo->UsbGetStringDescriptor (
  257. UsbIo,
  258. mBmUsbLangId,
  259. DevDesc.StrProduct,
  260. &Product
  261. );
  262. if (EFI_ERROR (Status)) {
  263. Product = &NullChar;
  264. }
  265. Status = UsbIo->UsbGetStringDescriptor (
  266. UsbIo,
  267. mBmUsbLangId,
  268. DevDesc.StrSerialNumber,
  269. &SerialNumber
  270. );
  271. if (EFI_ERROR (Status)) {
  272. SerialNumber = &NullChar;
  273. }
  274. if ((Manufacturer == &NullChar) &&
  275. (Product == &NullChar) &&
  276. (SerialNumber == &NullChar)
  277. )
  278. {
  279. return NULL;
  280. }
  281. DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);
  282. Description = AllocateZeroPool (DescMaxSize);
  283. ASSERT (Description != NULL);
  284. StrCatS (Description, DescMaxSize/sizeof (CHAR16), Manufacturer);
  285. StrCatS (Description, DescMaxSize/sizeof (CHAR16), L" ");
  286. StrCatS (Description, DescMaxSize/sizeof (CHAR16), Product);
  287. StrCatS (Description, DescMaxSize/sizeof (CHAR16), L" ");
  288. StrCatS (Description, DescMaxSize/sizeof (CHAR16), SerialNumber);
  289. if (Manufacturer != &NullChar) {
  290. FreePool (Manufacturer);
  291. }
  292. if (Product != &NullChar) {
  293. FreePool (Product);
  294. }
  295. if (SerialNumber != &NullChar) {
  296. FreePool (SerialNumber);
  297. }
  298. BmEliminateExtraSpaces (Description);
  299. return Description;
  300. }
  301. /**
  302. Return the description for network boot device.
  303. @param Handle Controller handle.
  304. @return The description string.
  305. **/
  306. CHAR16 *
  307. BmGetNetworkDescription (
  308. IN EFI_HANDLE Handle
  309. )
  310. {
  311. EFI_STATUS Status;
  312. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  313. MAC_ADDR_DEVICE_PATH *Mac;
  314. VLAN_DEVICE_PATH *Vlan;
  315. EFI_DEVICE_PATH_PROTOCOL *Ip;
  316. EFI_DEVICE_PATH_PROTOCOL *Uri;
  317. CHAR16 *Description;
  318. UINTN DescriptionSize;
  319. Status = gBS->OpenProtocol (
  320. Handle,
  321. &gEfiLoadFileProtocolGuid,
  322. NULL,
  323. gImageHandle,
  324. Handle,
  325. EFI_OPEN_PROTOCOL_TEST_PROTOCOL
  326. );
  327. if (EFI_ERROR (Status)) {
  328. return NULL;
  329. }
  330. Status = gBS->OpenProtocol (
  331. Handle,
  332. &gEfiDevicePathProtocolGuid,
  333. (VOID **)&DevicePath,
  334. gImageHandle,
  335. Handle,
  336. EFI_OPEN_PROTOCOL_GET_PROTOCOL
  337. );
  338. if (EFI_ERROR (Status) || (DevicePath == NULL)) {
  339. return NULL;
  340. }
  341. //
  342. // The PXE device path is like:
  343. // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]
  344. // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)
  345. // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)
  346. //
  347. // The HTTP device path is like:
  348. // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)
  349. // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)
  350. //
  351. while (!IsDevicePathEnd (DevicePath) &&
  352. ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
  353. (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP))
  354. )
  355. {
  356. DevicePath = NextDevicePathNode (DevicePath);
  357. }
  358. if (IsDevicePathEnd (DevicePath)) {
  359. return NULL;
  360. }
  361. Mac = (MAC_ADDR_DEVICE_PATH *)DevicePath;
  362. DevicePath = NextDevicePathNode (DevicePath);
  363. //
  364. // Locate the optional Vlan node
  365. //
  366. if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
  367. (DevicePathSubType (DevicePath) == MSG_VLAN_DP)
  368. )
  369. {
  370. Vlan = (VLAN_DEVICE_PATH *)DevicePath;
  371. DevicePath = NextDevicePathNode (DevicePath);
  372. } else {
  373. Vlan = NULL;
  374. }
  375. //
  376. // Skip the optional Wi-Fi node
  377. //
  378. if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
  379. (DevicePathSubType (DevicePath) == MSG_WIFI_DP)
  380. )
  381. {
  382. DevicePath = NextDevicePathNode (DevicePath);
  383. }
  384. //
  385. // Locate the IP node
  386. //
  387. if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
  388. ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||
  389. (DevicePathSubType (DevicePath) == MSG_IPv6_DP))
  390. )
  391. {
  392. Ip = DevicePath;
  393. DevicePath = NextDevicePathNode (DevicePath);
  394. } else {
  395. Ip = NULL;
  396. }
  397. //
  398. // Skip the optional DNS node
  399. //
  400. if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
  401. (DevicePathSubType (DevicePath) == MSG_DNS_DP)
  402. )
  403. {
  404. DevicePath = NextDevicePathNode (DevicePath);
  405. }
  406. //
  407. // Locate the URI node
  408. //
  409. if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
  410. (DevicePathSubType (DevicePath) == MSG_URI_DP)
  411. )
  412. {
  413. Uri = DevicePath;
  414. DevicePath = NextDevicePathNode (DevicePath);
  415. } else {
  416. Uri = NULL;
  417. }
  418. //
  419. // Build description like below:
  420. // "PXEv6 (MAC:112233445566 VLAN1)"
  421. // "HTTPv4 (MAC:112233445566)"
  422. //
  423. DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");
  424. Description = AllocatePool (DescriptionSize);
  425. ASSERT (Description != NULL);
  426. UnicodeSPrint (
  427. Description,
  428. DescriptionSize,
  429. (Vlan == NULL) ?
  430. L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
  431. L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
  432. (Uri == NULL) ? L"PXE" : L"HTTP",
  433. ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,
  434. Mac->MacAddress.Addr[0],
  435. Mac->MacAddress.Addr[1],
  436. Mac->MacAddress.Addr[2],
  437. Mac->MacAddress.Addr[3],
  438. Mac->MacAddress.Addr[4],
  439. Mac->MacAddress.Addr[5],
  440. (Vlan == NULL) ? 0 : Vlan->VlanId
  441. );
  442. return Description;
  443. }
  444. /**
  445. Return the boot description for LoadFile
  446. @param Handle Controller handle.
  447. @return The description string.
  448. **/
  449. CHAR16 *
  450. BmGetLoadFileDescription (
  451. IN EFI_HANDLE Handle
  452. )
  453. {
  454. EFI_STATUS Status;
  455. EFI_DEVICE_PATH_PROTOCOL *FilePath;
  456. EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
  457. CHAR16 *Description;
  458. EFI_LOAD_FILE_PROTOCOL *LoadFile;
  459. Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);
  460. if (EFI_ERROR (Status)) {
  461. return NULL;
  462. }
  463. //
  464. // Get the file name
  465. //
  466. Description = NULL;
  467. Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);
  468. if (!EFI_ERROR (Status)) {
  469. DevicePathNode = FilePath;
  470. while (!IsDevicePathEnd (DevicePathNode)) {
  471. if ((DevicePathNode->Type == MEDIA_DEVICE_PATH) && (DevicePathNode->SubType == MEDIA_FILEPATH_DP)) {
  472. Description = (CHAR16 *)(DevicePathNode + 1);
  473. break;
  474. }
  475. DevicePathNode = NextDevicePathNode (DevicePathNode);
  476. }
  477. }
  478. if (Description != NULL) {
  479. return AllocateCopyPool (StrSize (Description), Description);
  480. }
  481. return NULL;
  482. }
  483. /**
  484. Return the boot description for NVME boot device.
  485. @param Handle Controller handle.
  486. @return The description string.
  487. **/
  488. CHAR16 *
  489. BmGetNvmeDescription (
  490. IN EFI_HANDLE Handle
  491. )
  492. {
  493. EFI_STATUS Status;
  494. EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru;
  495. EFI_DEV_PATH_PTR DevicePath;
  496. EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
  497. EFI_NVM_EXPRESS_COMMAND Command;
  498. EFI_NVM_EXPRESS_COMPLETION Completion;
  499. NVME_ADMIN_CONTROLLER_DATA ControllerData;
  500. CHAR16 *Description;
  501. CHAR16 *Char;
  502. UINTN Index;
  503. Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath.DevPath);
  504. if (EFI_ERROR (Status)) {
  505. return NULL;
  506. }
  507. Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle);
  508. if (EFI_ERROR (Status) ||
  509. (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) ||
  510. (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP))
  511. {
  512. //
  513. // Do not return description when the Handle is not a child of NVME controller.
  514. //
  515. return NULL;
  516. }
  517. //
  518. // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number.
  519. //
  520. Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **)&NvmePassthru);
  521. ASSERT_EFI_ERROR (Status);
  522. ZeroMem (&CommandPacket, sizeof (EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
  523. ZeroMem (&Command, sizeof (EFI_NVM_EXPRESS_COMMAND));
  524. ZeroMem (&Completion, sizeof (EFI_NVM_EXPRESS_COMPLETION));
  525. Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
  526. //
  527. // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
  528. // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
  529. //
  530. Command.Nsid = 0;
  531. CommandPacket.NvmeCmd = &Command;
  532. CommandPacket.NvmeCompletion = &Completion;
  533. CommandPacket.TransferBuffer = &ControllerData;
  534. CommandPacket.TransferLength = sizeof (ControllerData);
  535. CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);
  536. CommandPacket.QueueType = NVME_ADMIN_QUEUE;
  537. //
  538. // Set bit 0 (Cns bit) to 1 to identify a controller
  539. //
  540. Command.Cdw10 = 1;
  541. Command.Flags = CDW10_VALID;
  542. Status = NvmePassthru->PassThru (
  543. NvmePassthru,
  544. 0,
  545. &CommandPacket,
  546. NULL
  547. );
  548. if (EFI_ERROR (Status)) {
  549. return NULL;
  550. }
  551. Description = AllocateZeroPool (
  552. (ARRAY_SIZE (ControllerData.Mn) + 1
  553. + ARRAY_SIZE (ControllerData.Sn) + 1
  554. + MAXIMUM_VALUE_CHARACTERS + 1
  555. ) * sizeof (CHAR16)
  556. );
  557. if (Description != NULL) {
  558. Char = Description;
  559. for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) {
  560. *(Char++) = (CHAR16)ControllerData.Mn[Index];
  561. }
  562. *(Char++) = L' ';
  563. for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) {
  564. *(Char++) = (CHAR16)ControllerData.Sn[Index];
  565. }
  566. *(Char++) = L' ';
  567. UnicodeValueToStringS (
  568. Char,
  569. sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1),
  570. 0,
  571. DevicePath.NvmeNamespace->NamespaceId,
  572. 0
  573. );
  574. BmEliminateExtraSpaces (Description);
  575. }
  576. return Description;
  577. }
  578. /**
  579. Return the boot description for the controller based on the type.
  580. @param Handle Controller handle.
  581. @return The description string.
  582. **/
  583. CHAR16 *
  584. BmGetMiscDescription (
  585. IN EFI_HANDLE Handle
  586. )
  587. {
  588. EFI_STATUS Status;
  589. CHAR16 *Description;
  590. EFI_BLOCK_IO_PROTOCOL *BlockIo;
  591. EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
  592. switch (BmDevicePathType (DevicePathFromHandle (Handle))) {
  593. case BmAcpiFloppyBoot:
  594. Description = L"Floppy";
  595. break;
  596. case BmMessageAtapiBoot:
  597. case BmMessageSataBoot:
  598. Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
  599. ASSERT_EFI_ERROR (Status);
  600. //
  601. // Assume a removable SATA device should be the DVD/CD device
  602. //
  603. Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
  604. break;
  605. case BmMessageUsbBoot:
  606. Description = L"USB Device";
  607. break;
  608. case BmMessageScsiBoot:
  609. Description = L"SCSI Device";
  610. break;
  611. case BmHardwareDeviceBoot:
  612. Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
  613. if (!EFI_ERROR (Status)) {
  614. Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
  615. } else {
  616. Description = L"Misc Device";
  617. }
  618. break;
  619. default:
  620. Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
  621. if (!EFI_ERROR (Status)) {
  622. Description = L"Non-Block Boot Device";
  623. } else {
  624. Description = L"Misc Device";
  625. }
  626. break;
  627. }
  628. return AllocateCopyPool (StrSize (Description), Description);
  629. }
  630. /**
  631. Register the platform provided boot description handler.
  632. @param Handler The platform provided boot description handler
  633. @retval EFI_SUCCESS The handler was registered successfully.
  634. @retval EFI_ALREADY_STARTED The handler was already registered.
  635. @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
  636. **/
  637. EFI_STATUS
  638. EFIAPI
  639. EfiBootManagerRegisterBootDescriptionHandler (
  640. IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
  641. )
  642. {
  643. LIST_ENTRY *Link;
  644. BM_BOOT_DESCRIPTION_ENTRY *Entry;
  645. for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
  646. ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
  647. ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
  648. )
  649. {
  650. Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
  651. if (Entry->Handler == Handler) {
  652. return EFI_ALREADY_STARTED;
  653. }
  654. }
  655. Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));
  656. if (Entry == NULL) {
  657. return EFI_OUT_OF_RESOURCES;
  658. }
  659. Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;
  660. Entry->Handler = Handler;
  661. InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);
  662. return EFI_SUCCESS;
  663. }
  664. BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {
  665. BmGetUsbDescription,
  666. BmGetDescriptionFromDiskInfo,
  667. BmGetNetworkDescription,
  668. BmGetLoadFileDescription,
  669. BmGetNvmeDescription,
  670. BmGetMiscDescription
  671. };
  672. /**
  673. Return the boot description for the controller.
  674. @param Handle Controller handle.
  675. @return The description string.
  676. **/
  677. CHAR16 *
  678. BmGetBootDescription (
  679. IN EFI_HANDLE Handle
  680. )
  681. {
  682. LIST_ENTRY *Link;
  683. BM_BOOT_DESCRIPTION_ENTRY *Entry;
  684. CHAR16 *Description;
  685. CHAR16 *DefaultDescription;
  686. CHAR16 *Temp;
  687. UINTN Index;
  688. //
  689. // Firstly get the default boot description
  690. //
  691. DefaultDescription = NULL;
  692. for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {
  693. DefaultDescription = mBmBootDescriptionHandlers[Index](Handle);
  694. if (DefaultDescription != NULL) {
  695. //
  696. // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
  697. // ONLY for core provided boot description handler.
  698. //
  699. Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));
  700. ASSERT (Temp != NULL);
  701. StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);
  702. StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);
  703. FreePool (DefaultDescription);
  704. DefaultDescription = Temp;
  705. break;
  706. }
  707. }
  708. ASSERT (DefaultDescription != NULL);
  709. //
  710. // Secondly query platform for the better boot description
  711. //
  712. for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
  713. ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
  714. ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
  715. )
  716. {
  717. Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
  718. Description = Entry->Handler (Handle, DefaultDescription);
  719. if (Description != NULL) {
  720. FreePool (DefaultDescription);
  721. return Description;
  722. }
  723. }
  724. return DefaultDescription;
  725. }
  726. /**
  727. Enumerate all boot option descriptions and append " 2"/" 3"/... to make
  728. unique description.
  729. @param BootOptions Array of boot options.
  730. @param BootOptionCount Count of boot options.
  731. **/
  732. VOID
  733. BmMakeBootOptionDescriptionUnique (
  734. EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
  735. UINTN BootOptionCount
  736. )
  737. {
  738. UINTN Base;
  739. UINTN Index;
  740. UINTN DescriptionSize;
  741. UINTN MaxSuffixSize;
  742. BOOLEAN *Visited;
  743. UINTN MatchCount;
  744. if (BootOptionCount == 0) {
  745. return;
  746. }
  747. //
  748. // Calculate the maximum buffer size for the number suffix.
  749. // The initial sizeof (CHAR16) is for the blank space before the number.
  750. //
  751. MaxSuffixSize = sizeof (CHAR16);
  752. for (Index = BootOptionCount; Index != 0; Index = Index / 10) {
  753. MaxSuffixSize += sizeof (CHAR16);
  754. }
  755. Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);
  756. ASSERT (Visited != NULL);
  757. for (Base = 0; Base < BootOptionCount; Base++) {
  758. if (!Visited[Base]) {
  759. MatchCount = 1;
  760. Visited[Base] = TRUE;
  761. DescriptionSize = StrSize (BootOptions[Base].Description);
  762. for (Index = Base + 1; Index < BootOptionCount; Index++) {
  763. if (!Visited[Index] && (StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0)) {
  764. Visited[Index] = TRUE;
  765. MatchCount++;
  766. FreePool (BootOptions[Index].Description);
  767. BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);
  768. UnicodeSPrint (
  769. BootOptions[Index].Description,
  770. DescriptionSize + MaxSuffixSize,
  771. L"%s %d",
  772. BootOptions[Base].Description,
  773. MatchCount
  774. );
  775. }
  776. }
  777. }
  778. }
  779. FreePool (Visited);
  780. }