]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Disk/DiskIo/Dxe/diskio.c
d5efd634cfe3ae03ecb04de096a555f5c8cc29bc
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / DiskIo / Dxe / diskio.c
1 /** @file
2 DiskIo driver that layers it's self on every Block IO protocol in the system.
3 DiskIo converts a block oriented device to a byte oriented device.
4
5 ReadDisk may have to do reads that are not aligned on 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 - 2007, Intel Corporation
13 All rights reserved. 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 //
24 // Include common header file for this module.
25 //
26 #include "CommonHeader.h"
27
28 #include "DiskIo.h"
29
30 EFI_DRIVER_BINDING_PROTOCOL gDiskIoDriverBinding = {
31 DiskIoDriverBindingSupported,
32 DiskIoDriverBindingStart,
33 DiskIoDriverBindingStop,
34 0xa,
35 NULL,
36 NULL
37 };
38
39 DISK_IO_PRIVATE_DATA gDiskIoPrivateDataTemplate = {
40 DISK_IO_PRIVATE_DATA_SIGNATURE,
41 {
42 EFI_DISK_IO_PROTOCOL_REVISION,
43 DiskIoReadDisk,
44 DiskIoWriteDisk
45 },
46 NULL
47 };
48
49
50 /**
51 Test to see if this driver supports ControllerHandle.
52
53 @param This Protocol instance pointer.
54 @param ControllerHandle Handle of device to test
55 @param RemainingDevicePath Optional parameter use to pick a specific child
56 device to start.
57
58 @retval EFI_SUCCESS This driver supports this device
59 @retval EFI_ALREADY_STARTED This driver is already running on this device
60 @retval other This driver does not support this device
61
62 **/
63 EFI_STATUS
64 EFIAPI
65 DiskIoDriverBindingSupported (
66 IN EFI_DRIVER_BINDING_PROTOCOL *This,
67 IN EFI_HANDLE ControllerHandle,
68 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
69 )
70 {
71 EFI_STATUS Status;
72 EFI_BLOCK_IO_PROTOCOL *BlockIo;
73
74 //
75 // Open the IO Abstraction(s) needed to perform the supported test.
76 //
77 Status = gBS->OpenProtocol (
78 ControllerHandle,
79 &gEfiBlockIoProtocolGuid,
80 (VOID **) &BlockIo,
81 This->DriverBindingHandle,
82 ControllerHandle,
83 EFI_OPEN_PROTOCOL_BY_DRIVER
84 );
85 if (EFI_ERROR (Status)) {
86 return Status;
87 }
88
89 //
90 // Close the I/O Abstraction(s) used to perform the supported test.
91 //
92 gBS->CloseProtocol (
93 ControllerHandle,
94 &gEfiBlockIoProtocolGuid,
95 This->DriverBindingHandle,
96 ControllerHandle
97 );
98 return EFI_SUCCESS;
99 }
100
101
102 /**
103 Start this driver on ControllerHandle by opening a Block IO protocol and
104 installing a Disk IO protocol on ControllerHandle.
105
106 @param This Protocol instance pointer.
107 @param ControllerHandle Handle of device to bind driver to
108 @param RemainingDevicePath Optional parameter use to pick a specific child
109 device to start.
110
111 @retval EFI_SUCCESS This driver is added to ControllerHandle
112 @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle
113 @retval other This driver does not support this device
114
115 **/
116 EFI_STATUS
117 EFIAPI
118 DiskIoDriverBindingStart (
119 IN EFI_DRIVER_BINDING_PROTOCOL *This,
120 IN EFI_HANDLE ControllerHandle,
121 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
122 )
123 {
124 EFI_STATUS Status;
125 DISK_IO_PRIVATE_DATA *Private;
126
127 Private = NULL;
128
129 //
130 // Connect to the Block IO interface on ControllerHandle.
131 //
132 Status = gBS->OpenProtocol (
133 ControllerHandle,
134 &gEfiBlockIoProtocolGuid,
135 (VOID **) &gDiskIoPrivateDataTemplate.BlockIo,
136 This->DriverBindingHandle,
137 ControllerHandle,
138 EFI_OPEN_PROTOCOL_BY_DRIVER
139 );
140 if (EFI_ERROR (Status)) {
141 return Status;
142 }
143
144 //
145 // Initialize the Disk IO device instance.
146 //
147 Private = AllocateCopyPool (sizeof (DISK_IO_PRIVATE_DATA), &gDiskIoPrivateDataTemplate);
148 if (Private == NULL) {
149 Status = EFI_OUT_OF_RESOURCES;
150 goto ErrorExit;
151 }
152
153 //
154 // Install protocol interfaces for the Disk IO device.
155 //
156 Status = gBS->InstallProtocolInterface (
157 &ControllerHandle,
158 &gEfiDiskIoProtocolGuid,
159 EFI_NATIVE_INTERFACE,
160 &Private->DiskIo
161 );
162
163 ErrorExit:
164 if (EFI_ERROR (Status)) {
165
166 if (Private != NULL) {
167 FreePool (Private);
168 }
169
170 gBS->CloseProtocol (
171 ControllerHandle,
172 &gEfiBlockIoProtocolGuid,
173 This->DriverBindingHandle,
174 ControllerHandle
175 );
176 }
177
178 return Status;
179 }
180
181
182 /**
183 Stop this driver on ControllerHandle by removing Disk IO protocol and closing
184 the Block IO protocol on ControllerHandle.
185
186 @param This Protocol instance pointer.
187 @param ControllerHandle Handle of device to stop driver on
188 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
189 children is zero stop the entire bus driver.
190 @param ChildHandleBuffer List of Child Handles to Stop.
191
192 @retval EFI_SUCCESS This driver is removed ControllerHandle
193 @retval other This driver was not removed from this device
194
195 **/
196 EFI_STATUS
197 EFIAPI
198 DiskIoDriverBindingStop (
199 IN EFI_DRIVER_BINDING_PROTOCOL *This,
200 IN EFI_HANDLE ControllerHandle,
201 IN UINTN NumberOfChildren,
202 IN EFI_HANDLE *ChildHandleBuffer
203 )
204 {
205 EFI_STATUS Status;
206 EFI_DISK_IO_PROTOCOL *DiskIo;
207 DISK_IO_PRIVATE_DATA *Private;
208
209 //
210 // Get our context back.
211 //
212 Status = gBS->OpenProtocol (
213 ControllerHandle,
214 &gEfiDiskIoProtocolGuid,
215 (VOID **) &DiskIo,
216 This->DriverBindingHandle,
217 ControllerHandle,
218 EFI_OPEN_PROTOCOL_GET_PROTOCOL
219 );
220 if (EFI_ERROR (Status)) {
221 return EFI_UNSUPPORTED;
222 }
223
224 Private = DISK_IO_PRIVATE_DATA_FROM_THIS (DiskIo);
225
226 Status = gBS->UninstallProtocolInterface (
227 ControllerHandle,
228 &gEfiDiskIoProtocolGuid,
229 &Private->DiskIo
230 );
231 if (!EFI_ERROR (Status)) {
232
233 Status = gBS->CloseProtocol (
234 ControllerHandle,
235 &gEfiBlockIoProtocolGuid,
236 This->DriverBindingHandle,
237 ControllerHandle
238 );
239 }
240
241 if (!EFI_ERROR (Status)) {
242 FreePool (Private);
243 }
244
245 return Status;
246 }
247
248
249
250 /**
251 Read BufferSize bytes from Offset into Buffer.
252 Reads may support reads that are not aligned on
253 sector boundaries. There are three cases:
254 UnderRun - The first byte is not on a sector boundary or the read request is
255 less than a sector in length.
256 Aligned - A read of N contiguous sectors.
257 OverRun - The last byte is not on a sector boundary.
258
259 @param This Protocol instance pointer.
260 @param MediaId Id of the media, changes every time the media is replaced.
261 @param Offset The starting byte offset to read from
262 @param BufferSize Size of Buffer
263 @param Buffer Buffer containing read data
264
265 @retval EFI_SUCCESS The data was read correctly from the device.
266 @retval EFI_DEVICE_ERROR The device reported an error while performing the read.
267 @retval EFI_NO_MEDIA There is no media in the device.
268 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
269 @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
270 valid for the device.
271
272 **/
273 EFI_STATUS
274 EFIAPI
275 DiskIoReadDisk (
276 IN EFI_DISK_IO_PROTOCOL *This,
277 IN UINT32 MediaId,
278 IN UINT64 Offset,
279 IN UINTN BufferSize,
280 OUT VOID *Buffer
281 )
282 {
283 EFI_STATUS Status;
284 DISK_IO_PRIVATE_DATA *Private;
285 EFI_BLOCK_IO_PROTOCOL *BlockIo;
286 EFI_BLOCK_IO_MEDIA *Media;
287 UINT32 BlockSize;
288 UINT64 Lba;
289 UINT64 OverRunLba;
290 UINT32 UnderRun;
291 UINT32 OverRun;
292 BOOLEAN TransactionComplete;
293 UINTN WorkingBufferSize;
294 UINT8 *WorkingBuffer;
295 UINTN Length;
296 UINT8 *Data;
297 UINT8 *PreData;
298 UINTN IsBufferAligned;
299 UINTN DataBufferSize;
300 BOOLEAN LastRead;
301
302 Private = DISK_IO_PRIVATE_DATA_FROM_THIS (This);
303
304 BlockIo = Private->BlockIo;
305 Media = BlockIo->Media;
306 BlockSize = Media->BlockSize;
307
308 if (Media->MediaId != MediaId) {
309 return EFI_MEDIA_CHANGED;
310 }
311
312 WorkingBuffer = Buffer;
313 WorkingBufferSize = BufferSize;
314
315 //
316 // Allocate a temporary buffer for operation
317 //
318 DataBufferSize = BlockSize * DATA_BUFFER_BLOCK_NUM;
319
320 if (Media->IoAlign > 1) {
321 PreData = AllocatePool (DataBufferSize + Media->IoAlign);
322 Data = PreData - ((UINTN) PreData & (Media->IoAlign - 1)) + Media->IoAlign;
323 } else {
324 PreData = AllocatePool (DataBufferSize);
325 Data = PreData;
326 }
327
328 if (PreData == NULL) {
329 return EFI_OUT_OF_RESOURCES;
330 }
331
332 Lba = DivU64x32Remainder (Offset, BlockSize, &UnderRun);
333
334 Length = BlockSize - UnderRun;
335 TransactionComplete = FALSE;
336
337 Status = EFI_SUCCESS;
338 if (UnderRun != 0) {
339 //
340 // Offset starts in the middle of an Lba, so read the entire block.
341 //
342 Status = BlockIo->ReadBlocks (
343 BlockIo,
344 MediaId,
345 Lba,
346 BlockSize,
347 Data
348 );
349
350 if (EFI_ERROR (Status)) {
351 goto Done;
352 }
353
354 if (Length > BufferSize) {
355 Length = BufferSize;
356 TransactionComplete = TRUE;
357 }
358
359 CopyMem (WorkingBuffer, Data + UnderRun, Length);
360
361 WorkingBuffer += Length;
362
363 WorkingBufferSize -= Length;
364 if (WorkingBufferSize == 0) {
365 goto Done;
366 }
367
368 Lba += 1;
369 }
370
371 OverRunLba = Lba + DivU64x32Remainder (WorkingBufferSize, BlockSize, &OverRun);
372
373 if (!TransactionComplete && WorkingBufferSize >= BlockSize) {
374 //
375 // If the DiskIo maps directly to a BlockIo device do the read.
376 //
377 if (OverRun != 0) {
378 WorkingBufferSize -= OverRun;
379 }
380 //
381 // Check buffer alignment
382 //
383 IsBufferAligned = (UINTN) WorkingBuffer & (UINTN) (Media->IoAlign - 1);
384
385 if (Media->IoAlign <= 1 || IsBufferAligned == 0) {
386 //
387 // Alignment is satisfied, so read them together
388 //
389 Status = BlockIo->ReadBlocks (
390 BlockIo,
391 MediaId,
392 Lba,
393 WorkingBufferSize,
394 WorkingBuffer
395 );
396
397 if (EFI_ERROR (Status)) {
398 goto Done;
399 }
400
401 WorkingBuffer += WorkingBufferSize;
402
403 } else {
404 //
405 // Use the allocated buffer instead of the original buffer
406 // to avoid alignment issue.
407 // Here, the allocated buffer (8-byte align) can satisfy the alignment
408 //
409 LastRead = FALSE;
410 do {
411 if (WorkingBufferSize <= DataBufferSize) {
412 //
413 // It is the last calling to readblocks in this loop
414 //
415 DataBufferSize = WorkingBufferSize;
416 LastRead = TRUE;
417 }
418
419 Status = BlockIo->ReadBlocks (
420 BlockIo,
421 MediaId,
422 Lba,
423 DataBufferSize,
424 Data
425 );
426 if (EFI_ERROR (Status)) {
427 goto Done;
428 }
429
430 CopyMem (WorkingBuffer, Data, DataBufferSize);
431 WorkingBufferSize -= DataBufferSize;
432 WorkingBuffer += DataBufferSize;
433 Lba += DATA_BUFFER_BLOCK_NUM;
434 } while (!LastRead);
435 }
436 }
437
438 if (!TransactionComplete && OverRun != 0) {
439 //
440 // Last read is not a complete block.
441 //
442 Status = BlockIo->ReadBlocks (
443 BlockIo,
444 MediaId,
445 OverRunLba,
446 BlockSize,
447 Data
448 );
449
450 if (EFI_ERROR (Status)) {
451 goto Done;
452 }
453
454 CopyMem (WorkingBuffer, Data, OverRun);
455 }
456
457 Done:
458 if (PreData != NULL) {
459 FreePool (PreData);
460 }
461
462 return Status;
463 }
464
465
466 /**
467 Read BufferSize bytes from Offset into Buffer.
468 Writes may require a read modify write to support writes that are not
469 aligned on sector boundaries. There are three cases:
470 UnderRun - The first byte is not on a sector boundary or the write request
471 is less than a sector in length. Read modify write is required.
472 Aligned - A write of N contiguous sectors.
473 OverRun - The last byte is not on a sector boundary. Read modified write
474 required.
475
476 @param This Protocol instance pointer.
477 @param MediaId Id of the media, changes every time the media is replaced.
478 @param Offset The starting byte offset to read from
479 @param BufferSize Size of Buffer
480 @param Buffer Buffer containing read data
481
482 @retval EFI_SUCCESS The data was written correctly to the device.
483 @retval EFI_WRITE_PROTECTED The device can not be written to.
484 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
485 @retval EFI_NO_MEDIA There is no media in the device.
486 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
487 @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
488 valid for the device.
489
490 **/
491 EFI_STATUS
492 EFIAPI
493 DiskIoWriteDisk (
494 IN EFI_DISK_IO_PROTOCOL *This,
495 IN UINT32 MediaId,
496 IN UINT64 Offset,
497 IN UINTN BufferSize,
498 IN VOID *Buffer
499 )
500 {
501 EFI_STATUS Status;
502 DISK_IO_PRIVATE_DATA *Private;
503 EFI_BLOCK_IO_PROTOCOL *BlockIo;
504 EFI_BLOCK_IO_MEDIA *Media;
505 UINT32 BlockSize;
506 UINT64 Lba;
507 UINT64 OverRunLba;
508 UINT32 UnderRun;
509 UINT32 OverRun;
510 BOOLEAN TransactionComplete;
511 UINTN WorkingBufferSize;
512 UINT8 *WorkingBuffer;
513 UINTN Length;
514 UINT8 *Data;
515 UINT8 *PreData;
516 UINTN IsBufferAligned;
517 UINTN DataBufferSize;
518 BOOLEAN LastWrite;
519
520 Private = DISK_IO_PRIVATE_DATA_FROM_THIS (This);
521
522 BlockIo = Private->BlockIo;
523 Media = BlockIo->Media;
524 BlockSize = Media->BlockSize;
525
526 if (Media->ReadOnly) {
527 return EFI_WRITE_PROTECTED;
528 }
529
530 if (Media->MediaId != MediaId) {
531 return EFI_MEDIA_CHANGED;
532 }
533
534 DataBufferSize = BlockSize * DATA_BUFFER_BLOCK_NUM;
535
536 if (Media->IoAlign > 1) {
537 PreData = AllocatePool (DataBufferSize + Media->IoAlign);
538 Data = PreData - ((UINTN) PreData & (Media->IoAlign - 1)) + Media->IoAlign;
539 } else {
540 PreData = AllocatePool (DataBufferSize);
541 Data = PreData;
542 }
543
544 if (PreData == NULL) {
545 return EFI_OUT_OF_RESOURCES;
546 }
547
548 WorkingBuffer = Buffer;
549 WorkingBufferSize = BufferSize;
550
551 Lba = DivU64x32Remainder (Offset, BlockSize, &UnderRun);
552
553 Length = BlockSize - UnderRun;
554 TransactionComplete = FALSE;
555
556 Status = EFI_SUCCESS;
557 if (UnderRun != 0) {
558 //
559 // Offset starts in the middle of an Lba, so do read modify write.
560 //
561 Status = BlockIo->ReadBlocks (
562 BlockIo,
563 MediaId,
564 Lba,
565 BlockSize,
566 Data
567 );
568
569 if (EFI_ERROR (Status)) {
570 goto Done;
571 }
572
573 if (Length > BufferSize) {
574 Length = BufferSize;
575 TransactionComplete = TRUE;
576 }
577
578 CopyMem (Data + UnderRun, WorkingBuffer, Length);
579
580 Status = BlockIo->WriteBlocks (
581 BlockIo,
582 MediaId,
583 Lba,
584 BlockSize,
585 Data
586 );
587 if (EFI_ERROR (Status)) {
588 goto Done;
589 }
590
591 WorkingBuffer += Length;
592 WorkingBufferSize -= Length;
593 if (WorkingBufferSize == 0) {
594 goto Done;
595 }
596
597 Lba += 1;
598 }
599
600 OverRunLba = Lba + DivU64x32Remainder (WorkingBufferSize, BlockSize, &OverRun);
601
602 if (!TransactionComplete && WorkingBufferSize >= BlockSize) {
603 //
604 // If the DiskIo maps directly to a BlockIo device do the write.
605 //
606 if (OverRun != 0) {
607 WorkingBufferSize -= OverRun;
608 }
609 //
610 // Check buffer alignment
611 //
612 IsBufferAligned = (UINTN) WorkingBuffer & (UINTN) (Media->IoAlign - 1);
613
614 if (Media->IoAlign <= 1 || IsBufferAligned == 0) {
615 //
616 // Alignment is satisfied, so write them together
617 //
618 Status = BlockIo->WriteBlocks (
619 BlockIo,
620 MediaId,
621 Lba,
622 WorkingBufferSize,
623 WorkingBuffer
624 );
625
626 if (EFI_ERROR (Status)) {
627 goto Done;
628 }
629
630 WorkingBuffer += WorkingBufferSize;
631
632 } else {
633 //
634 // The buffer parameter is not aligned with the request
635 // So use the allocated instead.
636 // It can fit almost all the cases.
637 //
638 LastWrite = FALSE;
639 do {
640 if (WorkingBufferSize <= DataBufferSize) {
641 //
642 // It is the last calling to writeblocks in this loop
643 //
644 DataBufferSize = WorkingBufferSize;
645 LastWrite = TRUE;
646 }
647
648 CopyMem (Data, WorkingBuffer, DataBufferSize);
649 Status = BlockIo->WriteBlocks (
650 BlockIo,
651 MediaId,
652 Lba,
653 DataBufferSize,
654 Data
655 );
656 if (EFI_ERROR (Status)) {
657 goto Done;
658 }
659
660 WorkingBufferSize -= DataBufferSize;
661 WorkingBuffer += DataBufferSize;
662 Lba += DATA_BUFFER_BLOCK_NUM;
663 } while (!LastWrite);
664 }
665 }
666
667 if (!TransactionComplete && OverRun != 0) {
668 //
669 // Last bit is not a complete block, so do a read modify write.
670 //
671 Status = BlockIo->ReadBlocks (
672 BlockIo,
673 MediaId,
674 OverRunLba,
675 BlockSize,
676 Data
677 );
678
679 if (EFI_ERROR (Status)) {
680 goto Done;
681 }
682
683 CopyMem (Data, WorkingBuffer, OverRun);
684
685 Status = BlockIo->WriteBlocks (
686 BlockIo,
687 MediaId,
688 OverRunLba,
689 BlockSize,
690 Data
691 );
692 if (EFI_ERROR (Status)) {
693 goto Done;
694 }
695 }
696
697 Done:
698 if (PreData != NULL) {
699 FreePool (PreData);
700 }
701
702 return Status;
703 }
704
705
706 /**
707 The user Entry Point for module DiskIo. The user code starts with this function.
708
709 @param[in] ImageHandle The firmware allocated handle for the EFI image.
710 @param[in] SystemTable A pointer to the EFI System Table.
711
712 @retval EFI_SUCCESS The entry point is executed successfully.
713 @retval other Some error occurs when executing this entry point.
714
715 **/
716 EFI_STATUS
717 EFIAPI
718 InitializeDiskIo (
719 IN EFI_HANDLE ImageHandle,
720 IN EFI_SYSTEM_TABLE *SystemTable
721 )
722 {
723 EFI_STATUS Status;
724
725 //
726 // Install driver model protocol(s).
727 //
728 Status = EfiLibInstallAllDriverProtocols (
729 ImageHandle,
730 SystemTable,
731 &gDiskIoDriverBinding,
732 ImageHandle,
733 &gDiskIoComponentName,
734 NULL,
735 NULL
736 );
737 ASSERT_EFI_ERROR (Status);
738
739
740 return Status;
741 }
742