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