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