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