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