]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
MdeModulePkg/NvmExpressPei: Consume S3StorageDeviceInitList LockBox
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressPei / NvmExpressPei.c
1 /** @file
2 The NvmExpressPei driver is used to manage non-volatile memory subsystem
3 which follows NVM Express specification at PEI phase.
4
5 Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions
9 of the BSD License which accompanies this distribution. The
10 full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16 **/
17
18 #include "NvmExpressPei.h"
19
20 EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIoPpiListTemplate = {
21 EFI_PEI_PPI_DESCRIPTOR_PPI,
22 &gEfiPeiVirtualBlockIoPpiGuid,
23 NULL
24 };
25
26 EFI_PEI_PPI_DESCRIPTOR mNvmeBlkIo2PpiListTemplate = {
27 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
28 &gEfiPeiVirtualBlockIo2PpiGuid,
29 NULL
30 };
31
32 EFI_PEI_PPI_DESCRIPTOR mNvmeStorageSecurityPpiListTemplate = {
33 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
34 &gEdkiiPeiStorageSecurityCommandPpiGuid,
35 NULL
36 };
37
38 EFI_PEI_NOTIFY_DESCRIPTOR mNvmeEndOfPeiNotifyListTemplate = {
39 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
40 &gEfiEndOfPeiSignalPpiGuid,
41 NvmePeimEndOfPei
42 };
43
44 /**
45 Check if the specified Nvm Express device namespace is active, and then get the Identify
46 Namespace data.
47
48 @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
49 @param[in] NamespaceId The specified namespace identifier.
50
51 @retval EFI_SUCCESS The specified namespace in the device is successfully enumerated.
52 @return Others Error occurs when enumerating the namespace.
53
54 **/
55 EFI_STATUS
56 EnumerateNvmeDevNamespace (
57 IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private,
58 IN UINT32 NamespaceId
59 )
60 {
61 EFI_STATUS Status;
62 NVME_ADMIN_NAMESPACE_DATA *NamespaceData;
63 PEI_NVME_NAMESPACE_INFO *NamespaceInfo;
64 UINT32 DeviceIndex;
65 UINT32 Lbads;
66 UINT32 Flbas;
67 UINT32 LbaFmtIdx;
68
69 NamespaceData = (NVME_ADMIN_NAMESPACE_DATA *) AllocateZeroPool (sizeof (NVME_ADMIN_NAMESPACE_DATA));
70 if (NamespaceData == NULL) {
71 return EFI_OUT_OF_RESOURCES;
72 }
73
74 //
75 // Identify Namespace
76 //
77 Status = NvmeIdentifyNamespace (
78 Private,
79 NamespaceId,
80 NamespaceData
81 );
82 if (EFI_ERROR (Status)) {
83 DEBUG ((DEBUG_ERROR, "%a: NvmeIdentifyNamespace fail, Status - %r\n", __FUNCTION__, Status));
84 goto Exit;
85 }
86
87 //
88 // Validate Namespace
89 //
90 if (NamespaceData->Ncap == 0) {
91 DEBUG ((DEBUG_INFO, "%a: Namespace ID %d is an inactive one.\n", __FUNCTION__, NamespaceId));
92 Status = EFI_DEVICE_ERROR;
93 goto Exit;
94 }
95
96 DeviceIndex = Private->ActiveNamespaceNum;
97 NamespaceInfo = &Private->NamespaceInfo[DeviceIndex];
98 NamespaceInfo->NamespaceId = NamespaceId;
99 NamespaceInfo->NamespaceUuid = NamespaceData->Eui64;
100 NamespaceInfo->Controller = Private;
101 Private->ActiveNamespaceNum++;
102
103 //
104 // Build BlockIo media structure
105 //
106 Flbas = NamespaceData->Flbas;
107 LbaFmtIdx = Flbas & 0xF;
108 Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;
109
110 NamespaceInfo->Media.InterfaceType = MSG_NVME_NAMESPACE_DP;
111 NamespaceInfo->Media.RemovableMedia = FALSE;
112 NamespaceInfo->Media.MediaPresent = TRUE;
113 NamespaceInfo->Media.ReadOnly = FALSE;
114 NamespaceInfo->Media.BlockSize = (UINT32) 1 << Lbads;
115 NamespaceInfo->Media.LastBlock = (EFI_PEI_LBA) NamespaceData->Nsze - 1;
116 DEBUG ((
117 DEBUG_INFO,
118 "%a: Namespace ID %d - BlockSize = 0x%x, LastBlock = 0x%lx\n",
119 __FUNCTION__,
120 NamespaceId,
121 NamespaceInfo->Media.BlockSize,
122 NamespaceInfo->Media.LastBlock
123 ));
124
125 Exit:
126 if (NamespaceData != NULL) {
127 FreePool (NamespaceData);
128 }
129
130 return Status;
131 }
132
133 /**
134 Discover all Nvm Express device active namespaces.
135
136 @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
137
138 @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.
139 @return EFI_NOT_FOUND No active namespaces can be found.
140
141 **/
142 EFI_STATUS
143 NvmeDiscoverNamespaces (
144 IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
145 )
146 {
147 UINT32 NamespaceId;
148
149 Private->ActiveNamespaceNum = 0;
150 Private->NamespaceInfo = AllocateZeroPool (Private->ControllerData->Nn * sizeof (PEI_NVME_NAMESPACE_INFO));
151
152 //
153 // According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of the identify
154 // controller data defines the number of valid namespaces present for the
155 // controller. Namespaces shall be allocated in order (starting with 1) and
156 // packed sequentially.
157 //
158 for (NamespaceId = 1; NamespaceId <= Private->ControllerData->Nn; NamespaceId++) {
159 //
160 // For now, we do not care the return status. Since if a valid namespace is inactive,
161 // error status will be returned. But we continue to enumerate other valid namespaces.
162 //
163 EnumerateNvmeDevNamespace (Private, NamespaceId);
164 }
165 if (Private->ActiveNamespaceNum == 0) {
166 return EFI_NOT_FOUND;
167 }
168
169 return EFI_SUCCESS;
170 }
171
172 /**
173 One notified function to cleanup the allocated resources at the end of PEI.
174
175 @param[in] PeiServices Pointer to PEI Services Table.
176 @param[in] NotifyDescriptor Pointer to the descriptor for the Notification
177 event that caused this function to execute.
178 @param[in] Ppi Pointer to the PPI data associated with this function.
179
180 @retval EFI_SUCCESS The function completes successfully
181
182 **/
183 EFI_STATUS
184 EFIAPI
185 NvmePeimEndOfPei (
186 IN EFI_PEI_SERVICES **PeiServices,
187 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
188 IN VOID *Ppi
189 )
190 {
191 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
192
193 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
194 NvmeFreeDmaResource (Private);
195
196 return EFI_SUCCESS;
197 }
198
199 /**
200 Entry point of the PEIM.
201
202 @param[in] FileHandle Handle of the file being invoked.
203 @param[in] PeiServices Describes the list of possible PEI Services.
204
205 @retval EFI_SUCCESS PPI successfully installed.
206
207 **/
208 EFI_STATUS
209 EFIAPI
210 NvmExpressPeimEntry (
211 IN EFI_PEI_FILE_HANDLE FileHandle,
212 IN CONST EFI_PEI_SERVICES **PeiServices
213 )
214 {
215 EFI_STATUS Status;
216 EFI_BOOT_MODE BootMode;
217 EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;
218 UINT8 Controller;
219 UINTN MmioBase;
220 UINTN DevicePathLength;
221 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
222 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
223 EFI_PHYSICAL_ADDRESS DeviceAddress;
224
225 DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
226
227 //
228 // Get the current boot mode.
229 //
230 Status = PeiServicesGetBootMode (&BootMode);
231 if (EFI_ERROR (Status)) {
232 DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
233 return Status;
234 }
235
236 //
237 // Locate the NVME host controller PPI
238 //
239 Status = PeiServicesLocatePpi (
240 &gEdkiiPeiNvmExpressHostControllerPpiGuid,
241 0,
242 NULL,
243 (VOID **) &NvmeHcPpi
244 );
245 if (EFI_ERROR (Status)) {
246 DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));
247 return EFI_UNSUPPORTED;
248 }
249
250 Controller = 0;
251 MmioBase = 0;
252 while (TRUE) {
253 Status = NvmeHcPpi->GetNvmeHcMmioBar (
254 NvmeHcPpi,
255 Controller,
256 &MmioBase
257 );
258 //
259 // When status is error, meant no controller is found
260 //
261 if (EFI_ERROR (Status)) {
262 break;
263 }
264
265 Status = NvmeHcPpi->GetNvmeHcDevicePath (
266 NvmeHcPpi,
267 Controller,
268 &DevicePathLength,
269 &DevicePath
270 );
271 if (EFI_ERROR (Status)) {
272 DEBUG ((
273 DEBUG_ERROR, "%a: Fail to allocate get the device path for Controller %d.\n",
274 __FUNCTION__, Controller
275 ));
276 return Status;
277 }
278
279 //
280 // Check validity of the device path of the NVM Express controller.
281 //
282 Status = NvmeIsHcDevicePathValid (DevicePath, DevicePathLength);
283 if (EFI_ERROR (Status)) {
284 DEBUG ((
285 DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",
286 __FUNCTION__, Controller
287 ));
288 Controller++;
289 continue;
290 }
291
292 //
293 // For S3 resume performance consideration, not all NVM Express controllers
294 // will be initialized. The driver consumes the content within
295 // S3StorageDeviceInitList LockBox to see if a controller will be skipped
296 // during S3 resume.
297 //
298 if ((BootMode == BOOT_ON_S3_RESUME) &&
299 (NvmeS3SkipThisController (DevicePath, DevicePathLength))) {
300 DEBUG ((
301 DEBUG_ERROR, "%a: Controller %d is skipped during S3.\n",
302 __FUNCTION__, Controller
303 ));
304 Controller++;
305 continue;
306 }
307
308 //
309 // Memory allocation for controller private data
310 //
311 Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));
312 if (Private == NULL) {
313 DEBUG ((
314 DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",
315 __FUNCTION__, Controller
316 ));
317 return EFI_OUT_OF_RESOURCES;
318 }
319
320 //
321 // Memory allocation for transfer-related data
322 //
323 Status = IoMmuAllocateBuffer (
324 NVME_MEM_MAX_PAGES,
325 &Private->Buffer,
326 &DeviceAddress,
327 &Private->BufferMapping
328 );
329 if (EFI_ERROR (Status)) {
330 DEBUG ((
331 DEBUG_ERROR, "%a: Fail to allocate DMA buffers for Controller %d.\n",
332 __FUNCTION__, Controller
333 ));
334 return Status;
335 }
336 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private->Buffer));
337 DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));
338
339 //
340 // Initialize controller private data
341 //
342 Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
343 Private->MmioBase = MmioBase;
344 Private->DevicePathLength = DevicePathLength;
345 Private->DevicePath = DevicePath;
346
347 //
348 // Initialize the NVME controller
349 //
350 Status = NvmeControllerInit (Private);
351 if (EFI_ERROR (Status)) {
352 DEBUG ((
353 DEBUG_ERROR,
354 "%a: Controller initialization fail for Controller %d with Status - %r.\n",
355 __FUNCTION__, Controller, Status
356 ));
357 NvmeFreeDmaResource (Private);
358 Controller++;
359 continue;
360 }
361
362 //
363 // Enumerate the NVME namespaces on the controller
364 //
365 Status = NvmeDiscoverNamespaces (Private);
366 if (EFI_ERROR (Status)) {
367 //
368 // No active namespace was found on the controller
369 //
370 DEBUG ((
371 DEBUG_ERROR,
372 "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
373 __FUNCTION__, Controller, Status
374 ));
375 NvmeFreeDmaResource (Private);
376 Controller++;
377 continue;
378 }
379
380 Private->BlkIoPpi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo;
381 Private->BlkIoPpi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo;
382 Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
383 CopyMem (
384 &Private->BlkIoPpiList,
385 &mNvmeBlkIoPpiListTemplate,
386 sizeof (EFI_PEI_PPI_DESCRIPTOR)
387 );
388 Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
389
390 Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
391 Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;
392 Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;
393 Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
394 CopyMem (
395 &Private->BlkIo2PpiList,
396 &mNvmeBlkIo2PpiListTemplate,
397 sizeof (EFI_PEI_PPI_DESCRIPTOR)
398 );
399 Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
400 PeiServicesInstallPpi (&Private->BlkIoPpiList);
401
402 //
403 // Check if the NVME controller supports the Security Receive/Send commands
404 //
405 if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
406 DEBUG ((
407 DEBUG_INFO,
408 "%a: Security Security Command PPI will be produced for Controller %d.\n",
409 __FUNCTION__, Controller
410 ));
411 Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION;
412 Private->StorageSecurityPpi.GetNumberofDevices = NvmeStorageSecurityGetDeviceNo;
413 Private->StorageSecurityPpi.GetDevicePath = NvmeStorageSecurityGetDevicePath;
414 Private->StorageSecurityPpi.ReceiveData = NvmeStorageSecurityReceiveData;
415 Private->StorageSecurityPpi.SendData = NvmeStorageSecuritySendData;
416 CopyMem (
417 &Private->StorageSecurityPpiList,
418 &mNvmeStorageSecurityPpiListTemplate,
419 sizeof (EFI_PEI_PPI_DESCRIPTOR)
420 );
421 Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;
422 PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
423 }
424
425 CopyMem (
426 &Private->EndOfPeiNotifyList,
427 &mNvmeEndOfPeiNotifyListTemplate,
428 sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
429 );
430 PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
431
432 DEBUG ((
433 DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",
434 __FUNCTION__, Controller
435 ));
436 Controller++;
437 }
438
439 return EFI_SUCCESS;
440 }