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