]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPeiBlockIo.c
MdeModulePkg/PciBusDxe: Fix small memory leak in FreePciDevice
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressPei / NvmExpressPeiBlockIo.c
CommitLineData
b8b69433
HW
1/** @file\r
2 The NvmExpressPei driver is used to manage non-volatile memory subsystem\r
3 which follows NVM Express specification at PEI phase.\r
4\r
5 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
6\r
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions\r
9 of the BSD License which accompanies this distribution. The\r
10 full text of the license may be found at\r
11 http://opensource.org/licenses/bsd-license.php\r
12\r
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
18#include "NvmExpressPei.h"\r
19\r
20/**\r
21 Read some sectors from the device.\r
22\r
23 @param NamespaceInfo The pointer to the PEI_NVME_NAMESPACE_INFO data structure.\r
24 @param Buffer The buffer used to store the data read from the device.\r
25 @param Lba The start block number.\r
26 @param Blocks Total block number to be read.\r
27\r
28 @retval EFI_SUCCESS Data are read from the device.\r
29 @retval Others Fail to read all the data.\r
30\r
31**/\r
32EFI_STATUS\r
33ReadSectors (\r
34 IN PEI_NVME_NAMESPACE_INFO *NamespaceInfo,\r
35 OUT UINTN Buffer,\r
36 IN UINT64 Lba,\r
37 IN UINT32 Blocks\r
38 )\r
39{\r
40 EFI_STATUS Status;\r
41 UINT32 BlockSize;\r
42 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
43 UINT32 Bytes;\r
44 EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
45 EDKII_PEI_NVM_EXPRESS_COMMAND Command;\r
46 EDKII_PEI_NVM_EXPRESS_COMPLETION Completion;\r
47\r
48 Private = NamespaceInfo->Controller;\r
49 BlockSize = NamespaceInfo->Media.BlockSize;\r
50 Bytes = Blocks * BlockSize;\r
51\r
52 ZeroMem (&CommandPacket, sizeof(EDKII_PEI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
53 ZeroMem (&Command, sizeof(EDKII_PEI_NVM_EXPRESS_COMMAND));\r
54 ZeroMem (&Completion, sizeof(EDKII_PEI_NVM_EXPRESS_COMPLETION));\r
55\r
56 CommandPacket.NvmeCmd = &Command;\r
57 CommandPacket.NvmeCompletion = &Completion;\r
58\r
59 CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;\r
60 CommandPacket.NvmeCmd->Nsid = NamespaceInfo->NamespaceId;\r
61 CommandPacket.TransferBuffer = (VOID *)Buffer;\r
62\r
63 CommandPacket.TransferLength = Bytes;\r
64 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
65 CommandPacket.QueueType = NVME_IO_QUEUE;\r
66\r
67 CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
68 CommandPacket.NvmeCmd->Cdw11 = (UINT32)RShiftU64(Lba, 32);\r
69 CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
70\r
71 CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
72\r
73 Status = NvmePassThru (\r
74 Private,\r
75 NamespaceInfo->NamespaceId,\r
76 &CommandPacket\r
77 );\r
78 return Status;\r
79}\r
80\r
81/**\r
82 Read some blocks from the device.\r
83\r
84 @param[in] NamespaceInfo The pointer to the PEI_NVME_NAMESPACE_INFO data structure.\r
85 @param[out] Buffer The Buffer used to store the Data read from the device.\r
86 @param[in] Lba The start block number.\r
87 @param[in] Blocks Total block number to be read.\r
88\r
89 @retval EFI_SUCCESS Data are read from the device.\r
90 @retval Others Fail to read all the data.\r
91\r
92**/\r
93EFI_STATUS\r
94NvmeRead (\r
95 IN PEI_NVME_NAMESPACE_INFO *NamespaceInfo,\r
96 OUT UINTN Buffer,\r
97 IN UINT64 Lba,\r
98 IN UINTN Blocks\r
99 )\r
100{\r
101 EFI_STATUS Status;\r
102 UINT32 Retries;\r
103 UINT32 BlockSize;\r
104 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
105 UINT32 MaxTransferBlocks;\r
106 UINTN OrginalBlocks;\r
107\r
108 Status = EFI_SUCCESS;\r
109 Retries = 0;\r
110 Private = NamespaceInfo->Controller;\r
111 BlockSize = NamespaceInfo->Media.BlockSize;\r
112 OrginalBlocks = Blocks;\r
113\r
114 if (Private->ControllerData->Mdts != 0) {\r
115 MaxTransferBlocks = (1 << (Private->ControllerData->Mdts)) * (1 << (Private->Cap.Mpsmin + 12)) / BlockSize;\r
116 } else {\r
117 MaxTransferBlocks = 1024;\r
118 }\r
119\r
120 while (Blocks > 0) {\r
121 Status = ReadSectors (\r
122 NamespaceInfo,\r
123 Buffer,\r
124 Lba,\r
125 Blocks > MaxTransferBlocks ? MaxTransferBlocks : (UINT32)Blocks\r
126 );\r
127 if (EFI_ERROR(Status)) {\r
128 Retries++;\r
129 MaxTransferBlocks = MaxTransferBlocks >> 1;\r
130\r
131 if (Retries > NVME_READ_MAX_RETRY || MaxTransferBlocks < 1) {\r
132 DEBUG ((DEBUG_ERROR, "%a: ReadSectors fail, Status - %r\n", __FUNCTION__, Status));\r
133 break;\r
134 }\r
135 DEBUG ((\r
136 DEBUG_BLKIO,\r
137 "%a: ReadSectors fail, retry with smaller transfer block number - 0x%x\n",\r
138 __FUNCTION__,\r
139 MaxTransferBlocks\r
140 ));\r
141 continue;\r
142 }\r
143\r
144 if (Blocks > MaxTransferBlocks) {\r
145 Blocks -= MaxTransferBlocks;\r
146 Buffer += (MaxTransferBlocks * BlockSize);\r
147 Lba += MaxTransferBlocks;\r
148 } else {\r
149 Blocks = 0;\r
150 }\r
151 }\r
152\r
153 DEBUG ((DEBUG_BLKIO, "%a: Lba = 0x%08Lx, Original = 0x%08Lx, "\r
154 "Remaining = 0x%08Lx, BlockSize = 0x%x, Status = %r\n", __FUNCTION__, Lba,\r
155 (UINT64)OrginalBlocks, (UINT64)Blocks, BlockSize, Status));\r
156 return Status;\r
157}\r
158\r
159/**\r
160 Gets the count of block I/O devices that one specific block driver detects.\r
161\r
162 This function is used for getting the count of block I/O devices that one\r
163 specific block driver detects. If no device is detected, then the function\r
164 will return zero.\r
165\r
166 @param[in] PeiServices General-purpose services that are available\r
167 to every PEIM.\r
168 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI\r
169 instance.\r
170 @param[out] NumberBlockDevices The number of block I/O devices discovered.\r
171\r
172 @retval EFI_SUCCESS The operation performed successfully.\r
173\r
174**/\r
175EFI_STATUS\r
176EFIAPI\r
177NvmeBlockIoPeimGetDeviceNo (\r
178 IN EFI_PEI_SERVICES **PeiServices,\r
179 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,\r
180 OUT UINTN *NumberBlockDevices\r
181 )\r
182{\r
183 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
184\r
185 if (This == NULL || NumberBlockDevices == NULL) {\r
186 return EFI_INVALID_PARAMETER;\r
187 }\r
188\r
189 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
190 *NumberBlockDevices = Private->ActiveNamespaceNum;\r
191\r
192 return EFI_SUCCESS;\r
193}\r
194\r
195/**\r
196 Gets a block device's media information.\r
197\r
198 This function will provide the caller with the specified block device's media\r
199 information. If the media changes, calling this function will update the media\r
200 information accordingly.\r
201\r
202 @param[in] PeiServices General-purpose services that are available to every\r
203 PEIM\r
204 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
205 @param[in] DeviceIndex Specifies the block device to which the function wants\r
206 to talk. Because the driver that implements Block I/O\r
207 PPIs will manage multiple block devices, the PPIs that\r
208 want to talk to a single device must specify the\r
209 device index that was assigned during the enumeration\r
210 process. This index is a number from one to\r
211 NumberBlockDevices.\r
212 @param[out] MediaInfo The media information of the specified block media.\r
213 The caller is responsible for the ownership of this\r
214 data structure.\r
215\r
216 @par Note:\r
217 The MediaInfo structure describes an enumeration of possible block device\r
218 types. This enumeration exists because no device paths are actually passed\r
219 across interfaces that describe the type or class of hardware that is publishing\r
220 the block I/O interface. This enumeration will allow for policy decisions\r
221 in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
222 LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
223 by a given device type, they should be reported in ascending order; this\r
224 order also applies to nested partitions, such as legacy MBR, where the\r
225 outermost partitions would have precedence in the reporting order. The\r
226 same logic applies to systems such as IDE that have precedence relationships\r
227 like "Master/Slave" or "Primary/Secondary". The master device should be\r
228 reported first, the slave second.\r
229\r
230 @retval EFI_SUCCESS Media information about the specified block device\r
231 was obtained successfully.\r
232 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware\r
233 error.\r
234\r
235**/\r
236EFI_STATUS\r
237EFIAPI\r
238NvmeBlockIoPeimGetMediaInfo (\r
239 IN EFI_PEI_SERVICES **PeiServices,\r
240 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,\r
241 IN UINTN DeviceIndex,\r
242 OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo\r
243 )\r
244{\r
245 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
246\r
247 if (This == NULL || MediaInfo == NULL) {\r
248 return EFI_INVALID_PARAMETER;\r
249 }\r
250\r
251 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
252\r
253 if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {\r
254 return EFI_INVALID_PARAMETER;\r
255 }\r
256\r
257 MediaInfo->DeviceType = (EFI_PEI_BLOCK_DEVICE_TYPE) EDKII_PEI_BLOCK_DEVICE_TYPE_NVME;\r
258 MediaInfo->MediaPresent = TRUE;\r
259 MediaInfo->LastBlock = (UINTN)Private->NamespaceInfo[DeviceIndex-1].Media.LastBlock;\r
260 MediaInfo->BlockSize = Private->NamespaceInfo[DeviceIndex-1].Media.BlockSize;\r
261\r
262 return EFI_SUCCESS;\r
263}\r
264\r
265/**\r
266 Reads the requested number of blocks from the specified block device.\r
267\r
268 The function reads the requested number of blocks from the device. All the\r
269 blocks are read, or an error is returned. If there is no media in the device,\r
270 the function returns EFI_NO_MEDIA.\r
271\r
272 @param[in] PeiServices General-purpose services that are available to\r
273 every PEIM.\r
274 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
275 @param[in] DeviceIndex Specifies the block device to which the function wants\r
276 to talk. Because the driver that implements Block I/O\r
277 PPIs will manage multiple block devices, PPIs that\r
278 want to talk to a single device must specify the device\r
279 index that was assigned during the enumeration process.\r
280 This index is a number from one to NumberBlockDevices.\r
281 @param[in] StartLBA The starting logical block address (LBA) to read from\r
282 on the device\r
283 @param[in] BufferSize The size of the Buffer in bytes. This number must be\r
284 a multiple of the intrinsic block size of the device.\r
285 @param[out] Buffer A pointer to the destination buffer for the data.\r
286 The caller is responsible for the ownership of the\r
287 buffer.\r
288\r
289 @retval EFI_SUCCESS The data was read correctly from the device.\r
290 @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
291 to perform the read operation.\r
292 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not\r
293 valid, or the buffer is not properly aligned.\r
294 @retval EFI_NO_MEDIA There is no media in the device.\r
295 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
296 the intrinsic block size of the device.\r
297\r
298**/\r
299EFI_STATUS\r
300EFIAPI\r
301NvmeBlockIoPeimReadBlocks (\r
302 IN EFI_PEI_SERVICES **PeiServices,\r
303 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,\r
304 IN UINTN DeviceIndex,\r
305 IN EFI_PEI_LBA StartLBA,\r
306 IN UINTN BufferSize,\r
307 OUT VOID *Buffer\r
308 )\r
309{\r
310 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
311 PEI_NVME_NAMESPACE_INFO *NamespaceInfo;\r
312 UINT32 BlockSize;\r
313 UINTN NumberOfBlocks;\r
314\r
315 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO (This);\r
316\r
317 //\r
318 // Check parameters\r
319 //\r
320 if (This == NULL || Buffer == NULL) {\r
321 return EFI_INVALID_PARAMETER;\r
322 }\r
323\r
324 if (BufferSize == 0) {\r
325 return EFI_SUCCESS;\r
326 }\r
327\r
328 if ((DeviceIndex == 0) || (DeviceIndex > Private->ActiveNamespaceNum)) {\r
329 return EFI_INVALID_PARAMETER;\r
330 }\r
331\r
332 //\r
333 // Check BufferSize and StartLBA\r
334 //\r
335 NamespaceInfo = &(Private->NamespaceInfo[DeviceIndex - 1]);\r
336 BlockSize = NamespaceInfo->Media.BlockSize;\r
337 if (BufferSize % BlockSize != 0) {\r
338 return EFI_BAD_BUFFER_SIZE;\r
339 }\r
340\r
341 if (StartLBA > NamespaceInfo->Media.LastBlock) {\r
342 return EFI_INVALID_PARAMETER;\r
343 }\r
344 NumberOfBlocks = BufferSize / BlockSize;\r
345 if (NumberOfBlocks - 1 > NamespaceInfo->Media.LastBlock - StartLBA) {\r
346 return EFI_INVALID_PARAMETER;\r
347 }\r
348\r
349 return NvmeRead (NamespaceInfo, (UINTN)Buffer, StartLBA, NumberOfBlocks);\r
350}\r
351\r
352/**\r
353 Gets the count of block I/O devices that one specific block driver detects.\r
354\r
355 This function is used for getting the count of block I/O devices that one\r
356 specific block driver detects. If no device is detected, then the function\r
357 will return zero.\r
358\r
359 @param[in] PeiServices General-purpose services that are available\r
360 to every PEIM.\r
361 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI\r
362 instance.\r
363 @param[out] NumberBlockDevices The number of block I/O devices discovered.\r
364\r
365 @retval EFI_SUCCESS The operation performed successfully.\r
366\r
367**/\r
368EFI_STATUS\r
369EFIAPI\r
370NvmeBlockIoPeimGetDeviceNo2 (\r
371 IN EFI_PEI_SERVICES **PeiServices,\r
372 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,\r
373 OUT UINTN *NumberBlockDevices\r
374 )\r
375{\r
376 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
377\r
378 if (This == NULL || NumberBlockDevices == NULL) {\r
379 return EFI_INVALID_PARAMETER;\r
380 }\r
381\r
382 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
383 *NumberBlockDevices = Private->ActiveNamespaceNum;\r
384\r
385 return EFI_SUCCESS;\r
386}\r
387\r
388/**\r
389 Gets a block device's media information.\r
390\r
391 This function will provide the caller with the specified block device's media\r
392 information. If the media changes, calling this function will update the media\r
393 information accordingly.\r
394\r
395 @param[in] PeiServices General-purpose services that are available to every\r
396 PEIM\r
397 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
398 @param[in] DeviceIndex Specifies the block device to which the function wants\r
399 to talk. Because the driver that implements Block I/O\r
400 PPIs will manage multiple block devices, the PPIs that\r
401 want to talk to a single device must specify the\r
402 device index that was assigned during the enumeration\r
403 process. This index is a number from one to\r
404 NumberBlockDevices.\r
405 @param[out] MediaInfo The media information of the specified block media.\r
406 The caller is responsible for the ownership of this\r
407 data structure.\r
408\r
409 @par Note:\r
410 The MediaInfo structure describes an enumeration of possible block device\r
411 types. This enumeration exists because no device paths are actually passed\r
412 across interfaces that describe the type or class of hardware that is publishing\r
413 the block I/O interface. This enumeration will allow for policy decisions\r
414 in the Recovery PEIM, such as "Try to recover from legacy floppy first,\r
415 LS-120 second, CD-ROM third." If there are multiple partitions abstracted\r
416 by a given device type, they should be reported in ascending order; this\r
417 order also applies to nested partitions, such as legacy MBR, where the\r
418 outermost partitions would have precedence in the reporting order. The\r
419 same logic applies to systems such as IDE that have precedence relationships\r
420 like "Master/Slave" or "Primary/Secondary". The master device should be\r
421 reported first, the slave second.\r
422\r
423 @retval EFI_SUCCESS Media information about the specified block device\r
424 was obtained successfully.\r
425 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware\r
426 error.\r
427\r
428**/\r
429EFI_STATUS\r
430EFIAPI\r
431NvmeBlockIoPeimGetMediaInfo2 (\r
432 IN EFI_PEI_SERVICES **PeiServices,\r
433 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,\r
434 IN UINTN DeviceIndex,\r
435 OUT EFI_PEI_BLOCK_IO2_MEDIA *MediaInfo\r
436 )\r
437{\r
438 EFI_STATUS Status;\r
439 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
440 EFI_PEI_BLOCK_IO_MEDIA Media;\r
441\r
442 if (This == NULL || MediaInfo == NULL) {\r
443 return EFI_INVALID_PARAMETER;\r
444 }\r
445\r
446 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
447\r
448 Status = NvmeBlockIoPeimGetMediaInfo (\r
449 PeiServices,\r
450 &Private->BlkIoPpi,\r
451 DeviceIndex,\r
452 &Media\r
453 );\r
454 if (EFI_ERROR (Status)) {\r
455 return Status;\r
456 }\r
457\r
458 CopyMem (\r
459 MediaInfo,\r
460 &(Private->NamespaceInfo[DeviceIndex - 1].Media),\r
461 sizeof (EFI_PEI_BLOCK_IO2_MEDIA)\r
462 );\r
463\r
464 return EFI_SUCCESS;\r
465}\r
466\r
467/**\r
468 Reads the requested number of blocks from the specified block device.\r
469\r
470 The function reads the requested number of blocks from the device. All the\r
471 blocks are read, or an error is returned. If there is no media in the device,\r
472 the function returns EFI_NO_MEDIA.\r
473\r
474 @param[in] PeiServices General-purpose services that are available to\r
475 every PEIM.\r
476 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO2_PPI instance.\r
477 @param[in] DeviceIndex Specifies the block device to which the function wants\r
478 to talk. Because the driver that implements Block I/O\r
479 PPIs will manage multiple block devices, PPIs that\r
480 want to talk to a single device must specify the device\r
481 index that was assigned during the enumeration process.\r
482 This index is a number from one to NumberBlockDevices.\r
483 @param[in] StartLBA The starting logical block address (LBA) to read from\r
484 on the device\r
485 @param[in] BufferSize The size of the Buffer in bytes. This number must be\r
486 a multiple of the intrinsic block size of the device.\r
487 @param[out] Buffer A pointer to the destination buffer for the data.\r
488 The caller is responsible for the ownership of the\r
489 buffer.\r
490\r
491 @retval EFI_SUCCESS The data was read correctly from the device.\r
492 @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
493 to perform the read operation.\r
494 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not\r
495 valid, or the buffer is not properly aligned.\r
496 @retval EFI_NO_MEDIA There is no media in the device.\r
497 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
498 the intrinsic block size of the device.\r
499\r
500**/\r
501EFI_STATUS\r
502EFIAPI\r
503NvmeBlockIoPeimReadBlocks2 (\r
504 IN EFI_PEI_SERVICES **PeiServices,\r
505 IN EFI_PEI_RECOVERY_BLOCK_IO2_PPI *This,\r
506 IN UINTN DeviceIndex,\r
507 IN EFI_PEI_LBA StartLBA,\r
508 IN UINTN BufferSize,\r
509 OUT VOID *Buffer\r
510 )\r
511{\r
512 PEI_NVME_CONTROLLER_PRIVATE_DATA *Private;\r
513\r
514 if (This == NULL) {\r
515 return EFI_INVALID_PARAMETER;\r
516 }\r
517\r
518 Private = GET_NVME_PEIM_HC_PRIVATE_DATA_FROM_THIS_BLKIO2 (This);\r
519 return NvmeBlockIoPeimReadBlocks (\r
520 PeiServices,\r
521 &Private->BlkIoPpi,\r
522 DeviceIndex,\r
523 StartLBA,\r
524 BufferSize,\r
525 Buffer\r
526 );\r
527}\r