HddPasswordPei.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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. Status = EFI_DEVICE_ERROR;
  83. }
  84. FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
  85. ZeroMem (Buffer, sizeof (Buffer));
  86. DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));
  87. return Status;
  88. }
  89. /**
  90. Send security freeze lock cmd through ATA PassThru PPI.
  91. @param[in] AtaPassThru The pointer to the ATA PassThru PPI.
  92. @param[in] Port The port number of the ATA device.
  93. @param[in] PortMultiplierPort The port multiplier port number of the ATA device.
  94. @retval EFI_SUCCESS Successful to send security freeze lock cmd.
  95. @retval EFI_INVALID_PARAMETER The parameter passed-in is invalid.
  96. @retval EFI_OUT_OF_RESOURCES Not enough memory to send unlock hdd password cmd.
  97. @retval EFI_DEVICE_ERROR Can not send security freeze lock cmd.
  98. **/
  99. EFI_STATUS
  100. FreezeLockDevice (
  101. IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThru,
  102. IN UINT16 Port,
  103. IN UINT16 PortMultiplierPort
  104. )
  105. {
  106. EFI_STATUS Status;
  107. EFI_ATA_COMMAND_BLOCK Acb;
  108. EFI_ATA_STATUS_BLOCK *Asb;
  109. EFI_ATA_PASS_THRU_COMMAND_PACKET Packet;
  110. if (AtaPassThru == NULL) {
  111. return EFI_INVALID_PARAMETER;
  112. }
  113. //
  114. // The 'Asb' field (a pointer to the EFI_ATA_STATUS_BLOCK structure) in
  115. // EFI_ATA_PASS_THRU_COMMAND_PACKET is required to be aligned specified by
  116. // the 'IoAlign' field in the EFI_ATA_PASS_THRU_MODE structure. Meanwhile,
  117. // the structure EFI_ATA_STATUS_BLOCK is composed of only UINT8 fields, so it
  118. // may not be aligned when allocated on stack for some compilers. Hence, we
  119. // use the API AllocateAlignedPages to ensure this structure is properly
  120. // aligned.
  121. //
  122. Asb = AllocateAlignedPages (
  123. EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)),
  124. AtaPassThru->Mode->IoAlign
  125. );
  126. if (Asb == NULL) {
  127. return EFI_OUT_OF_RESOURCES;
  128. }
  129. //
  130. // Prepare for ATA command block.
  131. //
  132. ZeroMem (&Acb, sizeof (Acb));
  133. ZeroMem (Asb, sizeof (EFI_ATA_STATUS_BLOCK));
  134. Acb.AtaCommand = ATA_SECURITY_FREEZE_LOCK_CMD;
  135. Acb.AtaDeviceHead = (UINT8) (PortMultiplierPort == 0xFFFF ? 0 : (PortMultiplierPort << 4));
  136. //
  137. // Prepare for ATA pass through packet.
  138. //
  139. ZeroMem (&Packet, sizeof (Packet));
  140. Packet.Protocol = EFI_ATA_PASS_THRU_PROTOCOL_ATA_NON_DATA;
  141. Packet.Length = EFI_ATA_PASS_THRU_LENGTH_NO_DATA_TRANSFER;
  142. Packet.Asb = Asb;
  143. Packet.Acb = &Acb;
  144. Packet.Timeout = ATA_TIMEOUT;
  145. Status = AtaPassThru->PassThru (
  146. AtaPassThru,
  147. Port,
  148. PortMultiplierPort,
  149. &Packet
  150. );
  151. if (!EFI_ERROR (Status) &&
  152. ((Asb->AtaStatus & ATA_STSREG_ERR) != 0) &&
  153. ((Asb->AtaError & ATA_ERRREG_ABRT) != 0)) {
  154. Status = EFI_DEVICE_ERROR;
  155. }
  156. FreeAlignedPages (Asb, EFI_SIZE_TO_PAGES (sizeof (EFI_ATA_STATUS_BLOCK)));
  157. DEBUG ((DEBUG_INFO, "%a() - %r\n", __FUNCTION__, Status));
  158. return Status;
  159. }
  160. /**
  161. Unlock HDD password for S3.
  162. @param[in] AtaPassThruPpi Pointer to the EDKII_PEI_ATA_PASS_THRU_PPI instance.
  163. **/
  164. VOID
  165. UnlockHddPassword (
  166. IN EDKII_PEI_ATA_PASS_THRU_PPI *AtaPassThruPpi
  167. )
  168. {
  169. EFI_STATUS Status;
  170. VOID *Buffer;
  171. UINTN Length;
  172. UINT8 DummyData;
  173. HDD_PASSWORD_DEVICE_INFO *DevInfo;
  174. UINT16 Port;
  175. UINT16 PortMultiplierPort;
  176. EFI_DEVICE_PATH_PROTOCOL *DevicePath;
  177. UINTN DevicePathLength;
  178. //
  179. // Get HDD password device info from LockBox.
  180. //
  181. Buffer = (VOID *) &DummyData;
  182. Length = sizeof (DummyData);
  183. Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);
  184. if (Status == EFI_BUFFER_TOO_SMALL) {
  185. Buffer = AllocatePages (EFI_SIZE_TO_PAGES (Length));
  186. if (Buffer != NULL) {
  187. Status = RestoreLockBox (&mHddPasswordDeviceInfoGuid, Buffer, &Length);
  188. }
  189. }
  190. if ((Buffer == NULL) || (Buffer == (VOID *) &DummyData)) {
  191. return;
  192. } else if (EFI_ERROR (Status)) {
  193. FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));
  194. return;
  195. }
  196. Status = AtaPassThruPpi->GetDevicePath (AtaPassThruPpi, &DevicePathLength, &DevicePath);
  197. if (EFI_ERROR (Status) || (DevicePathLength <= sizeof (EFI_DEVICE_PATH_PROTOCOL))) {
  198. goto Exit;
  199. }
  200. //
  201. // Go through all the devices managed by the AtaPassThru PPI instance.
  202. //
  203. Port = 0xFFFF;
  204. while (TRUE) {
  205. Status = AtaPassThruPpi->GetNextPort (AtaPassThruPpi, &Port);
  206. if (EFI_ERROR (Status)) {
  207. //
  208. // We cannot find more legal port then we are done.
  209. //
  210. break;
  211. }
  212. PortMultiplierPort = 0xFFFF;
  213. while (TRUE) {
  214. Status = AtaPassThruPpi->GetNextDevice (AtaPassThruPpi, Port, &PortMultiplierPort);
  215. if (EFI_ERROR (Status)) {
  216. //
  217. // We cannot find more legal port multiplier port number for ATA device
  218. // on the port, then we are done.
  219. //
  220. break;
  221. }
  222. //
  223. // Search the device in the restored LockBox.
  224. //
  225. DevInfo = (HDD_PASSWORD_DEVICE_INFO *) Buffer;
  226. while ((UINTN) DevInfo < ((UINTN) Buffer + Length)) {
  227. //
  228. // Find the matching device.
  229. //
  230. if ((DevInfo->Device.Port == Port) &&
  231. (DevInfo->Device.PortMultiplierPort == PortMultiplierPort) &&
  232. (DevInfo->DevicePathLength >= DevicePathLength) &&
  233. (CompareMem (
  234. DevInfo->DevicePath,
  235. DevicePath,
  236. DevicePathLength - sizeof (EFI_DEVICE_PATH_PROTOCOL)) == 0)) {
  237. //
  238. // If device locked, unlock first.
  239. //
  240. if (!IsZeroBuffer (DevInfo->Password, HDD_PASSWORD_MAX_LENGTH)) {
  241. UnlockDevice (AtaPassThruPpi, Port, PortMultiplierPort, 0, DevInfo->Password);
  242. }
  243. //
  244. // Freeze lock the device.
  245. //
  246. FreezeLockDevice (AtaPassThruPpi, Port, PortMultiplierPort);
  247. break;
  248. }
  249. DevInfo = (HDD_PASSWORD_DEVICE_INFO *)
  250. ((UINTN) DevInfo + sizeof (HDD_PASSWORD_DEVICE_INFO) + DevInfo->DevicePathLength);
  251. }
  252. }
  253. }
  254. Exit:
  255. ZeroMem (Buffer, Length);
  256. FreePages (Buffer, EFI_SIZE_TO_PAGES (Length));
  257. }
  258. /**
  259. Entry point of the notification callback function itself within the PEIM.
  260. It is to unlock HDD password for S3.
  261. @param PeiServices Indirect reference to the PEI Services Table.
  262. @param NotifyDescriptor Address of the notification descriptor data structure.
  263. @param Ppi Address of the PPI that was installed.
  264. @return Status of the notification.
  265. The status code returned from this function is ignored.
  266. **/
  267. EFI_STATUS
  268. EFIAPI
  269. HddPasswordAtaPassThruNotify (
  270. IN EFI_PEI_SERVICES **PeiServices,
  271. IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
  272. IN VOID *Ppi
  273. )
  274. {
  275. DEBUG ((DEBUG_INFO, "%a() - enter at S3 resume\n", __FUNCTION__));
  276. UnlockHddPassword ((EDKII_PEI_ATA_PASS_THRU_PPI *) Ppi);
  277. DEBUG ((DEBUG_INFO, "%a() - exit at S3 resume\n", __FUNCTION__));
  278. return EFI_SUCCESS;
  279. }
  280. EFI_PEI_NOTIFY_DESCRIPTOR mHddPasswordAtaPassThruPpiNotifyDesc = {
  281. (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
  282. &gEdkiiPeiAtaPassThruPpiGuid,
  283. HddPasswordAtaPassThruNotify
  284. };
  285. /**
  286. Main entry for this module.
  287. @param FileHandle Handle of the file being invoked.
  288. @param PeiServices Pointer to PEI Services table.
  289. @return Status from PeiServicesNotifyPpi.
  290. **/
  291. EFI_STATUS
  292. EFIAPI
  293. HddPasswordPeiInit (
  294. IN EFI_PEI_FILE_HANDLE FileHandle,
  295. IN CONST EFI_PEI_SERVICES **PeiServices
  296. )
  297. {
  298. EFI_STATUS Status;
  299. EFI_BOOT_MODE BootMode;
  300. Status = PeiServicesGetBootMode (&BootMode);
  301. if ((EFI_ERROR (Status)) || (BootMode != BOOT_ON_S3_RESUME)) {
  302. return EFI_UNSUPPORTED;
  303. }
  304. DEBUG ((DEBUG_INFO, "%a: Enters in S3 path.\n", __FUNCTION__));
  305. Status = PeiServicesNotifyPpi (&mHddPasswordAtaPassThruPpiNotifyDesc);
  306. ASSERT_EFI_ERROR (Status);
  307. return Status;
  308. }