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