]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIo.c
fc04363eba41d2d92d13855adfb16933cc9b8679
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / DiskIoDxe / DiskIo.c
1 /** @file
2 DiskIo driver that lays on every BlockIo protocol in the system.
3 DiskIo converts a block oriented device to a byte oriented device.
4
5 Disk access may have to handle unaligned request about sector boundaries.
6 There are three cases:
7 UnderRun - The first byte is not on a sector boundary or the read request is
8 less than a sector in length.
9 Aligned - A read of N contiguous sectors.
10 OverRun - The last byte is not on a sector boundary.
11
12 Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
13 This program and the accompanying materials
14 are licensed and made available under the terms and conditions of the BSD License
15 which accompanies this distribution. The full text of the license may be found at
16 http://opensource.org/licenses/bsd-license.php
17
18 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
19 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20
21 **/
22
23 #include "DiskIo.h"
24
25 //
26 // Driver binding protocol implementation for DiskIo driver.
27 //
28 EFI_DRIVER_BINDING_PROTOCOL gDiskIoDriverBinding = {
29 DiskIoDriverBindingSupported,
30 DiskIoDriverBindingStart,
31 DiskIoDriverBindingStop,
32 0xa,
33 NULL,
34 NULL
35 };
36
37 //
38 // Template for DiskIo private data structure.
39 // The pointer to BlockIo protocol interface is assigned dynamically.
40 //
41 DISK_IO_PRIVATE_DATA gDiskIoPrivateDataTemplate = {
42 DISK_IO_PRIVATE_DATA_SIGNATURE,
43 {
44 EFI_DISK_IO_PROTOCOL_REVISION,
45 DiskIoReadDisk,
46 DiskIoWriteDisk
47 },
48 {
49 EFI_DISK_IO2_PROTOCOL_REVISION,
50 DiskIo2Cancel,
51 DiskIo2ReadDiskEx,
52 DiskIo2WriteDiskEx,
53 DiskIo2FlushDiskEx
54 }
55 };
56
57 /**
58 Test to see if this driver supports ControllerHandle.
59
60 @param This Protocol instance pointer.
61 @param ControllerHandle Handle of device to test
62 @param RemainingDevicePath Optional parameter use to pick a specific child
63 device to start.
64
65 @retval EFI_SUCCESS This driver supports this device
66 @retval EFI_ALREADY_STARTED This driver is already running on this device
67 @retval other This driver does not support this device
68
69 **/
70 EFI_STATUS
71 EFIAPI
72 DiskIoDriverBindingSupported (
73 IN EFI_DRIVER_BINDING_PROTOCOL *This,
74 IN EFI_HANDLE ControllerHandle,
75 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
76 )
77 {
78 EFI_STATUS Status;
79 EFI_BLOCK_IO_PROTOCOL *BlockIo;
80
81 //
82 // Open the IO Abstraction(s) needed to perform the supported test.
83 //
84 Status = gBS->OpenProtocol (
85 ControllerHandle,
86 &gEfiBlockIoProtocolGuid,
87 (VOID **) &BlockIo,
88 This->DriverBindingHandle,
89 ControllerHandle,
90 EFI_OPEN_PROTOCOL_BY_DRIVER
91 );
92 if (EFI_ERROR (Status)) {
93 return Status;
94 }
95
96 //
97 // Close the I/O Abstraction(s) used to perform the supported test.
98 //
99 gBS->CloseProtocol (
100 ControllerHandle,
101 &gEfiBlockIoProtocolGuid,
102 This->DriverBindingHandle,
103 ControllerHandle
104 );
105 return EFI_SUCCESS;
106 }
107
108
109 /**
110 Start this driver on ControllerHandle by opening a Block IO protocol and
111 installing a Disk IO protocol on ControllerHandle.
112
113 @param This Protocol instance pointer.
114 @param ControllerHandle Handle of device to bind driver to
115 @param RemainingDevicePath Optional parameter use to pick a specific child
116 device to start.
117
118 @retval EFI_SUCCESS This driver is added to ControllerHandle
119 @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
120 @retval other This driver does not support this device
121
122 **/
123 EFI_STATUS
124 EFIAPI
125 DiskIoDriverBindingStart (
126 IN EFI_DRIVER_BINDING_PROTOCOL *This,
127 IN EFI_HANDLE ControllerHandle,
128 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
129 )
130 {
131 EFI_STATUS Status;
132 DISK_IO_PRIVATE_DATA *Instance;
133 EFI_TPL OldTpl;
134
135 Instance = NULL;
136
137 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
138
139 //
140 // Connect to the Block IO and Block IO2 interface on ControllerHandle.
141 //
142 Status = gBS->OpenProtocol (
143 ControllerHandle,
144 &gEfiBlockIoProtocolGuid,
145 (VOID **) &gDiskIoPrivateDataTemplate.BlockIo,
146 This->DriverBindingHandle,
147 ControllerHandle,
148 EFI_OPEN_PROTOCOL_BY_DRIVER
149 );
150 if (EFI_ERROR (Status)) {
151 goto ErrorExit1;
152 }
153
154 Status = gBS->OpenProtocol (
155 ControllerHandle,
156 &gEfiBlockIo2ProtocolGuid,
157 (VOID **) &gDiskIoPrivateDataTemplate.BlockIo2,
158 This->DriverBindingHandle,
159 ControllerHandle,
160 EFI_OPEN_PROTOCOL_BY_DRIVER
161 );
162 if (EFI_ERROR (Status)) {
163 gDiskIoPrivateDataTemplate.BlockIo2 = NULL;
164 }
165
166 //
167 // Initialize the Disk IO device instance.
168 //
169 Instance = AllocateCopyPool (sizeof (DISK_IO_PRIVATE_DATA), &gDiskIoPrivateDataTemplate);
170 if (Instance == NULL) {
171 Status = EFI_OUT_OF_RESOURCES;
172 goto ErrorExit;
173 }
174
175 //
176 // The BlockSize and IoAlign of BlockIo and BlockIo2 should equal.
177 //
178 ASSERT ((Instance->BlockIo2 == NULL) ||
179 ((Instance->BlockIo->Media->IoAlign == Instance->BlockIo2->Media->IoAlign) &&
180 (Instance->BlockIo->Media->BlockSize == Instance->BlockIo2->Media->BlockSize)
181 ));
182
183 InitializeListHead (&Instance->TaskQueue);
184 EfiInitializeLock (&Instance->TaskQueueLock, TPL_NOTIFY);
185 Instance->SharedWorkingBuffer = AllocateAlignedPages (
186 EFI_SIZE_TO_PAGES (DATA_BUFFER_BLOCK_NUM * Instance->BlockIo->Media->BlockSize),
187 Instance->BlockIo->Media->IoAlign
188 );
189 if (Instance->SharedWorkingBuffer == NULL) {
190 Status = EFI_OUT_OF_RESOURCES;
191 goto ErrorExit;
192 }
193
194 //
195 // Install protocol interfaces for the Disk IO device.
196 //
197 if (Instance->BlockIo2 != NULL) {
198 Status = gBS->InstallMultipleProtocolInterfaces (
199 &ControllerHandle,
200 &gEfiDiskIoProtocolGuid, &Instance->DiskIo,
201 &gEfiDiskIo2ProtocolGuid, &Instance->DiskIo2,
202 NULL
203 );
204 } else {
205 Status = gBS->InstallMultipleProtocolInterfaces (
206 &ControllerHandle,
207 &gEfiDiskIoProtocolGuid, &Instance->DiskIo,
208 NULL
209 );
210 }
211
212 ErrorExit:
213 if (EFI_ERROR (Status)) {
214 if (Instance != NULL && Instance->SharedWorkingBuffer != NULL) {
215 FreeAlignedPages (
216 Instance->SharedWorkingBuffer,
217 EFI_SIZE_TO_PAGES (DATA_BUFFER_BLOCK_NUM * Instance->BlockIo->Media->BlockSize)
218 );
219 }
220
221 if (Instance != NULL) {
222 FreePool (Instance);
223 }
224
225 gBS->CloseProtocol (
226 ControllerHandle,
227 &gEfiBlockIoProtocolGuid,
228 This->DriverBindingHandle,
229 ControllerHandle
230 );
231 }
232
233 ErrorExit1:
234 gBS->RestoreTPL (OldTpl);
235 return Status;
236 }
237
238 /**
239 Stop this driver on ControllerHandle by removing Disk IO protocol and closing
240 the Block IO protocol on ControllerHandle.
241
242 @param This Protocol instance pointer.
243 @param ControllerHandle Handle of device to stop driver on
244 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
245 children is zero stop the entire bus driver.
246 @param ChildHandleBuffer List of Child Handles to Stop.
247
248 @retval EFI_SUCCESS This driver is removed ControllerHandle
249 @retval other This driver was not removed from this device
250
251 **/
252 EFI_STATUS
253 EFIAPI
254 DiskIoDriverBindingStop (
255 IN EFI_DRIVER_BINDING_PROTOCOL *This,
256 IN EFI_HANDLE ControllerHandle,
257 IN UINTN NumberOfChildren,
258 IN EFI_HANDLE *ChildHandleBuffer
259 )
260 {
261 EFI_STATUS Status;
262 EFI_DISK_IO_PROTOCOL *DiskIo;
263 EFI_DISK_IO2_PROTOCOL *DiskIo2;
264 DISK_IO_PRIVATE_DATA *Instance;
265 BOOLEAN AllTaskDone;
266
267 //
268 // Get our context back.
269 //
270 Status = gBS->OpenProtocol (
271 ControllerHandle,
272 &gEfiDiskIoProtocolGuid,
273 (VOID **) &DiskIo,
274 This->DriverBindingHandle,
275 ControllerHandle,
276 EFI_OPEN_PROTOCOL_GET_PROTOCOL
277 );
278 if (EFI_ERROR (Status)) {
279 return Status;
280 }
281 Status = gBS->OpenProtocol (
282 ControllerHandle,
283 &gEfiDiskIo2ProtocolGuid,
284 (VOID **) &DiskIo2,
285 This->DriverBindingHandle,
286 ControllerHandle,
287 EFI_OPEN_PROTOCOL_GET_PROTOCOL
288 );
289 if (EFI_ERROR (Status)) {
290 DiskIo2 = NULL;
291 }
292
293 Instance = DISK_IO_PRIVATE_DATA_FROM_DISK_IO (DiskIo);
294
295 if (DiskIo2 != NULL) {
296 //
297 // Call BlockIo2::Reset() to terminate any in-flight non-blocking I/O requests
298 //
299 ASSERT (Instance->BlockIo2 != NULL);
300 Status = Instance->BlockIo2->Reset (Instance->BlockIo2, FALSE);
301 if (EFI_ERROR (Status)) {
302 return Status;
303 }
304 Status = gBS->UninstallMultipleProtocolInterfaces (
305 ControllerHandle,
306 &gEfiDiskIoProtocolGuid, &Instance->DiskIo,
307 &gEfiDiskIo2ProtocolGuid, &Instance->DiskIo2,
308 NULL
309 );
310 } else {
311 Status = gBS->UninstallMultipleProtocolInterfaces (
312 ControllerHandle,
313 &gEfiDiskIoProtocolGuid, &Instance->DiskIo,
314 NULL
315 );
316 }
317 if (!EFI_ERROR (Status)) {
318
319 do {
320 EfiAcquireLock (&Instance->TaskQueueLock);
321 AllTaskDone = IsListEmpty (&Instance->TaskQueue);
322 EfiReleaseLock (&Instance->TaskQueueLock);
323 } while (!AllTaskDone);
324
325 FreeAlignedPages (
326 Instance->SharedWorkingBuffer,
327 EFI_SIZE_TO_PAGES (DATA_BUFFER_BLOCK_NUM * Instance->BlockIo->Media->BlockSize)
328 );
329
330 Status = gBS->CloseProtocol (
331 ControllerHandle,
332 &gEfiBlockIoProtocolGuid,
333 This->DriverBindingHandle,
334 ControllerHandle
335 );
336 ASSERT_EFI_ERROR (Status);
337 if (DiskIo2 != NULL) {
338 Status = gBS->CloseProtocol (
339 ControllerHandle,
340 &gEfiBlockIo2ProtocolGuid,
341 This->DriverBindingHandle,
342 ControllerHandle
343 );
344 ASSERT_EFI_ERROR (Status);
345 }
346
347 FreePool (Instance);
348 }
349
350 return Status;
351 }
352
353
354 /**
355 Destroy the sub task.
356
357 @param Subtask Subtask.
358
359 @return LIST_ENTRY * Pointer to the next link of subtask.
360 **/
361 LIST_ENTRY *
362 DiskIoDestroySubtask (
363 IN DISK_IO_PRIVATE_DATA *Instance,
364 IN DISK_IO_SUBTASK *Subtask
365 )
366 {
367 LIST_ENTRY *Link;
368 Link = RemoveEntryList (&Subtask->Link);
369 if (!Subtask->Blocking) {
370 if (Subtask->WorkingBuffer != NULL) {
371 FreeAlignedPages (
372 Subtask->WorkingBuffer,
373 Subtask->Length < Instance->BlockIo->Media->BlockSize
374 ? EFI_SIZE_TO_PAGES (Instance->BlockIo->Media->BlockSize)
375 : EFI_SIZE_TO_PAGES (Subtask->Length)
376 );
377 }
378 if (Subtask->BlockIo2Token.Event != NULL) {
379 gBS->CloseEvent (Subtask->BlockIo2Token.Event);
380 }
381 }
382 FreePool (Subtask);
383
384 return Link;
385 }
386
387 /**
388 The callback for the BlockIo2 ReadBlocksEx/WriteBlocksEx.
389 @param Event Event whose notification function is being invoked.
390 @param Context The pointer to the notification function's context,
391 which points to the DISK_IO_SUBTASK instance.
392 **/
393 VOID
394 EFIAPI
395 DiskIo2OnReadWriteComplete (
396 IN EFI_EVENT Event,
397 IN VOID *Context
398 )
399 {
400 DISK_IO_SUBTASK *Subtask;
401 DISK_IO2_TASK *Task;
402 EFI_STATUS TransactionStatus;
403 DISK_IO_PRIVATE_DATA *Instance;
404
405 gBS->CloseEvent (Event);
406
407 Subtask = (DISK_IO_SUBTASK *) Context;
408 TransactionStatus = Subtask->BlockIo2Token.TransactionStatus;
409 Task = Subtask->Task;
410 Instance = Task->Instance;
411
412 ASSERT (Subtask->Signature == DISK_IO_SUBTASK_SIGNATURE);
413 ASSERT (Instance->Signature == DISK_IO_PRIVATE_DATA_SIGNATURE);
414 ASSERT (Task->Signature == DISK_IO2_TASK_SIGNATURE);
415
416 if (Subtask->WorkingBuffer != NULL) {
417 if (!EFI_ERROR (TransactionStatus) && (Task->Token != NULL) && !Subtask->Write) {
418 CopyMem (Subtask->Buffer, Subtask->WorkingBuffer + Subtask->Offset, Subtask->Length);
419 }
420
421 //
422 // The WorkingBuffer of blocking subtask either points to SharedWorkingBuffer
423 // or will be used by non-blocking subtask which will be freed below.
424 //
425 if (!Subtask->Blocking) {
426 FreeAlignedPages (
427 Subtask->WorkingBuffer,
428 Subtask->Length < Instance->BlockIo->Media->BlockSize
429 ? EFI_SIZE_TO_PAGES (Instance->BlockIo->Media->BlockSize)
430 : EFI_SIZE_TO_PAGES (Subtask->Length)
431 );
432 }
433 }
434 RemoveEntryList (&Subtask->Link);
435 FreePool (Subtask);
436
437 if (EFI_ERROR (TransactionStatus) || IsListEmpty (&Task->Subtasks)) {
438 if (Task->Token != NULL) {
439 //
440 // Signal error status once the subtask is failed.
441 // Or signal the last status once the last subtask is finished.
442 //
443 Task->Token->TransactionStatus = TransactionStatus;
444 gBS->SignalEvent (Task->Token->Event);
445
446 //
447 // Mark token to NULL
448 //
449 Task->Token = NULL;
450 }
451 }
452
453 if (IsListEmpty (&Task->Subtasks)) {
454 EfiAcquireLock (&Instance->TaskQueueLock);
455 RemoveEntryList (&Task->Link);
456 EfiReleaseLock (&Instance->TaskQueueLock);
457
458 FreePool (Task);
459 }
460 }
461
462 /**
463 Create the subtask.
464
465 @param Write TRUE: Write request; FALSE: Read request.
466 @param Lba The starting logical block address to read from on the device.
467 @param Offset The starting byte offset to read from the LBA.
468 @param Length The number of bytes to read from the device.
469 @param WorkingBuffer The aligned buffer to hold the data for reading or writing.
470 @param Buffer The buffer to hold the data for reading or writing.
471 @param Blocking TRUE: Blocking request; FALSE: Non-blocking request.
472
473 @return A pointer to the created subtask.
474 **/
475 DISK_IO_SUBTASK *
476 DiskIoCreateSubtask (
477 IN BOOLEAN Write,
478 IN UINT64 Lba,
479 IN UINT32 Offset,
480 IN UINTN Length,
481 IN VOID *WorkingBuffer, OPTIONAL
482 IN VOID *Buffer,
483 IN BOOLEAN Blocking
484 )
485 {
486 DISK_IO_SUBTASK *Subtask;
487 EFI_STATUS Status;
488
489 Subtask = AllocateZeroPool (sizeof (DISK_IO_SUBTASK));
490 if (Subtask == NULL) {
491 return NULL;
492 }
493 Subtask->Signature = DISK_IO_SUBTASK_SIGNATURE;
494 Subtask->Write = Write;
495 Subtask->Lba = Lba;
496 Subtask->Offset = Offset;
497 Subtask->Length = Length;
498 Subtask->WorkingBuffer = WorkingBuffer;
499 Subtask->Buffer = Buffer;
500 Subtask->Blocking = Blocking;
501 if (!Blocking) {
502 Status = gBS->CreateEvent (
503 EVT_NOTIFY_SIGNAL,
504 TPL_NOTIFY,
505 DiskIo2OnReadWriteComplete,
506 Subtask,
507 &Subtask->BlockIo2Token.Event
508 );
509 if (EFI_ERROR (Status)) {
510 FreePool (Subtask);
511 return NULL;
512 }
513 }
514 DEBUG ((
515 EFI_D_BLKIO,
516 " %c:Lba/Offset/Length/WorkingBuffer/Buffer = %016lx/%08x/%08x/%08x/%08x\n",
517 Write ? 'W': 'R', Lba, Offset, Length, WorkingBuffer, Buffer
518 ));
519
520 return Subtask;
521 }
522
523 /**
524 Create the subtask list.
525
526 @param Instance Pointer to the DISK_IO_PRIVATE_DATA.
527 @param Write TRUE: Write request; FALSE: Read request.
528 @param Offset The starting byte offset to read from the device.
529 @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device.
530 @param Buffer A pointer to the buffer for the data.
531 @param Blocking TRUE: Blocking request; FALSE: Non-blocking request.
532 @param SharedWorkingBuffer The aligned buffer to hold the data for reading or writing.
533 @param Subtasks The subtask list header.
534
535 @retval TRUE The subtask list is created successfully.
536 @retval FALSE The subtask list is not created.
537 **/
538 BOOLEAN
539 DiskIoCreateSubtaskList (
540 IN DISK_IO_PRIVATE_DATA *Instance,
541 IN BOOLEAN Write,
542 IN UINT64 Offset,
543 IN UINTN BufferSize,
544 IN VOID *Buffer,
545 IN BOOLEAN Blocking,
546 IN VOID *SharedWorkingBuffer,
547 IN OUT LIST_ENTRY *Subtasks
548 )
549 {
550 UINT32 BlockSize;
551 UINT32 IoAlign;
552 UINT64 Lba;
553 UINT64 OverRunLba;
554 UINT32 UnderRun;
555 UINT32 OverRun;
556 UINT8 *BufferPtr;
557 UINTN Length;
558 UINTN DataBufferSize;
559 DISK_IO_SUBTASK *Subtask;
560 VOID *WorkingBuffer;
561 LIST_ENTRY *Link;
562
563 DEBUG ((EFI_D_BLKIO, "DiskIo: Create subtasks for task: Offset/BufferSize/Buffer = %016lx/%08x/%08x\n", Offset, BufferSize, Buffer));
564
565 BlockSize = Instance->BlockIo->Media->BlockSize;
566 IoAlign = Instance->BlockIo->Media->IoAlign;
567 if (IoAlign == 0) {
568 IoAlign = 1;
569 }
570
571 Lba = DivU64x32Remainder (Offset, BlockSize, &UnderRun);
572 BufferPtr = (UINT8 *) Buffer;
573
574 //
575 // Special handling for zero BufferSize
576 //
577 if (BufferSize == 0) {
578 Subtask = DiskIoCreateSubtask (Write, Lba, UnderRun, 0, NULL, BufferPtr, Blocking);
579 if (Subtask == NULL) {
580 goto Done;
581 }
582 InsertTailList (Subtasks, &Subtask->Link);
583 return TRUE;
584 }
585
586 if (UnderRun != 0) {
587 Length = MIN (BlockSize - UnderRun, BufferSize);
588 if (Blocking) {
589 WorkingBuffer = SharedWorkingBuffer;
590 } else {
591 WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BlockSize), IoAlign);
592 if (WorkingBuffer == NULL) {
593 goto Done;
594 }
595 }
596 if (Write) {
597 //
598 // A half write operation can be splitted to a blocking block-read and half write operation
599 // This can simplify the sub task processing logic
600 //
601 Subtask = DiskIoCreateSubtask (FALSE, Lba, 0, BlockSize, NULL, WorkingBuffer, TRUE);
602 if (Subtask == NULL) {
603 goto Done;
604 }
605 InsertTailList (Subtasks, &Subtask->Link);
606 }
607
608 Subtask = DiskIoCreateSubtask (Write, Lba, UnderRun, Length, WorkingBuffer, BufferPtr, Blocking);
609 if (Subtask == NULL) {
610 goto Done;
611 }
612 InsertTailList (Subtasks, &Subtask->Link);
613
614 BufferPtr += Length;
615 Offset += Length;
616 BufferSize -= Length;
617 Lba ++;
618 }
619
620 OverRunLba = Lba + DivU64x32Remainder (BufferSize, BlockSize, &OverRun);
621 BufferSize -= OverRun;
622
623 if (OverRun != 0) {
624 if (Blocking) {
625 WorkingBuffer = SharedWorkingBuffer;
626 } else {
627 WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BlockSize), IoAlign);
628 if (WorkingBuffer == NULL) {
629 goto Done;
630 }
631 }
632 if (Write) {
633 //
634 // A half write operation can be splitted to a blocking block-read and half write operation
635 // This can simplify the sub task processing logic
636 //
637 Subtask = DiskIoCreateSubtask (FALSE, OverRunLba, 0, BlockSize, NULL, WorkingBuffer, TRUE);
638 if (Subtask == NULL) {
639 goto Done;
640 }
641 InsertTailList (Subtasks, &Subtask->Link);
642 }
643
644 Subtask = DiskIoCreateSubtask (Write, OverRunLba, 0, OverRun, WorkingBuffer, BufferPtr, Blocking);
645 if (Subtask == NULL) {
646 goto Done;
647 }
648 InsertTailList (Subtasks, &Subtask->Link);
649 }
650
651 if (OverRunLba > Lba) {
652 //
653 // If the DiskIo maps directly to a BlockIo device do the read.
654 //
655 if (ALIGN_POINTER (BufferPtr, IoAlign) == BufferPtr) {
656 Subtask = DiskIoCreateSubtask (Write, Lba, 0, BufferSize, NULL, BufferPtr, Blocking);
657 if (Subtask == NULL) {
658 goto Done;
659 }
660 InsertTailList (Subtasks, &Subtask->Link);
661
662 BufferPtr += BufferSize;
663 Offset += BufferSize;
664 BufferSize -= BufferSize;
665
666 } else {
667 if (Blocking) {
668 //
669 // Use the allocated buffer instead of the original buffer
670 // to avoid alignment issue.
671 //
672 for (; Lba < OverRunLba; Lba += DATA_BUFFER_BLOCK_NUM) {
673 DataBufferSize = MIN (BufferSize, DATA_BUFFER_BLOCK_NUM * BlockSize);
674
675 Subtask = DiskIoCreateSubtask (Write, Lba, 0, DataBufferSize, SharedWorkingBuffer, BufferPtr, Blocking);
676 if (Subtask == NULL) {
677 goto Done;
678 }
679 InsertTailList (Subtasks, &Subtask->Link);
680
681 BufferPtr += DataBufferSize;
682 Offset += DataBufferSize;
683 BufferSize -= DataBufferSize;
684 }
685 } else {
686 WorkingBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), IoAlign);
687 if (WorkingBuffer == NULL) {
688 //
689 // If there is not enough memory, downgrade to blocking access
690 //
691 DEBUG ((EFI_D_VERBOSE, "DiskIo: No enough memory so downgrade to blocking access\n"));
692 if (!DiskIoCreateSubtaskList (Instance, Write, Offset, BufferSize, BufferPtr, TRUE, SharedWorkingBuffer, Subtasks)) {
693 goto Done;
694 }
695 } else {
696 Subtask = DiskIoCreateSubtask (Write, Lba, 0, BufferSize, WorkingBuffer, BufferPtr, Blocking);
697 if (Subtask == NULL) {
698 goto Done;
699 }
700 InsertTailList (Subtasks, &Subtask->Link);
701 }
702
703 BufferPtr += BufferSize;
704 Offset += BufferSize;
705 BufferSize -= BufferSize;
706 }
707 }
708 }
709
710 ASSERT (BufferSize == 0);
711
712 return TRUE;
713
714 Done:
715 //
716 // Remove all the subtasks.
717 //
718 for (Link = GetFirstNode (Subtasks); !IsNull (Subtasks, Link); ) {
719 Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE);
720 Link = DiskIoDestroySubtask (Instance, Subtask);
721 }
722 return FALSE;
723 }
724
725 /**
726 Terminate outstanding asynchronous requests to a device.
727
728 @param This Indicates a pointer to the calling context.
729
730 @retval EFI_SUCCESS All outstanding requests were successfully terminated.
731 @retval EFI_DEVICE_ERROR The device reported an error while performing the cancel
732 operation.
733 **/
734 EFI_STATUS
735 EFIAPI
736 DiskIo2Cancel (
737 IN EFI_DISK_IO2_PROTOCOL *This
738 )
739 {
740 DISK_IO_PRIVATE_DATA *Instance;
741 DISK_IO2_TASK *Task;
742 LIST_ENTRY *Link;
743
744 Instance = DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This);
745
746 EfiAcquireLock (&Instance->TaskQueueLock);
747
748 for (Link = GetFirstNode (&Instance->TaskQueue)
749 ; !IsNull (&Instance->TaskQueue, Link)
750 ; Link = GetNextNode (&Instance->TaskQueue, Link)
751 ) {
752 Task = CR (Link, DISK_IO2_TASK, Link, DISK_IO2_TASK_SIGNATURE);
753
754 if (Task->Token != NULL) {
755 Task->Token->TransactionStatus = EFI_ABORTED;
756 gBS->SignalEvent (Task->Token->Event);
757 //
758 // Set Token to NULL so that the further BlockIo2 responses will be ignored
759 //
760 Task->Token = NULL;
761 }
762 }
763
764 EfiReleaseLock (&Instance->TaskQueueLock);
765
766 return EFI_SUCCESS;
767 }
768
769 /**
770 Common routine to access the disk.
771
772 @param Instance Pointer to the DISK_IO_PRIVATE_DATA.
773 @param Write TRUE: Write operation; FALSE: Read operation.
774 @param MediaId ID of the medium to access.
775 @param Offset The starting byte offset on the logical block I/O device to access.
776 @param Token A pointer to the token associated with the transaction.
777 If this field is NULL, synchronous/blocking IO is performed.
778 @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device.
779 @param Buffer A pointer to the destination buffer for the data.
780 The caller is responsible either having implicit or explicit ownership of the buffer.
781 **/
782 EFI_STATUS
783 DiskIo2ReadWriteDisk (
784 IN DISK_IO_PRIVATE_DATA *Instance,
785 IN BOOLEAN Write,
786 IN UINT32 MediaId,
787 IN UINT64 Offset,
788 IN EFI_DISK_IO2_TOKEN *Token,
789 IN UINTN BufferSize,
790 IN UINT8 *Buffer
791 )
792 {
793 EFI_STATUS Status;
794 EFI_BLOCK_IO_PROTOCOL *BlockIo;
795 EFI_BLOCK_IO2_PROTOCOL *BlockIo2;
796 EFI_BLOCK_IO_MEDIA *Media;
797 LIST_ENTRY *Link;
798 LIST_ENTRY Subtasks;
799 DISK_IO_SUBTASK *Subtask;
800 DISK_IO2_TASK *Task;
801 BOOLEAN TaskQueueEmpty;
802 EFI_TPL OldTpl;
803 BOOLEAN Blocking;
804 LIST_ENTRY *SubtasksPtr;
805
806 Task = NULL;
807 BlockIo = Instance->BlockIo;
808 BlockIo2 = Instance->BlockIo2;
809 Media = BlockIo->Media;
810 Status = EFI_SUCCESS;
811 Blocking = ((Token == NULL) || (Token->Event == NULL));
812
813 if (Media->MediaId != MediaId) {
814 return EFI_MEDIA_CHANGED;
815 }
816
817 if (Write && Media->ReadOnly) {
818 return EFI_WRITE_PROTECTED;
819 }
820
821 if (Blocking) {
822 //
823 // Wait till pending async task is completed.
824 //
825 do {
826 EfiAcquireLock (&Instance->TaskQueueLock);
827 TaskQueueEmpty = IsListEmpty (&Instance->TaskQueue);
828 EfiReleaseLock (&Instance->TaskQueueLock);
829 } while (!TaskQueueEmpty);
830
831 SubtasksPtr = &Subtasks;
832 } else {
833 Task = AllocatePool (sizeof (DISK_IO2_TASK));
834 if (Task == NULL) {
835 return EFI_OUT_OF_RESOURCES;
836 }
837
838 EfiAcquireLock (&Instance->TaskQueueLock);
839 InsertTailList (&Instance->TaskQueue, &Task->Link);
840 EfiReleaseLock (&Instance->TaskQueueLock);
841
842 Task->Signature = DISK_IO2_TASK_SIGNATURE;
843 Task->Instance = Instance;
844 Task->Token = Token;
845
846 SubtasksPtr = &Task->Subtasks;
847 }
848
849 InitializeListHead (SubtasksPtr);
850 if (!DiskIoCreateSubtaskList (Instance, Write, Offset, BufferSize, Buffer, Blocking, Instance->SharedWorkingBuffer, SubtasksPtr)) {
851 if (Task != NULL) {
852 FreePool (Task);
853 }
854 return EFI_OUT_OF_RESOURCES;
855 }
856 ASSERT (!IsListEmpty (SubtasksPtr));
857
858 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
859 for (Link = GetFirstNode (SubtasksPtr); !IsNull (SubtasksPtr, Link); Link = GetNextNode (SubtasksPtr, Link)) {
860 Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE);
861
862 if (!Subtask->Blocking) {
863 Subtask->Task = Task;
864 }
865
866 if (Subtask->Write) {
867 //
868 // Write
869 //
870 if (Subtask->WorkingBuffer != NULL) {
871 //
872 // A sub task before this one should be a block read operation, causing the WorkingBuffer filled with the entire one block data.
873 //
874 CopyMem (Subtask->WorkingBuffer + Subtask->Offset, Subtask->Buffer, Subtask->Length);
875 }
876
877 if (Subtask->Blocking) {
878 Status = BlockIo->WriteBlocks (
879 BlockIo,
880 MediaId,
881 Subtask->Lba,
882 Subtask->Length < Media->BlockSize ? Media->BlockSize : Subtask->Length,
883 (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer
884 );
885 } else {
886 Status = BlockIo2->WriteBlocksEx (
887 BlockIo2,
888 MediaId,
889 Subtask->Lba,
890 &Subtask->BlockIo2Token,
891 Subtask->Length < Media->BlockSize ? Media->BlockSize : Subtask->Length,
892 (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer
893 );
894 }
895
896 } else {
897 //
898 // Read
899 //
900 if (Subtask->Blocking) {
901 Status = BlockIo->ReadBlocks (
902 BlockIo,
903 MediaId,
904 Subtask->Lba,
905 Subtask->Length < Media->BlockSize ? Media->BlockSize : Subtask->Length,
906 (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer
907 );
908 if (!EFI_ERROR (Status) && (Subtask->WorkingBuffer != NULL)) {
909 CopyMem (Subtask->Buffer, Subtask->WorkingBuffer + Subtask->Offset, Subtask->Length);
910 }
911 } else {
912 Status = BlockIo2->ReadBlocksEx (
913 BlockIo2,
914 MediaId,
915 Subtask->Lba,
916 &Subtask->BlockIo2Token,
917 Subtask->Length < Media->BlockSize ? Media->BlockSize : Subtask->Length,
918 (Subtask->WorkingBuffer != NULL) ? Subtask->WorkingBuffer : Subtask->Buffer
919 );
920 }
921 }
922
923 if (EFI_ERROR (Status)) {
924 break;
925 }
926 }
927
928 gBS->RaiseTPL (TPL_NOTIFY);
929
930 //
931 // Remove all the remaining subtasks when failure.
932 // We shouldn't remove all the tasks because the non-blocking requests have been submitted and cannot be canceled.
933 //
934 if (EFI_ERROR (Status)) {
935 while (!IsNull (SubtasksPtr, Link)) {
936 Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE);
937 Link = DiskIoDestroySubtask (Instance, Subtask);
938 }
939 }
940
941 //
942 // Remove all the blocking subtasks because the non-blocking callback only removes the non-blocking subtask.
943 //
944 for (Link = GetFirstNode (SubtasksPtr); !IsNull (SubtasksPtr, Link); ) {
945 Subtask = CR (Link, DISK_IO_SUBTASK, Link, DISK_IO_SUBTASK_SIGNATURE);
946 if (Subtask->Blocking) {
947 Link = DiskIoDestroySubtask (Instance, Subtask);
948 } else {
949 Link = GetNextNode (SubtasksPtr, Link);
950 }
951 }
952
953 //
954 // It's possible that the callback runs before raising TPL to NOTIFY,
955 // so the subtasks list only contains blocking subtask.
956 // Remove the Task after the blocking subtasks are removed in above.
957 //
958 if (!Blocking && IsListEmpty (SubtasksPtr)) {
959 EfiAcquireLock (&Instance->TaskQueueLock);
960 RemoveEntryList (&Task->Link);
961 EfiReleaseLock (&Instance->TaskQueueLock);
962
963 if (Task->Token != NULL) {
964 //
965 // Task->Token should be set to NULL by the DiskIo2OnReadWriteComplete
966 // It it's not, that means the non-blocking request was downgraded to blocking request.
967 //
968 DEBUG ((EFI_D_VERBOSE, "DiskIo: Non-blocking request was downgraded to blocking request, signal event directly.\n"));
969 Task->Token->TransactionStatus = Status;
970 gBS->SignalEvent (Task->Token->Event);
971 }
972
973 FreePool (Task);
974 }
975
976 gBS->RestoreTPL (OldTpl);
977
978 return Status;
979 }
980
981 /**
982 Reads a specified number of bytes from a device.
983
984 @param This Indicates a pointer to the calling context.
985 @param MediaId ID of the medium to be read.
986 @param Offset The starting byte offset on the logical block I/O device to read from.
987 @param Token A pointer to the token associated with the transaction.
988 If this field is NULL, synchronous/blocking IO is performed.
989 @param BufferSize The size in bytes of Buffer. The number of bytes to read from the device.
990 @param Buffer A pointer to the destination buffer for the data.
991 The caller is responsible either having implicit or explicit ownership of the buffer.
992
993 @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was read correctly from the device.
994 If Event is not NULL (asynchronous I/O): The request was successfully queued for processing.
995 Event will be signaled upon completion.
996 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
997 @retval EFI_NO_MEDIA There is no medium in the device.
998 @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium.
999 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not valid for the device.
1000 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
1001
1002 **/
1003 EFI_STATUS
1004 EFIAPI
1005 DiskIo2ReadDiskEx (
1006 IN EFI_DISK_IO2_PROTOCOL *This,
1007 IN UINT32 MediaId,
1008 IN UINT64 Offset,
1009 IN OUT EFI_DISK_IO2_TOKEN *Token,
1010 IN UINTN BufferSize,
1011 OUT VOID *Buffer
1012 )
1013 {
1014 return DiskIo2ReadWriteDisk (
1015 DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This),
1016 FALSE, MediaId, Offset, Token, BufferSize, (UINT8 *) Buffer
1017 );
1018 }
1019
1020 /**
1021 Writes a specified number of bytes to a device.
1022
1023 @param This Indicates a pointer to the calling context.
1024 @param MediaId ID of the medium to be written.
1025 @param Offset The starting byte offset on the logical block I/O device to write to.
1026 @param Token A pointer to the token associated with the transaction.
1027 If this field is NULL, synchronous/blocking IO is performed.
1028 @param BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1029 @param Buffer A pointer to the buffer containing the data to be written.
1030
1031 @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was written correctly to the device.
1032 If Event is not NULL (asynchronous I/O): The request was successfully queued for processing.
1033 Event will be signaled upon completion.
1034 @retval EFI_WRITE_PROTECTED The device cannot be written to.
1035 @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation.
1036 @retval EFI_NO_MEDIA There is no medium in the device.
1037 @retval EFI_MEDIA_CHNAGED The MediaId is not for the current medium.
1038 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not valid for the device.
1039 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
1040
1041 **/
1042 EFI_STATUS
1043 EFIAPI
1044 DiskIo2WriteDiskEx (
1045 IN EFI_DISK_IO2_PROTOCOL *This,
1046 IN UINT32 MediaId,
1047 IN UINT64 Offset,
1048 IN OUT EFI_DISK_IO2_TOKEN *Token,
1049 IN UINTN BufferSize,
1050 IN VOID *Buffer
1051 )
1052 {
1053 return DiskIo2ReadWriteDisk (
1054 DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This),
1055 TRUE, MediaId, Offset, Token, BufferSize, (UINT8 *) Buffer
1056 );
1057 }
1058
1059 /**
1060 The callback for the BlockIo2 FlushBlocksEx.
1061 @param Event Event whose notification function is being invoked.
1062 @param Context The pointer to the notification function's context,
1063 which points to the DISK_IO2_FLUSH_TASK instance.
1064 **/
1065 VOID
1066 EFIAPI
1067 DiskIo2OnFlushComplete (
1068 IN EFI_EVENT Event,
1069 IN VOID *Context
1070 )
1071 {
1072 DISK_IO2_FLUSH_TASK *Task;
1073
1074 gBS->CloseEvent (Event);
1075
1076 Task = (DISK_IO2_FLUSH_TASK *) Context;
1077 ASSERT (Task->Signature == DISK_IO2_FLUSH_TASK_SIGNATURE);
1078 Task->Token->TransactionStatus = Task->BlockIo2Token.TransactionStatus;
1079 gBS->SignalEvent (Task->Token->Event);
1080 }
1081
1082 /**
1083 Flushes all modified data to the physical device.
1084
1085 @param This Indicates a pointer to the calling context.
1086 @param Token A pointer to the token associated with the transaction.
1087 If this field is NULL, synchronous/blocking IO is performed.
1088
1089 @retval EFI_SUCCESS If Event is NULL (blocking I/O): The data was flushed successfully to the device.
1090 If Event is not NULL (asynchronous I/O): The request was successfully queued for processing.
1091 Event will be signaled upon completion.
1092 @retval EFI_WRITE_PROTECTED The device cannot be written to.
1093 @retval EFI_DEVICE_ERROR The device reported an error while performing the write operation.
1094 @retval EFI_NO_MEDIA There is no medium in the device.
1095 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
1096 **/
1097 EFI_STATUS
1098 EFIAPI
1099 DiskIo2FlushDiskEx (
1100 IN EFI_DISK_IO2_PROTOCOL *This,
1101 IN OUT EFI_DISK_IO2_TOKEN *Token
1102 )
1103 {
1104 EFI_STATUS Status;
1105 DISK_IO2_FLUSH_TASK *Task;
1106 DISK_IO_PRIVATE_DATA *Private;
1107
1108 Private = DISK_IO_PRIVATE_DATA_FROM_DISK_IO2 (This);
1109
1110 if ((Token != NULL) && (Token->Event != NULL)) {
1111 Task = AllocatePool (sizeof (DISK_IO2_FLUSH_TASK));
1112 if (Task == NULL) {
1113 return EFI_OUT_OF_RESOURCES;
1114 }
1115
1116 Status = gBS->CreateEvent (
1117 EVT_NOTIFY_SIGNAL,
1118 TPL_CALLBACK,
1119 DiskIo2OnFlushComplete,
1120 Task,
1121 &Task->BlockIo2Token.Event
1122 );
1123 if (EFI_ERROR (Status)) {
1124 FreePool (Task);
1125 return Status;
1126 }
1127 Task->Signature = DISK_IO2_FLUSH_TASK_SIGNATURE;
1128 Status = Private->BlockIo2->FlushBlocksEx (Private->BlockIo2, &Task->BlockIo2Token);
1129 if (EFI_ERROR (Status)) {
1130 gBS->CloseEvent (Task->BlockIo2Token.Event);
1131 FreePool (Task);
1132 }
1133 } else {
1134 Status = Private->BlockIo2->FlushBlocksEx (Private->BlockIo2, NULL);
1135 }
1136
1137 return Status;
1138 }
1139
1140 /**
1141 Read BufferSize bytes from Offset into Buffer.
1142 Reads may support reads that are not aligned on
1143 sector boundaries. There are three cases:
1144 UnderRun - The first byte is not on a sector boundary or the read request is
1145 less than a sector in length.
1146 Aligned - A read of N contiguous sectors.
1147 OverRun - The last byte is not on a sector boundary.
1148
1149 @param This Protocol instance pointer.
1150 @param MediaId Id of the media, changes every time the media is replaced.
1151 @param Offset The starting byte offset to read from
1152 @param BufferSize Size of Buffer
1153 @param Buffer Buffer containing read data
1154
1155 @retval EFI_SUCCESS The data was read correctly from the device.
1156 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
1157 @retval EFI_NO_MEDIA There is no media in the device.
1158 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1159 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1160 valid for the device.
1161
1162 **/
1163 EFI_STATUS
1164 EFIAPI
1165 DiskIoReadDisk (
1166 IN EFI_DISK_IO_PROTOCOL *This,
1167 IN UINT32 MediaId,
1168 IN UINT64 Offset,
1169 IN UINTN BufferSize,
1170 OUT VOID *Buffer
1171 )
1172 {
1173 return DiskIo2ReadWriteDisk (
1174 DISK_IO_PRIVATE_DATA_FROM_DISK_IO (This),
1175 FALSE, MediaId, Offset, NULL, BufferSize, (UINT8 *) Buffer
1176 );
1177 }
1178
1179
1180 /**
1181 Writes BufferSize bytes from Buffer into Offset.
1182 Writes may require a read modify write to support writes that are not
1183 aligned on sector boundaries. There are three cases:
1184 UnderRun - The first byte is not on a sector boundary or the write request
1185 is less than a sector in length. Read modify write is required.
1186 Aligned - A write of N contiguous sectors.
1187 OverRun - The last byte is not on a sector boundary. Read modified write
1188 required.
1189
1190 @param This Protocol instance pointer.
1191 @param MediaId Id of the media, changes every time the media is replaced.
1192 @param Offset The starting byte offset to read from
1193 @param BufferSize Size of Buffer
1194 @param Buffer Buffer containing read data
1195
1196 @retval EFI_SUCCESS The data was written correctly to the device.
1197 @retval EFI_WRITE_PROTECTED The device can not be written to.
1198 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
1199 @retval EFI_NO_MEDIA There is no media in the device.
1200 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
1201 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1202 valid for the device.
1203
1204 **/
1205 EFI_STATUS
1206 EFIAPI
1207 DiskIoWriteDisk (
1208 IN EFI_DISK_IO_PROTOCOL *This,
1209 IN UINT32 MediaId,
1210 IN UINT64 Offset,
1211 IN UINTN BufferSize,
1212 IN VOID *Buffer
1213 )
1214 {
1215 return DiskIo2ReadWriteDisk (
1216 DISK_IO_PRIVATE_DATA_FROM_DISK_IO (This),
1217 TRUE, MediaId, Offset, NULL, BufferSize, (UINT8 *) Buffer
1218 );
1219 }
1220
1221 /**
1222 The user Entry Point for module DiskIo. The user code starts with this function.
1223
1224 @param[in] ImageHandle The firmware allocated handle for the EFI image.
1225 @param[in] SystemTable A pointer to the EFI System Table.
1226
1227 @retval EFI_SUCCESS The entry point is executed successfully.
1228 @retval other Some error occurs when executing this entry point.
1229
1230 **/
1231 EFI_STATUS
1232 EFIAPI
1233 InitializeDiskIo (
1234 IN EFI_HANDLE ImageHandle,
1235 IN EFI_SYSTEM_TABLE *SystemTable
1236 )
1237 {
1238 EFI_STATUS Status;
1239
1240 //
1241 // Install driver model protocol(s).
1242 //
1243 Status = EfiLibInstallDriverBindingComponentName2 (
1244 ImageHandle,
1245 SystemTable,
1246 &gDiskIoDriverBinding,
1247 ImageHandle,
1248 &gDiskIoComponentName,
1249 &gDiskIoComponentName2
1250 );
1251 ASSERT_EFI_ERROR (Status);
1252
1253 return Status;
1254 }