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