]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
MdeModulePkg/NvmExpressPei: Add logic to produce SSC 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 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 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 // Locate the NVME host controller PPI
228 //
229 Status = PeiServicesLocatePpi (
230 &gEdkiiPeiNvmExpressHostControllerPpiGuid,
231 0,
232 NULL,
233 (VOID **) &NvmeHcPpi
234 );
235 if (EFI_ERROR (Status)) {
236 DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));
237 return EFI_UNSUPPORTED;
238 }
239
240 Controller = 0;
241 MmioBase = 0;
242 while (TRUE) {
243 Status = NvmeHcPpi->GetNvmeHcMmioBar (
244 NvmeHcPpi,
245 Controller,
246 &MmioBase
247 );
248 //
249 // When status is error, meant no controller is found
250 //
251 if (EFI_ERROR (Status)) {
252 break;
253 }
254
255 Status = NvmeHcPpi->GetNvmeHcDevicePath (
256 NvmeHcPpi,
257 Controller,
258 &DevicePathLength,
259 &DevicePath
260 );
261 if (EFI_ERROR (Status)) {
262 DEBUG ((
263 DEBUG_ERROR, "%a: Fail to allocate get the device path for Controller %d.\n",
264 __FUNCTION__, Controller
265 ));
266 return Status;
267 }
268
269 //
270 // Check validity of the device path of the NVM Express controller.
271 //
272 Status = NvmeIsHcDevicePathValid (DevicePath, DevicePathLength);
273 if (EFI_ERROR (Status)) {
274 DEBUG ((
275 DEBUG_ERROR, "%a: The device path is invalid for Controller %d.\n",
276 __FUNCTION__, Controller
277 ));
278 Controller++;
279 continue;
280 }
281
282 //
283 // Memory allocation for controller private data
284 //
285 Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));
286 if (Private == NULL) {
287 DEBUG ((
288 DEBUG_ERROR, "%a: Fail to allocate private data for Controller %d.\n",
289 __FUNCTION__, Controller
290 ));
291 return EFI_OUT_OF_RESOURCES;
292 }
293
294 //
295 // Memory allocation for transfer-related data
296 //
297 Status = IoMmuAllocateBuffer (
298 NVME_MEM_MAX_PAGES,
299 &Private->Buffer,
300 &DeviceAddress,
301 &Private->BufferMapping
302 );
303 if (EFI_ERROR (Status)) {
304 DEBUG ((
305 DEBUG_ERROR, "%a: Fail to allocate DMA buffers for Controller %d.\n",
306 __FUNCTION__, Controller
307 ));
308 return Status;
309 }
310 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private->Buffer));
311 DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));
312
313 //
314 // Initialize controller private data
315 //
316 Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
317 Private->MmioBase = MmioBase;
318 Private->DevicePathLength = DevicePathLength;
319 Private->DevicePath = DevicePath;
320
321 //
322 // Initialize the NVME controller
323 //
324 Status = NvmeControllerInit (Private);
325 if (EFI_ERROR (Status)) {
326 DEBUG ((
327 DEBUG_ERROR,
328 "%a: Controller initialization fail for Controller %d with Status - %r.\n",
329 __FUNCTION__, Controller, Status
330 ));
331 NvmeFreeDmaResource (Private);
332 Controller++;
333 continue;
334 }
335
336 //
337 // Enumerate the NVME namespaces on the controller
338 //
339 Status = NvmeDiscoverNamespaces (Private);
340 if (EFI_ERROR (Status)) {
341 //
342 // No active namespace was found on the controller
343 //
344 DEBUG ((
345 DEBUG_ERROR,
346 "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
347 __FUNCTION__, Controller, Status
348 ));
349 NvmeFreeDmaResource (Private);
350 Controller++;
351 continue;
352 }
353
354 Private->BlkIoPpi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo;
355 Private->BlkIoPpi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo;
356 Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
357 CopyMem (
358 &Private->BlkIoPpiList,
359 &mNvmeBlkIoPpiListTemplate,
360 sizeof (EFI_PEI_PPI_DESCRIPTOR)
361 );
362 Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
363
364 Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
365 Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;
366 Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;
367 Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
368 CopyMem (
369 &Private->BlkIo2PpiList,
370 &mNvmeBlkIo2PpiListTemplate,
371 sizeof (EFI_PEI_PPI_DESCRIPTOR)
372 );
373 Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
374 PeiServicesInstallPpi (&Private->BlkIoPpiList);
375
376 //
377 // Check if the NVME controller supports the Security Receive/Send commands
378 //
379 if ((Private->ControllerData->Oacs & SECURITY_SEND_RECEIVE_SUPPORTED) != 0) {
380 DEBUG ((
381 DEBUG_INFO,
382 "%a: Security Security Command PPI will be produced for Controller %d.\n",
383 __FUNCTION__, Controller
384 ));
385 Private->StorageSecurityPpi.Revision = EDKII_STORAGE_SECURITY_PPI_REVISION;
386 Private->StorageSecurityPpi.GetNumberofDevices = NvmeStorageSecurityGetDeviceNo;
387 Private->StorageSecurityPpi.GetDevicePath = NvmeStorageSecurityGetDevicePath;
388 Private->StorageSecurityPpi.ReceiveData = NvmeStorageSecurityReceiveData;
389 Private->StorageSecurityPpi.SendData = NvmeStorageSecuritySendData;
390 CopyMem (
391 &Private->StorageSecurityPpiList,
392 &mNvmeStorageSecurityPpiListTemplate,
393 sizeof (EFI_PEI_PPI_DESCRIPTOR)
394 );
395 Private->StorageSecurityPpiList.Ppi = &Private->StorageSecurityPpi;
396 PeiServicesInstallPpi (&Private->StorageSecurityPpiList);
397 }
398
399 CopyMem (
400 &Private->EndOfPeiNotifyList,
401 &mNvmeEndOfPeiNotifyListTemplate,
402 sizeof (EFI_PEI_NOTIFY_DESCRIPTOR)
403 );
404 PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
405
406 DEBUG ((
407 DEBUG_INFO, "%a: Controller %d has been successfully initialized.\n",
408 __FUNCTION__, Controller
409 ));
410 Controller++;
411 }
412
413 return EFI_SUCCESS;
414 }