]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressBlockIo.c
MdeMdeModulePkg/NvmExpressDxe: Add NVM Express support.
[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
176 Status = EFI_SUCCESS;
177 Controller = Device->Controller;
178 BlockSize = Device->Media.BlockSize;
179
180 if (Controller->ControllerData->Mdts != 0) {
181 MaxTransferBlocks = (1 << (Controller->ControllerData->Mdts)) * (1 << (Controller->Cap.Mpsmin + 12)) / BlockSize;
182 } else {
183 MaxTransferBlocks = 1024;
184 }
185
186 while (Blocks > 0) {
187 if (Blocks > MaxTransferBlocks) {
188 Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, MaxTransferBlocks);
189
190 Blocks -= MaxTransferBlocks;
191 Buffer = (VOID *)(UINTN)((UINT64)(UINTN)Buffer + MaxTransferBlocks * BlockSize);
192 Lba += MaxTransferBlocks;
193 } else {
194 Status = ReadSectors (Device, (UINT64)(UINTN)Buffer, Lba, (UINT32)Blocks);
195 Blocks = 0;
196 }
197
198 if (EFI_ERROR(Status)) {
199 break;
200 }
201 }
202
203 DEBUG ((EFI_D_INFO, "NvmeRead() Lba = %8d, Blocks = %8d, BlockSize = %d Status = %r\n", Lba, Blocks, BlockSize, Status));
204
205 return Status;
206 }
207
208 /**
209 Write some blocks to the device.
210
211 @param Device The pointer to the NVME_DEVICE_PRIVATE_DATA data structure.
212 @param Buffer The buffer to be written into the device.
213 @param Lba The start block number.
214 @param Blocks Total block number to be written.
215
216 @retval EFI_SUCCESS Datum are written into the buffer.
217 @retval Others Fail to write all the datum.
218
219 **/
220 EFI_STATUS
221 NvmeWrite (
222 IN NVME_DEVICE_PRIVATE_DATA *Device,
223 IN VOID *Buffer,
224 IN UINT64 Lba,
225 IN UINTN Blocks
226 )
227 {
228 EFI_STATUS Status;
229 UINT32 BlockSize;
230 NVME_CONTROLLER_PRIVATE_DATA *Controller;
231 UINT32 MaxTransferBlocks;
232
233 Status = EFI_SUCCESS;
234 Controller = Device->Controller;
235 BlockSize = Device->Media.BlockSize;
236
237 if (Controller->ControllerData->Mdts != 0) {
238 MaxTransferBlocks = (1 << (Controller->ControllerData->Mdts)) * (1 << (Controller->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 = %8d, Blocks = %8d, BlockSize = %d Status = %r\n", Lba, 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 *Controller;
280 NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
281 NVM_EXPRESS_COMMAND Command;
282 NVM_EXPRESS_RESPONSE Response;
283 EFI_STATUS Status;
284
285 Controller = Device->Controller;
286
287 ZeroMem (&CommandPacket, sizeof(NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
288 ZeroMem (&Command, sizeof(NVM_EXPRESS_COMMAND));
289 ZeroMem (&Response, sizeof(NVM_EXPRESS_RESPONSE));
290
291 CommandPacket.NvmeCmd = &Command;
292 CommandPacket.NvmeResponse = &Response;
293
294 CommandPacket.NvmeCmd->Cdw0.Opcode = NVME_IO_FLUSH_OPC;
295 CommandPacket.NvmeCmd->Cdw0.Cid = Controller->Cid[1]++;
296 CommandPacket.NvmeCmd->Nsid = Device->NamespaceId;
297 CommandPacket.CommandTimeout = NVME_GENERIC_TIMEOUT;
298 CommandPacket.QueueId = NVME_IO_QUEUE;
299
300 Status = Controller->Passthru.PassThru (
301 &Controller->Passthru,
302 Device->NamespaceId,
303 0,
304 &CommandPacket,
305 NULL
306 );
307
308 return Status;
309 }
310
311
312 /**
313 Reset the Block Device.
314
315 @param This Indicates a pointer to the calling context.
316 @param ExtendedVerification Driver may perform diagnostics on reset.
317
318 @retval EFI_SUCCESS The device was reset.
319 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
320 not be reset.
321
322 **/
323 EFI_STATUS
324 EFIAPI
325 NvmeBlockIoReset (
326 IN EFI_BLOCK_IO_PROTOCOL *This,
327 IN BOOLEAN ExtendedVerification
328 )
329 {
330 EFI_TPL OldTpl;
331 NVME_CONTROLLER_PRIVATE_DATA *Private;
332 NVME_DEVICE_PRIVATE_DATA *Device;
333 EFI_STATUS Status;
334
335 if (This == NULL) {
336 return EFI_INVALID_PARAMETER;
337 }
338
339 //
340 // For Nvm Express subsystem, reset block device means reset controller.
341 //
342 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
343
344 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
345
346 Private = Device->Controller;
347
348 Status = NvmeControllerInit (Private);
349
350 gBS->RestoreTPL (OldTpl);
351
352 return Status;
353 }
354
355 /**
356 Read BufferSize bytes from Lba into Buffer.
357
358 @param This Indicates a pointer to the calling context.
359 @param MediaId Id of the media, changes every time the media is replaced.
360 @param Lba The starting Logical Block Address to read from.
361 @param BufferSize Size of Buffer, must be a multiple of device block size.
362 @param Buffer A pointer to the destination buffer for the data. The caller is
363 responsible for either having implicit or explicit ownership of the buffer.
364
365 @retval EFI_SUCCESS The data was read correctly from the device.
366 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
367 @retval EFI_NO_MEDIA There is no media in the device.
368 @retval EFI_MEDIA_CHANGED The MediaId does not matched the current device.
369 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
370 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
371 or the buffer is not on proper alignment.
372
373 **/
374 EFI_STATUS
375 EFIAPI
376 NvmeBlockIoReadBlocks (
377 IN EFI_BLOCK_IO_PROTOCOL *This,
378 IN UINT32 MediaId,
379 IN EFI_LBA Lba,
380 IN UINTN BufferSize,
381 OUT VOID *Buffer
382 )
383 {
384 NVME_DEVICE_PRIVATE_DATA *Device;
385 EFI_STATUS Status;
386 EFI_BLOCK_IO_MEDIA *Media;
387 UINTN BlockSize;
388 UINTN NumberOfBlocks;
389 UINTN IoAlign;
390 EFI_TPL OldTpl;
391
392 //
393 // Check parameters.
394 //
395 if (This == NULL) {
396 return EFI_INVALID_PARAMETER;
397 }
398
399 Media = This->Media;
400
401 if (MediaId != Media->MediaId) {
402 return EFI_MEDIA_CHANGED;
403 }
404
405 if (Buffer == NULL) {
406 return EFI_INVALID_PARAMETER;
407 }
408
409 if (BufferSize == 0) {
410 return EFI_SUCCESS;
411 }
412
413 BlockSize = Media->BlockSize;
414 if ((BufferSize % BlockSize) != 0) {
415 return EFI_BAD_BUFFER_SIZE;
416 }
417
418 NumberOfBlocks = BufferSize / BlockSize;
419 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
420 return EFI_INVALID_PARAMETER;
421 }
422
423 IoAlign = Media->IoAlign;
424 if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
425 return EFI_INVALID_PARAMETER;
426 }
427
428 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
429
430 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
431
432 Status = NvmeRead (Device, Buffer, Lba, NumberOfBlocks);
433
434 gBS->RestoreTPL (OldTpl);
435 return Status;
436 }
437
438 /**
439 Write BufferSize bytes from Lba into Buffer.
440
441 @param This Indicates a pointer to the calling context.
442 @param MediaId The media ID that the write request is for.
443 @param Lba The starting logical block address to be written. The caller is
444 responsible for writing to only legitimate locations.
445 @param BufferSize Size of Buffer, must be a multiple of device block size.
446 @param Buffer A pointer to the source buffer for the data.
447
448 @retval EFI_SUCCESS The data was written correctly to the device.
449 @retval EFI_WRITE_PROTECTED The device can not be written to.
450 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
451 @retval EFI_NO_MEDIA There is no media in the device.
452 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
453 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
454 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
455 or the buffer is not on proper alignment.
456
457 **/
458 EFI_STATUS
459 EFIAPI
460 NvmeBlockIoWriteBlocks (
461 IN EFI_BLOCK_IO_PROTOCOL *This,
462 IN UINT32 MediaId,
463 IN EFI_LBA Lba,
464 IN UINTN BufferSize,
465 IN VOID *Buffer
466 )
467 {
468 NVME_DEVICE_PRIVATE_DATA *Device;
469 EFI_STATUS Status;
470 EFI_BLOCK_IO_MEDIA *Media;
471 UINTN BlockSize;
472 UINTN NumberOfBlocks;
473 UINTN IoAlign;
474 EFI_TPL OldTpl;
475
476 //
477 // Check parameters.
478 //
479 if (This == NULL) {
480 return EFI_INVALID_PARAMETER;
481 }
482
483 Media = This->Media;
484
485 if (MediaId != Media->MediaId) {
486 return EFI_MEDIA_CHANGED;
487 }
488
489 if (Buffer == NULL) {
490 return EFI_INVALID_PARAMETER;
491 }
492
493 if (BufferSize == 0) {
494 return EFI_SUCCESS;
495 }
496
497 BlockSize = Media->BlockSize;
498 if ((BufferSize % BlockSize) != 0) {
499 return EFI_BAD_BUFFER_SIZE;
500 }
501
502 NumberOfBlocks = BufferSize / BlockSize;
503 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {
504 return EFI_INVALID_PARAMETER;
505 }
506
507 IoAlign = Media->IoAlign;
508 if (IoAlign > 0 && (((UINTN) Buffer & (IoAlign - 1)) != 0)) {
509 return EFI_INVALID_PARAMETER;
510 }
511
512 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
513
514 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
515
516 Status = NvmeWrite (Device, Buffer, Lba, NumberOfBlocks);
517
518 gBS->RestoreTPL (OldTpl);
519
520 return Status;
521 }
522
523 /**
524 Flush the Block Device.
525
526 @param This Indicates a pointer to the calling context.
527
528 @retval EFI_SUCCESS All outstanding data was written to the device.
529 @retval EFI_DEVICE_ERROR The device reported an error while writing back the data.
530 @retval EFI_NO_MEDIA There is no media in the device.
531
532 **/
533 EFI_STATUS
534 EFIAPI
535 NvmeBlockIoFlushBlocks (
536 IN EFI_BLOCK_IO_PROTOCOL *This
537 )
538 {
539 NVME_DEVICE_PRIVATE_DATA *Device;
540 EFI_STATUS Status;
541 EFI_TPL OldTpl;
542
543 //
544 // Check parameters.
545 //
546 if (This == NULL) {
547 return EFI_INVALID_PARAMETER;
548 }
549
550 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
551
552 Device = NVME_DEVICE_PRIVATE_DATA_FROM_BLOCK_IO (This);
553
554 Status = NvmeFlush (Device);
555
556 gBS->RestoreTPL (OldTpl);
557
558 return Status;
559 }