]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.c
2fe73e942c688123893b4c51fecc1c714dfcd472
[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_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 // Locate the NVME host controller PPI
219 //
220 Status = PeiServicesLocatePpi (
221 &gEdkiiPeiNvmExpressHostControllerPpiGuid,
222 0,
223 NULL,
224 (VOID **) &NvmeHcPpi
225 );
226 if (EFI_ERROR (Status)) {
227 DEBUG ((DEBUG_ERROR, "%a: Fail to locate NvmeHostControllerPpi.\n", __FUNCTION__));
228 return EFI_UNSUPPORTED;
229 }
230
231 Controller = 0;
232 MmioBase = 0;
233 while (TRUE) {
234 Status = NvmeHcPpi->GetNvmeHcMmioBar (
235 NvmeHcPpi,
236 Controller,
237 &MmioBase
238 );
239 //
240 // When status is error, meant no controller is found
241 //
242 if (EFI_ERROR (Status)) {
243 break;
244 }
245
246 //
247 // Memory allocation for controller private data
248 //
249 Private = AllocateZeroPool (sizeof (PEI_NVME_CONTROLLER_PRIVATE_DATA));
250 if (Private == NULL) {
251 DEBUG ((
252 DEBUG_ERROR,
253 "%a: Fail to allocate private data for Controller %d.\n",
254 __FUNCTION__,
255 Controller
256 ));
257 return EFI_OUT_OF_RESOURCES;
258 }
259
260 //
261 // Memory allocation for transfer-related data
262 //
263 Status = IoMmuAllocateBuffer (
264 NVME_MEM_MAX_PAGES,
265 &Private->Buffer,
266 &DeviceAddress,
267 &Private->BufferMapping
268 );
269 if (EFI_ERROR (Status)) {
270 DEBUG ((
271 DEBUG_ERROR,
272 "%a: Fail to allocate DMA buffers for Controller %d.\n",
273 __FUNCTION__,
274 Controller
275 ));
276 NvmeFreeControllerResource (Private);
277 return Status;
278 }
279 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) Private->Buffer));
280 DEBUG ((DEBUG_INFO, "%a: DMA buffer base at 0x%x\n", __FUNCTION__, Private->Buffer));
281
282 //
283 // Initialize controller private data
284 //
285 Private->Signature = NVME_PEI_CONTROLLER_PRIVATE_DATA_SIGNATURE;
286 Private->MmioBase = MmioBase;
287 Private->BlkIoPpi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo;
288 Private->BlkIoPpi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo;
289 Private->BlkIoPpi.ReadBlocks = NvmeBlockIoPeimReadBlocks;
290 Private->BlkIo2Ppi.Revision = EFI_PEI_RECOVERY_BLOCK_IO2_PPI_REVISION;
291 Private->BlkIo2Ppi.GetNumberOfBlockDevices = NvmeBlockIoPeimGetDeviceNo2;
292 Private->BlkIo2Ppi.GetBlockDeviceMediaInfo = NvmeBlockIoPeimGetMediaInfo2;
293 Private->BlkIo2Ppi.ReadBlocks = NvmeBlockIoPeimReadBlocks2;
294 CopyMem (&Private->BlkIoPpiList, &mNvmeBlkIoPpiListTemplate, sizeof (EFI_PEI_PPI_DESCRIPTOR));
295 CopyMem (&Private->BlkIo2PpiList, &mNvmeBlkIo2PpiListTemplate, sizeof (EFI_PEI_PPI_DESCRIPTOR));
296 CopyMem (&Private->EndOfPeiNotifyList, &mNvmeEndOfPeiNotifyListTemplate, sizeof (EFI_PEI_NOTIFY_DESCRIPTOR));
297 Private->BlkIoPpiList.Ppi = &Private->BlkIoPpi;
298 Private->BlkIo2PpiList.Ppi = &Private->BlkIo2Ppi;
299
300 //
301 // Initialize the NVME controller
302 //
303 Status = NvmeControllerInit (Private);
304 if (EFI_ERROR (Status)) {
305 DEBUG ((
306 DEBUG_ERROR,
307 "%a: Controller initialization fail for Controller %d with Status - %r.\n",
308 __FUNCTION__,
309 Controller,
310 Status
311 ));
312 NvmeFreeControllerResource (Private);
313 Controller++;
314 continue;
315 }
316
317 //
318 // Enumerate the NVME namespaces on the controller
319 //
320 Status = NvmeDiscoverNamespaces (Private);
321 if (EFI_ERROR (Status)) {
322 //
323 // No active namespace was found on the controller
324 //
325 DEBUG ((
326 DEBUG_ERROR,
327 "%a: Namespaces discovery fail for Controller %d with Status - %r.\n",
328 __FUNCTION__,
329 Controller,
330 Status
331 ));
332 NvmeFreeControllerResource (Private);
333 Controller++;
334 continue;
335 }
336
337 PeiServicesInstallPpi (&Private->BlkIoPpiList);
338 PeiServicesNotifyPpi (&Private->EndOfPeiNotifyList);
339 DEBUG ((
340 DEBUG_INFO,
341 "%a: BlockIO PPI has been installed on Controller %d.\n",
342 __FUNCTION__,
343 Controller
344 ));
345 Controller++;
346 }
347
348 return EFI_SUCCESS;
349 }