HddPasswordPei.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /** @file
  2. HddPassword PEI module which is used to unlock HDD password for S3.
  3. Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include "HddPasswordPei.h"
  7. EFI_GUID mHddPasswordDeviceInfoGuid = HDD_PASSWORD_DEVICE_INFO_GUID;
  8. /**
  9. Send unlock hdd password cmd through ATA PassThru PPI.
  10. @param[in] AtaPassThru The pointer to the ATA PassThru PPI.
  11. @param[in] Port The port number of the ATA device.
  12. @param[in] PortMultiplierPort The port multiplier port number of the ATA device.
  13. @param[in] Identifier The identifier to set user or master password.
  14. @param[in] Password The hdd password of attached ATA device.
  15. @retval EFI_SUCCESS Successful to send unlock hdd password cmd.
  16. @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
  17. @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.
  18. @retval EFI_DEVICE_ERROR Can not send unlock hdd password cmd.
  19. **/
  20. EFI_STATUS
  21. UnlockDevice (
  22. IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,
  23. IN UINT16 Port,
  24. IN UINT16 PortMultiplierPort,
  25. IN CHAR8 Identifier,
  26. IN CHAR8 *Password
  27. )
  28. {
  29. EFI_STATUS Status;
  30. EFI_ATA_COMMAND_BLOCK Acb;
  31. EFI_ATA_STATUS_BLOCK *Asb;
  32. EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;
  33. UINT8 Buffer[HDD_PAYLOAD];
  34. if ((AtaPassThru == NULL) || (Password == NULL)) {
  35. return EFI_INVALID_PARAMETER;
  36. }
  37. //
  38. // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in
  39. // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by
  40. // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,
  41. // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it
  42. // may not be aligned when allocated on stack for some compilers. Hence, we
  43. // use the API AllocateAlignedPages to ensure this structure is properly
  44. // aligned.
  45. //
  46. Asb = AllocateAlignedPages (
  47. EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),
  48. AtaPassThru->Mode->IoAlign
  49. );
  50. if (Asb == NULL) {
  51. return EFI_OUT_OF_RESOURCES;
  52. }
  53. //
  54. // Prepare for ATA command block.
  55. //
  56. ZeroMem (&Acb, sizeof (Acb));
  57. ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));
  58. Acb.AtaCommand = ATA_SECURITY_UNLOCK_CMD;
  59. Acb.AtaDeviceHead = (UINT8)(PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));
  60. //
  61. // Prepare for ATA pass through packet.
  62. //
  63. ZeroMem (&Packet, sizeof (Packet));
  64. Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_PIO_DATA_OUT;
  65. Packet.Length = EFI_ATA_PASS_THRU_LENGTH_BYTES;
  66. Packet.Asb = Asb;
  67. Packet.Acb = &Acb;
  68. ((CHAR16 *)Buffer)[0] = Identifier & BIT0;
  69. CopyMem (&((CHAR16 *)Buffer)[1], Password, HDD_PASSWORD_MAX_LENGTH);
  70. Packet.OutDataBuffer = Buffer;
  71. Packet.OutTransferLength = sizeof (Buffer);
  72. Packet.Timeout = ATA_TIMEOUT;
  73. Status = AtaPassThru->PassThru (
  74. AtaPassThru,
  75. Port,
  76. PortMultiplierPort,
  77. &Packet
  78. );
  79. if (!EFI_ERROR (Status) &&
  80. ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&
  81. ((Asb->AtaError & ATA_ERRREG_ABRT) != 0))
  82. {
  83. Status = EFI_DEVICE_ERROR;
  84. }
  85. FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
  86. ZeroMem (Buffer, sizeof (Buffer));
  87. DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));
  88. return Status;
  89. }
  90. /**
  91. Send security freeze lock cmd through ATA PassThru PPI.
  92. @param[in] AtaPassThru The pointer to the ATA PassThru PPI.
  93. @param[in] Port The port number of the ATA device.
  94. @param[in] PortMultiplierPort The port multiplier port number of the ATA device.
  95. @retval EFI_SUCCESS Successful to send security freeze lock cmd.
  96. @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
  97. @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.
  98. @retval EFI_DEVICE_ERROR Can not send security freeze lock cmd.
  99. **/
  100. EFI_STATUS
  101. FreezeLockDevice (
  102. IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,
  103. IN UINT16 Port,
  104. IN UINT16 PortMultiplierPort
  105. )
  106. {
  107. EFI_STATUS Status;
  108. EFI_ATA_COMMAND_BLOCK Acb;
  109. EFI_ATA_STATUS_BLOCK *Asb;
  110. EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;
  111. if (AtaPassThru == NULL) {
  112. return EFI_INVALID_PARAMETER;
  113. }
  114. //
  115. // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in
  116. // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by
  117. // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,
  118. // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it
  119. // may not be aligned when allocated on stack for some compilers. Hence, we
  120. // use the API AllocateAlignedPages to ensure this structure is properly
  121. // aligned.
  122. //
  123. Asb = AllocateAlignedPages (
  124. EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),
  125. AtaPassThru->Mode->IoAlign
  126. );
  127. if (Asb == NULL) {
  128. return EFI_OUT_OF_RESOURCES;
  129. }
  130. //
  131. // Prepare for ATA command block.
  132. //
  133. ZeroMem (&Acb, sizeof (Acb));
  134. ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));
  135. Acb.AtaCommand = ATA_SECURITY_FREEZE_LOCK_CMD;
  136. Acb.AtaDeviceHead = (UINT8)(PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));
  137. //
  138. // Prepare for ATA pass through packet.
  139. //
  140. ZeroMem (&Packet, sizeof (Packet));
  141. Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;
  142. Packet.Length = EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER;
  143. Packet.Asb = Asb;
  144. Packet.Acb = &Acb;
  145. Packet.Timeout = ATA_TIMEOUT;
  146. Status = AtaPassThru->PassThru (
  147. AtaPassThru,
  148. Port,
  149. PortMultiplierPort,
  150. &Packet
  151. );
  152. if (!EFI_ERROR (Status) &&
  153. ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&
  154. ((Asb->AtaError & ATA_ERRREG_ABRT) != 0))
  155. {
  156. Status = EFI_DEVICE_ERROR;
  157. }
  158. FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
  159. DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));
  160. return Status;
  161. }
  162. /**
  163. Unlock HDD password for S3.
  164. @param[in] AtaPassThruPpi Pointer to the EDKII_PEI_ATA_PASS_THRU_PPI instance.
  165. **/
  166. VOID
  167. UnlockHddPassword (
  168. IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThruPpi
  169. )
  170. {
  171. EFI_STATUS Status;
  172. VOID *Buffer;
  173. UINTN Length;
  174. UINT8 DummyData;
  175. HDD_PASSWORD_DEVICE_INFO *DevInfo;
  176. UINT16 Port;
  177. UINT16 PortMultiplierPort;
  178. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  179. UINTN DevicePathLength;
  180. //
  181. // Get HDD password device info from LockBox.
  182. //
  183. Buffer = (VOID *)&DummyData;
  184. Length = sizeof (DummyData);
  185. Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);
  186. if (Status == EFI_BUFFER_TOO_SMALL) {
  187. Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Length));
  188. if (Buffer != NULL) {
  189. Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);
  190. }
  191. }
  192. if ((Buffer == NULL) || (Buffer == (VOID *)&DummyData)) {
  193. return;
  194. } else if (EFI_ERROR (Status)) {
  195. FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));
  196. return;
  197. }
  198. Status = AtaPassThruPpi->GetDevicePath (AtaPassThruPpi, &DevicePathLength, &DevicePath);
  199. if (EFI_ERROR (Status) || (DevicePathLength <= sizeof (EFI_DEVICE_PATH_PROTOCOL))) {
  200. goto Exit;
  201. }
  202. //
  203. // Go through all the devices managed by the AtaPassThru PPI instance.
  204. //
  205. Port = 0xFFFF;
  206. while (TRUE) {
  207. Status = AtaPassThruPpi->GetNextPort (AtaPassThruPpi, &Port);
  208. if (EFI_ERROR (Status)) {
  209. //
  210. // We cannot find more legal port then we are done.
  211. //
  212. break;
  213. }
  214. PortMultiplierPort = 0xFFFF;
  215. while (TRUE) {
  216. Status = AtaPassThruPpi->GetNextDevice (AtaPassThruPpi, Port, &PortMultiplierPort);
  217. if (EFI_ERROR (Status)) {
  218. //
  219. // We cannot find more legal port multiplier port number for ATA device
  220. // on the port, then we are done.
  221. //
  222. break;
  223. }
  224. //
  225. // Search the device in the restored LockBox.
  226. //
  227. DevInfo = (HDD_PASSWORD_DEVICE_INFO *)Buffer;
  228. while ((UINTN)DevInfo < ((UINTN)Buffer + Length)) {
  229. //
  230. // Find the matching device.
  231. //
  232. if ((DevInfo->Device.Port == Port) &&
  233. (DevInfo->Device.PortMultiplierPort == PortMultiplierPort) &&
  234. (DevInfo->DevicePathLength >= DevicePathLength) &&
  235. (CompareMem (
  236. DevInfo->DevicePath,
  237. DevicePath,
  238. DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)
  239. ) == 0))
  240. {
  241. //
  242. // If device locked, unlock first.
  243. //
  244. if (!IsZeroBuffer (DevInfo->Password, HDD_PASSWORD_MAX_LENGTH)) {
  245. UnlockDevice (AtaPassThruPpi, Port, PortMultiplierPort, 0, DevInfo->Password);
  246. }
  247. //
  248. // Freeze lock the device.
  249. //
  250. FreezeLockDevice (AtaPassThruPpi, Port, PortMultiplierPort);
  251. break;
  252. }
  253. DevInfo = (HDD_PASSWORD_DEVICE_INFO *)
  254. ((UINTN)DevInfo + sizeof (HDD_PASSWORD_DEVICE_INFO) + DevInfo->DevicePathLength);
  255. }
  256. }
  257. }
  258. Exit:
  259. ZeroMem (Buffer, Length);
  260. FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));
  261. }
  262. /**
  263. Entry point of the notification callback function itself within the PEIM.
  264. It is to unlock HDD password for S3.
  265. @param PeiServices Indirect reference to the PEI Services Table.
  266. @param NotifyDescriptor Address of the notification descriptor data structure.
  267. @param Ppi Address of the PPI that was installed.
  268. @return Status of the notification.
  269. The status code returned from this function is ignored.
  270. **/
  271. EFI_STATUS
  272. EFIAPI
  273. HddPasswordAtaPassThruNotify (
  274. IN EFI_PEI_SERVICES **PeiServices,
  275. IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
  276. IN VOID *Ppi
  277. )
  278. {
  279. DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__));
  280. UnlockHddPassword ((EDKII_PEI_ATA_PASS_THRU_PPI *)Ppi);
  281. DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__));
  282. return EFI_SUCCESS;
  283. }
  284. EFI_PEI_NOTIFY_DESCRIPTOR mHddPasswordAtaPassThruPpiNotifyDesc = {
  285. (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
  286. &gEdkiiPeiAtaPassThruPpiGuid,
  287. HddPasswordAtaPassThruNotify
  288. };
  289. /**
  290. Main entry for this module.
  291. @param FileHandle Handle of the file being invoked.
  292. @param PeiServices Pointer to PEI Services table.
  293. @return Status from PeiServicesNotifyPpi.
  294. **/
  295. EFI_STATUS
  296. EFIAPI
  297. HddPasswordPeiInit (
  298. IN EFI_PEI_FILE_HANDLE FileHandle,
  299. IN CONST EFI_PEI_SERVICES **PeiServices
  300. )
  301. {
  302. EFI_STATUS Status;
  303. EFI_BOOT_MODE BootMode;
  304. Status = PeiServicesGetBootMode (&BootMode);
  305. if ((EFI_ERROR (Status)) || (BootMode != BOOT_ON_S3_RESUME)) {
  306. return EFI_UNSUPPORTED;
  307. }
  308. DEBUG ((DEBUG_INFO, "%a: Enters in S3 path.\n", __FUNCTION__));
  309. Status = PeiServicesNotifyPpi (&mHddPasswordAtaPassThruPpiNotifyDesc);
  310. ASSERT_EFI_ERROR (Status);
  311. return Status;
  312. }