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