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