]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
MdeModulePkg: Add a check for metadata size in NvmExpress Driver
[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
108 //
109 // Currently this NVME driver only suport Metadata Size == 0
110 //
111 if (NamespaceData->LbaFormat[LbaFmtIdx].Ms != 0) {
112 DEBUG ((
113 DEBUG_ERROR,
114 "NVME IDENTIFY NAMESPACE [%d] Ms(%d) is not supported.\n",
115 NamespaceId,
116 NamespaceData->LbaFormat[LbaFmtIdx].Ms
117 ));
118 Status = EFI_UNSUPPORTED;
119 goto Exit;
120 }
121
122 Lbads = NamespaceData->LbaFormat[LbaFmtIdx].Lbads;
123
124 NamespaceInfo->Media.InterfaceType = MSG_NVME_NAMESPACE_DP;
125 NamespaceInfo->Media.RemovableMedia = FALSE;
126 NamespaceInfo->Media.MediaPresent = TRUE;
127 NamespaceInfo->Media.ReadOnly = FALSE;
128 NamespaceInfo->Media.BlockSize = (UINT32)1 << Lbads;
129 NamespaceInfo->Media.LastBlock = (EFI_PEI_LBA)NamespaceData->Nsze - 1;
130 DEBUG ((
131 DEBUG_INFO,
132 "%a: Namespace ID %d - BlockSize = 0x%x, LastBlock = 0x%lx\n",
133 __FUNCTION__,
134 NamespaceId,
135 NamespaceInfo->Media.BlockSize,
136 NamespaceInfo->Media.LastBlock
137 ));
138
139 Exit:
140 if (NamespaceData != NULL) {
141 FreePool (NamespaceData);
142 }
143
144 return Status;
145 }
146
147 /**
148 Discover all Nvm Express device active namespaces.
149
150 @param[in,out] Private The pointer to the PEI_NVME_CONTROLLER_PRIVATE_DATA data structure.
151
152 @retval EFI_SUCCESS All the namespaces in the device are successfully enumerated.
153 @return EFI_NOT_FOUND No active namespaces can be found.
154
155 **/
156 EFI_STATUS
157 NvmeDiscoverNamespaces (
158 IN OUT PEI_NVME_CONTROLLER_PRIVATE_DATA *Private
159 )
160 {
161 UINT32 NamespaceId;
162
163 Private->ActiveNamespaceNum = 0;
164 Private->NamespaceInfo = AllocateZeroPool (Private->ControllerData->Nn * sizeof (PEI_NVME_NAMESPACE_INFO));
165
166 //
167 // According to Nvm Express 1.1 spec Figure 82, the field 'Nn' of the identify
168 // controller data defines the number of valid namespaces present for the
169 // controller. Namespaces shall be allocated in order (starting with 1) and
170 // packed sequentially.
171 //
172 for (NamespaceId = 1; NamespaceId <= Private->ControllerData->Nn; NamespaceId++) {
173 //
174 // For now, we do not care the return status. Since if a valid namespace is inactive,
175 // error status will be returned. But we continue to enumerate other valid namespaces.
176 //
177 EnumerateNvmeDevNamespace (Private, NamespaceId);
178 }
179
180 if (Private->ActiveNamespaceNum == 0) {
181 return EFI_NOT_FOUND;
182 }
183
184 return EFI_SUCCESS;
185 }
186
187 /**
188 One notified function to cleanup the allocated resources at the end of PEI.
189
190 @param[in] PeiServices Pointer to PEI Services Table.
191 @param[in] NotifyDescriptor Pointer to the descriptor for the Notification
192 event that caused this function to execute.
193 @param[in] Ppi Pointer to the PPI data associated with this function.
194
195 @retval EFI_SUCCESS The function completes successfully
196
197 **/
198 EFI_STATUS
199 EFIAPI
200 NvmePeimEndOfPei (
201 IN EFI_PEI_SERVICES **PeiServices,
202 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
203 IN VOID *Ppi
204 )
205 {
206 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
207
208 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_NOTIFY (NotifyDescriptor);
209 NvmeFreeDmaResource (Private);
210
211 return EFI_SUCCESS;
212 }
213
214 /**
215 Entry point of the PEIM.
216
217 @param[in] FileHandle Handle of the file being invoked.
218 @param[in] PeiServices Describes the list of possible PEI Services.
219
220 @retval EFI_SUCCESS PPI successfully installed.
221
222 **/
223 EFI_STATUS
224 EFIAPI
225 NvmExpressPeimEntry (
226 IN EFI_PEI_FILE_HANDLE FileHandle,
227 IN CONST EFI_PEI_SERVICES **PeiServices
228 )
229 {
230 EFI_STATUS Status;
231 EFI_BOOT_MODE BootMode;
232 EDKII_NVM_EXPRESS_HOST_CONTROLLER_PPI *NvmeHcPpi;
233 UINT8 Controller;
234 UINTN MmioBase;
235 UINTN DevicePathLength;
236 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
237 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;
238 EFI_PHYSICAL_ADDRESS DeviceAddress;
239
240 DEBUG ((DEBUG_INFO, "%a: Enters.\n", __FUNCTION__));
241
242 //
243 // Get the current boot mode.
244 //
245 Status = PeiServicesGetBootMode (&BootMode);
246 if (EFI_ERROR (Status)) {
247 DEBUG ((DEBUG_ERROR, "%a: Fail to get the current boot mode.\n", __FUNCTION__));
248 return Status;
249 }
250
251 //
252 // Locate the NVME host controller PPI
253 //
254 Status = PeiServicesLocatePpi (
255 &gEdkiiPeiNvmExpressHostControllerPpiGuid,
256 0,
257 NULL,
258 (VOID **)&NvmeHcPpi
259 );
260 if (EFI_ERROR (Status)) {
261 DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));
262 return EFI_UNSUPPORTED;
263 }
264
265 Controller = 0;
266 MmioBase = 0;
267 while (TRUE) {
268 Status = NvmeHcPpi->GetNvmeHcMmioBar (
269 NvmeHcPpi,
270 Controller,
271 &MmioBase
272 );
273 //
274 // When status is error, meant no controller is found
275 //
276 if (EFI_ERROR (Status)) {
277 break;
278 }
279
280 Status = NvmeHcPpi->GetNvmeHcDevicePath (
281 NvmeHcPpi,
282 Controller,
283 &DevicePathLength,
284 &DevicePath
285 );
286 if (EFI_ERROR (Status)) {
287 DEBUG ((
288 DEBUG_ERROR,
289 "%a: Fail to allocate get the device path for Controller %d.\n",
290 __FUNCTION__,
291 Controller
292 ));
293 return Status;
294 }
295
296 //
297 // Check validity of the device path of the NVM Express controller.
298 //
299 Status = NvmeIsHcDevicePathValid (DevicePath, DevicePathLength);
300 if (EFI_ERROR (Status)) {
301 DEBUG ((
302 DEBUG_ERROR,
303 "%a: The device path is invalid for Controller %d.\n",
304 __FUNCTION__,
305 Controller
306 ));
307 Controller++;
308 continue;
309 }
310
311 //
312 // For S3 resume performance consideration, not all NVM Express controllers
313 // will be initialized. The driver consumes the content within
314 // S3StorageDeviceInitList LockBox to see if a controller will be skipped
315 // during S3 resume.
316 //
317 if ((BootMode == BOOT_ON_S3_RESUME) &&
318 (NvmeS3SkipThisController (DevicePath, DevicePathLength)))
319 {
320 DEBUG ((
321 DEBUG_ERROR,
322 "%a: Controller %d is skipped during S3.\n",
323 __FUNCTION__,
324 Controller
325 ));
326 Controller++;
327 continue;
328 }
329
330 //
331 // Memory allocation for controller private data
332 //
333 Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));
334 if (Private == NULL) {
335 DEBUG ((
336 DEBUG_ERROR,
337 "%a: Fail to allocate private data for Controller %d.\n",
338 __FUNCTION__,
339 Controller
340 ));
341 return EFI_OUT_OF_RESOURCES;
342 }
343
344 //
345 // Memory allocation for transfer-related data
346 //
347 Status = IoMmuAllocateBuffer (
348 NVME_MEM_MAX_PAGES,
349 &Private->Buffer,
350 &DeviceAddress,
351 &Private->BufferMapping
352 );
353 if (EFI_ERROR (Status)) {
354 DEBUG ((
355 DEBUG_ERROR,
356 "%a: Fail to allocate DMA buffers for Controller %d.\n",
357 __FUNCTION__,
358 Controller
359 ));
360 return Status;
361 }
362
363 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS)(UINTN)Private->Buffer));
364 DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));
365
366 //
367 // Initialize controller private data
368 //
369 Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
370 Private->MmioBase = MmioBase;
371 Private->DevicePathLength = DevicePathLength;
372 Private->DevicePath = DevicePath;
373
374 //
375 // Initialize the NVME controller
376 //
377 Status = NvmeControllerInit (Private);
378 if (EFI_ERROR (Status)) {
379 DEBUG ((
380 DEBUG_ERROR,
381 "%a: Controller initialization fail for Controller %d with Status - %r.\n",
382 __FUNCTION__,
383 Controller,
384 Status
385 ));
386 NvmeFreeDmaResource (Private);
387 Controller++;
388 continue;
389 }
390
391 //
392 // Enumerate the NVME namespaces on the controller
393 //
394 Status = NvmeDiscoverNamespaces (Private);
395 if (EFI_ERROR (Status)) {
396 //
397 // No active namespace was found on the controller
398 //
399 DEBUG ((
400 DEBUG_ERROR,
401 "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
402 __FUNCTION__,
403 Controller,
404 Status
405 ));
406 NvmeFreeDmaResource (Private);
407 Controller++;
408 continue;
409 }
410
411 //
412 // Nvm Express Pass Thru PPI
413 //
414 Private->PassThruMode.Attributes = EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_PHYSICAL |
415 EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_LOGICAL |
416 EFI_NVM_EXPRESS_PASS_THRU_ATTRIBUTES_CMD_SET_NVM;
417 Private->PassThruMode.IoAlign = sizeof (UINTN);
418 Private->PassThruMode.NvmeVersion = EDKII_PEI_NVM_EXPRESS_PASS_THRU_PPI_REVISION;
419 Private->NvmePassThruPpi.Mode = &Private->PassThruMode;
420 Private->NvmePassThruPpi.GetDevicePath = NvmePassThruGetDevicePath;
421 Private->NvmePassThruPpi.GetNextNameSpace = NvmePassThruGetNextNameSpace;
422 Private->NvmePassThruPpi.PassThru = NvmePassThru;
423 CopyMem (
424 &Private->NvmePassThruPpiList,
425 &mNvmePassThruPpiListTemplate,
426 sizeof (EFI_PEI_PPI_DESCRIPTOR)
427 );
428 Private->NvmePassThruPpiList.Ppi = &Private->NvmePassThruPpi;
429 PeiServicesInstallPpi (&Private->NvmePassThruPpiList);
430
431 //
432 // Block Io PPI
433 //
434 Private->BlkIoPpi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo;
435 Private->BlkIoPpi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo;
436 Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
437 CopyMem (
438 &Private->BlkIoPpiList,
439 &mNvmeBlkIoPpiListTemplate,
440 sizeof (EFI_PEI_PPI_DESCRIPTOR)
441 );
442 Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
443
444 Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
445 Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;
446 Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;
447 Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
448 CopyMem (
449 &Private->BlkIo2PpiList,
450 &mNvmeBlkIo2PpiListTemplate,
451 sizeof (EFI_PEI_PPI_DESCRIPTOR)
452 );
453 Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
454 PeiServicesInstallPpi (&Private->BlkIoPpiList);
455
456 //
457 // Check if the NVME controller supports the Security Receive/Send commands
458 //
459 if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
460 DEBUG ((
461 DEBUG_INFO,
462 "%a: Security Security Command PPI will be produced for Controller %d.\n",
463 __FUNCTION__,
464 Controller
465 ));
466 Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION;
467 Private->StorageSecurityPpi.GetNumberofDevices = NvmeStorageSecurityGetDeviceNo;
468 Private->StorageSecurityPpi.GetDevicePath = NvmeStorageSecurityGetDevicePath;
469 Private->StorageSecurityPpi.ReceiveData = NvmeStorageSecurityReceiveData;
470 Private->StorageSecurityPpi.SendData = NvmeStorageSecuritySendData;
471 CopyMem (
472 &Private->StorageSecurityPpiList,
473 &mNvmeStorageSecurityPpiListTemplate,
474 sizeof (EFI_PEI_PPI_DESCRIPTOR)
475 );
476 Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;
477 PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
478 }
479
480 CopyMem (
481 &Private->EndOfPeiNotifyList,
482 &mNvmeEndOfPeiNotifyListTemplate,
483 sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
484 );
485 PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
486
487 DEBUG ((
488 DEBUG_INFO,
489 "%a: Controller %d has been successfully initialized.\n",
490 __FUNCTION__,
491 Controller
492 ));
493 Controller++;
494 }
495
496 return EFI_SUCCESS;
497 }