]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c
MdePkg: Add UEFI2.5 EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL definitions
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / NvmExpressDxe / NvmExpressBlockIo.c
CommitLineData
eb290d02
FT
1/** @file\r
2 NvmExpressDxe driver is used to manage non-volatile memory subsystem which follows\r
3 NVM Express specification.\r
4\r
5 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "NvmExpress.h"\r
17\r
18/**\r
19 Read some sectors from the device.\r
20\r
21 @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.\r
22 @param Buffer The buffer used to store the data read from the device.\r
23 @param Lba The start block number.\r
24 @param Blocks Total block number to be read.\r
25\r
26 @retval EFI_SUCCESS Datum are read from the device.\r
27 @retval Others Fail to read all the datum.\r
28\r
29**/\r
30EFI_STATUS\r
31ReadSectors (\r
32 IN NVME_DEVICE_PRIVATE_DATA *Device,\r
33 IN UINT64 Buffer,\r
34 IN UINT64 Lba,\r
35 IN UINT32 Blocks\r
36 )\r
37{\r
38 NVME_CONTROLLER_PRIVATE_DATA *Controller;\r
39 UINT32 Bytes;\r
40 NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
41 NVM_EXPRESS_COMMAND Command;\r
42 NVM_EXPRESS_RESPONSE Response;\r
43 EFI_STATUS Status;\r
44 UINT32 BlockSize;\r
45\r
46 Controller = Device->Controller;\r
47 BlockSize = Device->Media.BlockSize;\r
48 Bytes = Blocks * BlockSize;\r
49\r
50 ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
51 ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
52 ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
53\r
54 CommandPacket.NvmeCmd = &Command;\r
55 CommandPacket.NvmeResponse = &Response;\r
56\r
57 CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_READ_OPC;\r
58 CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;\r
59 CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;\r
60 CommandPacket.TransferBuffer = (VOID *)(UINTN)Buffer;\r
61\r
62 CommandPacket.TransferLength = Bytes;\r
63 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
64 CommandPacket.QueueId = NVME_IO_QUEUE;\r
65\r
66 CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
67 CommandPacket.NvmeCmd->Cdw11 = (UINT32)(Lba >> 32);\r
68 CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
69\r
70 CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
71\r
72 Status = Controller->Passthru.PassThru (\r
73 &Controller->Passthru,\r
74 Device->NamespaceId,\r
75 0,\r
76 &CommandPacket,\r
77 NULL\r
78 );\r
79\r
80 return Status;\r
81}\r
82\r
83/**\r
84 Write some sectors to the device.\r
85\r
86 @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.\r
87 @param Buffer The buffer to be written into the device.\r
88 @param Lba The start block number.\r
89 @param Blocks Total block number to be written.\r
90\r
91 @retval EFI_SUCCESS Datum are written into the buffer.\r
92 @retval Others Fail to write all the datum.\r
93\r
94**/\r
95EFI_STATUS\r
96WriteSectors (\r
97 IN NVME_DEVICE_PRIVATE_DATA *Device,\r
98 IN UINT64 Buffer,\r
99 IN UINT64 Lba,\r
100 IN UINT32 Blocks\r
101 )\r
102{\r
103 NVME_CONTROLLER_PRIVATE_DATA *Controller;\r
104 NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
105 NVM_EXPRESS_COMMAND Command;\r
106 NVM_EXPRESS_RESPONSE Response;\r
107 EFI_STATUS Status;\r
108 UINT32 Bytes;\r
109 UINT32 BlockSize;\r
110\r
111 Controller = Device->Controller;\r
112 BlockSize = Device->Media.BlockSize;\r
113 Bytes = Blocks * BlockSize;\r
114\r
115 ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
116 ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
117 ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
118\r
119 CommandPacket.NvmeCmd = &Command;\r
120 CommandPacket.NvmeResponse = &Response;\r
121\r
122 CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_WRITE_OPC;\r
123 CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;\r
124 CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;\r
125 CommandPacket.TransferBuffer = (VOID *)(UINTN)Buffer;\r
126\r
127 CommandPacket.TransferLength = Bytes;\r
128 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
129 CommandPacket.QueueId = NVME_IO_QUEUE;\r
130\r
131 CommandPacket.NvmeCmd->Cdw10 = (UINT32)Lba;\r
132 CommandPacket.NvmeCmd->Cdw11 = (UINT32)(Lba >> 32);\r
133 CommandPacket.NvmeCmd->Cdw12 = (Blocks - 1) & 0xFFFF;\r
134\r
135 CommandPacket.MetadataBuffer = NULL;\r
136 CommandPacket.MetadataLength = 0;\r
137\r
138 CommandPacket.NvmeCmd->Flags = CDW10_VALID | CDW11_VALID | CDW12_VALID;\r
139\r
140 Status = Controller->Passthru.PassThru (\r
141 &Controller->Passthru,\r
142 Device->NamespaceId,\r
143 0,\r
144 &CommandPacket,\r
145 NULL\r
146 );\r
147\r
148 return Status;\r
149}\r
150\r
151/**\r
152 Read some blocks from the device.\r
153\r
154 @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.\r
155 @param Buffer The buffer used to store the data read from the device.\r
156 @param Lba The start block number.\r
157 @param Blocks Total block number to be read.\r
158\r
159 @retval EFI_SUCCESS Datum are read from the device.\r
160 @retval Others Fail to read all the datum.\r
161\r
162**/\r
163EFI_STATUS\r
164NvmeRead (\r
165 IN NVME_DEVICE_PRIVATE_DATA *Device,\r
166 OUT VOID *Buffer,\r
167 IN UINT64 Lba,\r
168 IN UINTN Blocks\r
169 )\r
170{\r
171 EFI_STATUS Status;\r
172 UINT32 BlockSize;\r
173 NVME_CONTROLLER_PRIVATE_DATA *Controller;\r
174 UINT32 MaxTransferBlocks;\r
7b8883c6 175 UINTN OrginalBlocks;\r
eb290d02 176\r
7b8883c6
FT
177 Status = EFI_SUCCESS;\r
178 Controller = Device->Controller;\r
179 BlockSize = Device->Media.BlockSize;\r
180 OrginalBlocks = Blocks;\r
eb290d02
FT
181\r
182 if (Controller->ControllerData->Mdts != 0) {\r
183 MaxTransferBlocks = (1 << (Controller->ControllerData->Mdts)) * (1 << (Controller->Cap.Mpsmin + 12)) / BlockSize;\r
184 } else {\r
185 MaxTransferBlocks = 1024;\r
186 }\r
187\r
188 while (Blocks > 0) {\r
189 if (Blocks > MaxTransferBlocks) {\r
190 Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks);\r
191\r
192 Blocks -= MaxTransferBlocks;\r
193 Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);\r
194 Lba += MaxTransferBlocks;\r
195 } else {\r
196 Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks);\r
197 Blocks = 0;\r
198 }\r
199\r
200 if (EFI_ERROR(Status)) {\r
201 break;\r
202 }\r
203 }\r
204\r
7b8883c6 205 DEBUG ((EFI_D_INFO, "NvmeRead() Lba = 0x%08x, Original = 0x%08x, Remaining = 0x%08x, BlockSize = 0x%x Status = %r\n", Lba, OrginalBlocks, Blocks, BlockSize, Status));\r
eb290d02
FT
206\r
207 return Status;\r
208}\r
209\r
210/**\r
211 Write some blocks to the device.\r
212\r
213 @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.\r
214 @param Buffer The buffer to be written into the device.\r
215 @param Lba The start block number.\r
216 @param Blocks Total block number to be written.\r
217\r
218 @retval EFI_SUCCESS Datum are written into the buffer.\r
219 @retval Others Fail to write all the datum.\r
220\r
221**/\r
222EFI_STATUS\r
223NvmeWrite (\r
224 IN NVME_DEVICE_PRIVATE_DATA *Device,\r
225 IN VOID *Buffer,\r
226 IN UINT64 Lba,\r
227 IN UINTN Blocks\r
228 )\r
229{\r
230 EFI_STATUS Status;\r
231 UINT32 BlockSize;\r
232 NVME_CONTROLLER_PRIVATE_DATA *Controller;\r
233 UINT32 MaxTransferBlocks;\r
7b8883c6 234 UINTN OrginalBlocks;\r
eb290d02 235\r
7b8883c6
FT
236 Status = EFI_SUCCESS;\r
237 Controller = Device->Controller;\r
238 BlockSize = Device->Media.BlockSize;\r
239 OrginalBlocks = Blocks;\r
eb290d02
FT
240\r
241 if (Controller->ControllerData->Mdts != 0) {\r
242 MaxTransferBlocks = (1 << (Controller->ControllerData->Mdts)) * (1 << (Controller->Cap.Mpsmin + 12)) / BlockSize;\r
243 } else {\r
244 MaxTransferBlocks = 1024;\r
245 }\r
246\r
247 while (Blocks > 0) {\r
248 if (Blocks > MaxTransferBlocks) {\r
249 Status = WriteSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks);\r
250\r
251 Blocks -= MaxTransferBlocks;\r
252 Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);\r
253 Lba += MaxTransferBlocks;\r
254 } else {\r
255 Status = WriteSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks);\r
256 Blocks = 0;\r
257 }\r
258\r
259 if (EFI_ERROR(Status)) {\r
260 break;\r
261 }\r
262 }\r
263\r
7b8883c6 264 DEBUG ((EFI_D_INFO, "NvmeWrite() Lba = 0x%08x, Original = 0x%08x, Remaining = 0x%08x, BlockSize = 0x%x Status = %r\n", Lba, OrginalBlocks, Blocks, BlockSize, Status));\r
eb290d02
FT
265\r
266 return Status;\r
267}\r
268\r
269/**\r
270 Flushes all modified data to the device.\r
271\r
272 @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.\r
273\r
274 @retval EFI_SUCCESS Datum are written into the buffer.\r
275 @retval Others Fail to write all the datum.\r
276\r
277**/\r
278EFI_STATUS\r
279NvmeFlush (\r
280 IN NVME_DEVICE_PRIVATE_DATA *Device\r
281 )\r
282{\r
283 NVME_CONTROLLER_PRIVATE_DATA *Controller;\r
284 NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;\r
285 NVM_EXPRESS_COMMAND Command;\r
286 NVM_EXPRESS_RESPONSE Response;\r
287 EFI_STATUS Status;\r
288\r
289 Controller = Device->Controller;\r
290\r
291 ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));\r
292 ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));\r
293 ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));\r
294\r
295 CommandPacket.NvmeCmd = &Command;\r
296 CommandPacket.NvmeResponse = &Response;\r
297\r
298 CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC;\r
299 CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;\r
300 CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;\r
301 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;\r
302 CommandPacket.QueueId = NVME_IO_QUEUE;\r
303\r
304 Status = Controller->Passthru.PassThru (\r
305 &Controller->Passthru,\r
306 Device->NamespaceId,\r
307 0,\r
308 &CommandPacket,\r
309 NULL\r
310 );\r
311\r
312 return Status;\r
313}\r
314\r
315\r
316/**\r
317 Reset the Block Device.\r
318\r
319 @param This Indicates a pointer to the calling context.\r
320 @param ExtendedVerification Driver may perform diagnostics on reset.\r
321\r
322 @retval EFI_SUCCESS The device was reset.\r
323 @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
324 not be reset.\r
325\r
326**/\r
327EFI_STATUS\r
328EFIAPI\r
329NvmeBlockIoReset (\r
330 IN EFI_BLOCK_IO_PROTOCOL *This,\r
331 IN BOOLEAN ExtendedVerification\r
332 )\r
333{\r
334 EFI_TPL OldTpl;\r
335 NVME_CONTROLLER_PRIVATE_DATA *Private;\r
336 NVME_DEVICE_PRIVATE_DATA *Device;\r
337 EFI_STATUS Status;\r
338\r
339 if (This == NULL) {\r
340 return EFI_INVALID_PARAMETER;\r
341 }\r
342\r
343 //\r
344 // For Nvm Express subsystem, reset block device means reset controller.\r
345 //\r
346 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
347\r
348 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);\r
349\r
350 Private = Device->Controller;\r
351\r
352 Status = NvmeControllerInit (Private);\r
353\r
354 gBS->RestoreTPL (OldTpl);\r
355\r
356 return Status;\r
357}\r
358\r
359/**\r
360 Read BufferSize bytes from Lba into Buffer.\r
361\r
362 @param This Indicates a pointer to the calling context.\r
363 @param MediaId Id of the media, changes every time the media is replaced.\r
364 @param Lba The starting Logical Block Address to read from.\r
365 @param BufferSize Size of Buffer, must be a multiple of device block size.\r
366 @param Buffer A pointer to the destination buffer for the data. The caller is\r
367 responsible for either having implicit or explicit ownership of the buffer.\r
368\r
369 @retval EFI_SUCCESS The data was read correctly from the device.\r
370 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.\r
371 @retval EFI_NO_MEDIA There is no media in the device.\r
372 @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.\r
373 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
374 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,\r
375 or the buffer is not on proper alignment.\r
376\r
377**/\r
378EFI_STATUS\r
379EFIAPI\r
380NvmeBlockIoReadBlocks (\r
381 IN EFI_BLOCK_IO_PROTOCOL *This,\r
382 IN UINT32 MediaId,\r
383 IN EFI_LBA Lba,\r
384 IN UINTN BufferSize,\r
385 OUT VOID *Buffer\r
386 )\r
387{\r
388 NVME_DEVICE_PRIVATE_DATA *Device;\r
389 EFI_STATUS Status;\r
390 EFI_BLOCK_IO_MEDIA *Media;\r
391 UINTN BlockSize;\r
392 UINTN NumberOfBlocks;\r
393 UINTN IoAlign;\r
394 EFI_TPL OldTpl;\r
395\r
396 //\r
397 // Check parameters.\r
398 //\r
399 if (This == NULL) {\r
400 return EFI_INVALID_PARAMETER;\r
401 }\r
402\r
403 Media = This->Media;\r
404\r
405 if (MediaId != Media->MediaId) {\r
406 return EFI_MEDIA_CHANGED;\r
407 }\r
408\r
409 if (Buffer == NULL) {\r
410 return EFI_INVALID_PARAMETER;\r
411 }\r
412\r
413 if (BufferSize == 0) {\r
414 return EFI_SUCCESS;\r
415 }\r
416\r
417 BlockSize = Media->BlockSize;\r
418 if ((BufferSize % BlockSize) != 0) {\r
419 return EFI_BAD_BUFFER_SIZE;\r
420 }\r
421\r
422 NumberOfBlocks = BufferSize / BlockSize;\r
423 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
424 return EFI_INVALID_PARAMETER;\r
425 }\r
426\r
427 IoAlign = Media->IoAlign;\r
428 if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {\r
429 return EFI_INVALID_PARAMETER;\r
430 }\r
431\r
432 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
433\r
434 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);\r
435\r
436 Status = NvmeRead (Device, Buffer, Lba, NumberOfBlocks);\r
437\r
438 gBS->RestoreTPL (OldTpl);\r
439 return Status;\r
440}\r
441\r
442/**\r
443 Write BufferSize bytes from Lba into Buffer.\r
444\r
445 @param This Indicates a pointer to the calling context.\r
446 @param MediaId The media ID that the write request is for.\r
447 @param Lba The starting logical block address to be written. The caller is\r
448 responsible for writing to only legitimate locations.\r
449 @param BufferSize Size of Buffer, must be a multiple of device block size.\r
450 @param Buffer A pointer to the source buffer for the data.\r
451\r
452 @retval EFI_SUCCESS The data was written correctly to the device.\r
453 @retval EFI_WRITE_PROTECTED The device can not be written to.\r
454 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.\r
455 @retval EFI_NO_MEDIA There is no media in the device.\r
456 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.\r
457 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
458 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,\r
459 or the buffer is not on proper alignment.\r
460\r
461**/\r
462EFI_STATUS\r
463EFIAPI\r
464NvmeBlockIoWriteBlocks (\r
465 IN EFI_BLOCK_IO_PROTOCOL *This,\r
466 IN UINT32 MediaId,\r
467 IN EFI_LBA Lba,\r
468 IN UINTN BufferSize,\r
469 IN VOID *Buffer\r
470 )\r
471{\r
472 NVME_DEVICE_PRIVATE_DATA *Device;\r
473 EFI_STATUS Status;\r
474 EFI_BLOCK_IO_MEDIA *Media;\r
475 UINTN BlockSize;\r
476 UINTN NumberOfBlocks;\r
477 UINTN IoAlign;\r
478 EFI_TPL OldTpl;\r
479\r
480 //\r
481 // Check parameters.\r
482 //\r
483 if (This == NULL) {\r
484 return EFI_INVALID_PARAMETER;\r
485 }\r
486\r
487 Media = This->Media;\r
488\r
489 if (MediaId != Media->MediaId) {\r
490 return EFI_MEDIA_CHANGED;\r
491 }\r
492\r
493 if (Buffer == NULL) {\r
494 return EFI_INVALID_PARAMETER;\r
495 }\r
496\r
497 if (BufferSize == 0) {\r
498 return EFI_SUCCESS;\r
499 }\r
500\r
501 BlockSize = Media->BlockSize;\r
502 if ((BufferSize % BlockSize) != 0) {\r
503 return EFI_BAD_BUFFER_SIZE;\r
504 }\r
505\r
506 NumberOfBlocks = BufferSize / BlockSize;\r
507 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
508 return EFI_INVALID_PARAMETER;\r
509 }\r
510\r
511 IoAlign = Media->IoAlign;\r
512 if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {\r
513 return EFI_INVALID_PARAMETER;\r
514 }\r
515\r
516 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
517\r
518 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);\r
519\r
520 Status = NvmeWrite (Device, Buffer, Lba, NumberOfBlocks);\r
521\r
522 gBS->RestoreTPL (OldTpl);\r
523\r
524 return Status;\r
525}\r
526\r
527/**\r
528 Flush the Block Device.\r
529\r
530 @param This Indicates a pointer to the calling context.\r
531\r
532 @retval EFI_SUCCESS All outstanding data was written to the device.\r
533 @retval EFI_DEVICE_ERROR The device reported an error while writing back the data.\r
534 @retval EFI_NO_MEDIA There is no media in the device.\r
535\r
536**/\r
537EFI_STATUS\r
538EFIAPI\r
539NvmeBlockIoFlushBlocks (\r
540 IN EFI_BLOCK_IO_PROTOCOL *This\r
541 )\r
542{\r
543 NVME_DEVICE_PRIVATE_DATA *Device;\r
544 EFI_STATUS Status;\r
545 EFI_TPL OldTpl;\r
546\r
547 //\r
548 // Check parameters.\r
549 //\r
550 if (This == NULL) {\r
551 return EFI_INVALID_PARAMETER;\r
552 }\r
553\r
554 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
555\r
556 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);\r
557\r
558 Status = NvmeFlush (Device);\r
559\r
560 gBS->RestoreTPL (OldTpl);\r
561\r
562 return Status;\r
563}\r