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