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