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