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