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