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