b4fd38e17813e8d0c5f120cce57df6dc9004e0b8
[reactos.git] / boot / environ / lib / io / device.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/io/device.c
5 * PURPOSE: Boot Library Device Management Routines
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bl.h"
12
13 /* DATA VARIABLES ************************************************************/
14
15 typedef struct _BL_DEVICE_IO_INFORMATION
16 {
17 ULONGLONG ReadCount;
18 ULONGLONG WriteCount;
19 } BL_DEVICE_IO_INFORMATION, *PBL_DEVICE_IO_INFORMATION;
20
21 LIST_ENTRY DmRegisteredDevices;
22 ULONG DmTableEntries;
23 LIST_ENTRY DmRegisteredDevices;
24 PVOID* DmDeviceTable;
25
26 BL_DEVICE_IO_INFORMATION DmDeviceIoInformation;
27
28 /* FUNCTIONS *****************************************************************/
29
30 typedef struct _BL_REGISTERED_DEVICE
31 {
32 LIST_ENTRY ListEntry;
33 BL_DEVICE_CALLBACKS Callbacks;
34 } BL_REGISTERED_DEVICE, *PBL_REGISTERED_DEVICE;
35
36 PVOID* BlockIoDeviceTable;
37 ULONG BlockIoDeviceTableEntries;
38
39 ULONG BlockIoFirmwareRemovableDiskCount;
40 ULONG BlockIoFirmwareRawDiskCount;
41 ULONG BlockIoFirmwareCdromCount;
42
43 PVOID BlockIopAlignedBuffer;
44 ULONG BlockIopAlignedBufferSize;
45
46 PVOID BlockIopPartialBlockBuffer;
47 ULONG BlockIopPartialBlockBufferSize;
48
49 PVOID BlockIopPrefetchBuffer;
50
51 PVOID BlockIopReadBlockBuffer;
52 ULONG BlockIopReadBlockBufferSize;
53
54 ULONG HashTableId;
55
56 BOOLEAN BlockIoInitialized;
57
58 NTSTATUS
59 BlockIoOpen (
60 _In_ PBL_DEVICE_DESCRIPTOR Device,
61 _In_ PBL_DEVICE_ENTRY DeviceEntry
62 );
63
64 NTSTATUS
65 BlockIoGetInformation (
66 _In_ PBL_DEVICE_ENTRY DeviceEntry,
67 _Out_ PBL_DEVICE_INFORMATION DeviceInformation
68 );
69
70 NTSTATUS
71 BlockIoSetInformation (
72 _In_ PBL_DEVICE_ENTRY DeviceEntry,
73 _Out_ PBL_DEVICE_INFORMATION DeviceInformation
74 );
75
76 NTSTATUS
77 BlockIoRead (
78 _In_ PBL_DEVICE_ENTRY DeviceEntry,
79 _In_ PVOID Buffer,
80 _In_ ULONG Size,
81 _Out_ PULONG BytesRead
82 );
83
84 BL_DEVICE_CALLBACKS BlockIoDeviceFunctionTable =
85 {
86 NULL,
87 BlockIoOpen,
88 NULL,
89 BlockIoRead,
90 NULL,
91 BlockIoGetInformation,
92 BlockIoSetInformation
93 };
94
95 NTSTATUS
96 BlockIoFirmwareWrite (
97 _In_ PBL_BLOCK_DEVICE BlockDevice,
98 _In_ PVOID Buffer,
99 _In_ ULONGLONG Block,
100 _In_ ULONGLONG BlockCount
101 )
102 {
103 return STATUS_NOT_IMPLEMENTED;
104 }
105
106 NTSTATUS
107 BlockIoFirmwareRead (
108 _In_ PBL_BLOCK_DEVICE BlockDevice,
109 _In_ PVOID Buffer,
110 _In_ ULONGLONG Block,
111 _In_ ULONGLONG BlockCount
112 )
113 {
114 NTSTATUS Status;
115 EFI_BLOCK_IO *BlockProtocol;
116 BL_ARCH_MODE OldMode;
117 EFI_STATUS EfiStatus;
118 ULONG FailureCount;
119
120 for (FailureCount = 0, Status = STATUS_SUCCESS;
121 FailureCount < 2 && NT_SUCCESS(Status);
122 FailureCount++)
123 {
124 BlockProtocol = BlockDevice->Protocol;
125
126 OldMode = CurrentExecutionContext->Mode;
127 if (CurrentExecutionContext->Mode != 1)
128 {
129 Status = STATUS_NOT_IMPLEMENTED;
130 break;
131 }
132
133 //EfiPrintf(L"EFI Reading BLOCK %d off media %lx (%d blocks)\r\n",
134 //Block, BlockProtocol->Media->MediaId, BlockCount);
135 EfiStatus = BlockProtocol->ReadBlocks(BlockProtocol,
136 BlockProtocol->Media->MediaId,
137 Block,
138 BlockProtocol->Media->BlockSize * BlockCount,
139 Buffer);
140 if (EfiStatus == EFI_SUCCESS)
141 {
142 //EfiPrintf(L"EFI Read complete into buffer\r\n");
143 //EfiPrintf(L"Buffer data: %lx %lx %lx %lx\r\n", *(PULONG)Buffer, *((PULONG)Buffer + 1), *((PULONG)Buffer + 2), *((PULONG)Buffer + 3));
144 }
145
146 if (OldMode != 1)
147 {
148 BlpArchSwitchContext(OldMode);
149 }
150
151 Status = EfiGetNtStatusCode(EfiStatus);
152 if (Status != STATUS_MEDIA_CHANGED)
153 {
154 break;
155 }
156
157 EfiCloseProtocol(BlockDevice->Handle, &EfiBlockIoProtocol);
158
159 Status = EfiOpenProtocol(BlockDevice->Handle,
160 &EfiBlockIoProtocol,
161 (PVOID*)BlockDevice->Protocol);
162 }
163
164 return Status;
165 }
166
167 NTSTATUS
168 BlockIopFirmwareOperation (
169 PBL_DEVICE_ENTRY DeviceEntry,
170 _In_ PVOID Buffer,
171 _In_ ULONGLONG Block,
172 _In_ ULONGLONG BlockCount,
173 _In_ ULONG OperationType
174 )
175 {
176 ULONG FailureCount;
177 PBL_BLOCK_DEVICE BlockDevice;
178 NTSTATUS Status;
179
180 BlockDevice = DeviceEntry->DeviceSpecificData;
181
182 if (OperationType == 1)
183 {
184 for (FailureCount = 0; FailureCount < 3; FailureCount++)
185 {
186 Status = BlockIoFirmwareWrite(BlockDevice, Buffer, Block, BlockCount);
187 if (Status >= 0)
188 {
189 break;
190 }
191 }
192 }
193 else
194 {
195 for (FailureCount = 0; FailureCount < 3; FailureCount++)
196 {
197 Status = BlockIoFirmwareRead(BlockDevice, Buffer, Block, BlockCount);
198 if (Status >= 0)
199 {
200 break;
201 }
202 }
203 }
204 return Status;
205 }
206
207 NTSTATUS
208 BlockIopFreeAlignedBuffer (
209 _Inout_ PVOID* Buffer,
210 _Inout_ PULONG BufferSize
211 )
212 {
213 NTSTATUS Status;
214
215 if (*BufferSize)
216 {
217 Status = MmPapFreePages(*Buffer, BL_MM_INCLUDE_MAPPED_ALLOCATED);
218
219 *Buffer = NULL;
220 *BufferSize = 0;
221 }
222 else
223 {
224 Status = STATUS_SUCCESS;
225 }
226
227 return Status;
228 }
229
230 NTSTATUS
231 BlockIopAllocateAlignedBuffer (
232 _Inout_ PVOID* Buffer,
233 _Inout_ PULONG BufferSize,
234 _In_ ULONG Size,
235 _In_ ULONG Alignment
236 )
237 {
238 NTSTATUS Status;
239
240 if (!Alignment)
241 {
242 ++Alignment;
243 }
244
245 Status = STATUS_SUCCESS;
246 if ((Size > *BufferSize) || ((Alignment - 1) & (ULONG_PTR)*Buffer))
247 {
248 BlockIopFreeAlignedBuffer(Buffer, BufferSize);
249
250 *BufferSize = ROUND_TO_PAGES(Size);
251
252 Status = MmPapAllocatePagesInRange(Buffer,
253 BlLoaderDeviceMemory,
254 *BufferSize >> PAGE_SHIFT,
255 0,
256 Alignment >> PAGE_SHIFT,
257 NULL,
258 0);
259 if (!NT_SUCCESS(Status))
260 {
261 *BufferSize = 0;
262 }
263 }
264
265 return Status;
266 }
267
268 NTSTATUS
269 BlockIopReadUsingPrefetch (
270 _In_ PBL_DEVICE_ENTRY DeviceEntry,
271 _In_ PVOID Buffer,
272 _In_ ULONG BlockCount
273 )
274 {
275 EfiPrintf(L"No prefetch support\r\n");
276 return STATUS_NOT_IMPLEMENTED;
277 }
278
279 NTSTATUS
280 BlockIopOperation (
281 _In_ PBL_DEVICE_ENTRY DeviceEntry,
282 _In_ PVOID Buffer,
283 _In_ ULONG BlockCount,
284 _In_ ULONG OperationType
285 )
286 {
287 PBL_BLOCK_DEVICE BlockDevice;
288 ULONG BufferSize, Alignment;
289 ULONGLONG Offset;
290 NTSTATUS Status;
291
292 BlockDevice = DeviceEntry->DeviceSpecificData;
293 BufferSize = BlockDevice->BlockSize * BlockCount;
294 Offset = BlockDevice->Block + BlockDevice->StartOffset;
295 if ((BlockDevice->LastBlock + 1) < (BlockDevice->Block + BlockCount))
296 {
297 EfiPrintf(L"Read past end of device\r\n");
298 return STATUS_INVALID_PARAMETER;
299 }
300
301 Alignment = BlockDevice->Alignment;
302 if (!(Alignment) || !((Alignment - 1) & (ULONG_PTR)Buffer))
303 {
304 Status = BlockIopFirmwareOperation(DeviceEntry,
305 Buffer,
306 Offset,
307 BlockCount,
308 OperationType);
309 if (!NT_SUCCESS(Status))
310 {
311 EfiPrintf(L"EFI op failed: %lx\r\n", Status);
312 return Status;
313 }
314
315 return STATUS_SUCCESS;
316 }
317
318 Status = BlockIopAllocateAlignedBuffer(&BlockIopAlignedBuffer,
319 &BlockIopAlignedBufferSize,
320 BufferSize,
321 BlockDevice->Alignment);
322 if (!NT_SUCCESS(Status))
323 {
324 EfiPrintf(L"No memory for align\r\n");
325 return STATUS_NO_MEMORY;
326 }
327
328 if (OperationType == 1)
329 {
330 RtlCopyMemory(BlockIopAlignedBuffer, Buffer, BufferSize);
331 }
332
333 Status = BlockIopFirmwareOperation(DeviceEntry,
334 BlockIopAlignedBuffer,
335 Offset,
336 BlockCount,
337 OperationType);
338 if (!NT_SUCCESS(Status))
339 {
340 return Status;
341 }
342
343 if (!OperationType)
344 {
345 RtlCopyMemory(Buffer, BlockIopAlignedBuffer, BufferSize);
346 }
347
348 return STATUS_SUCCESS;
349 }
350
351 NTSTATUS
352 BlockIopReadWriteVirtualDevice (
353 _In_ PBL_DEVICE_ENTRY DeviceEntry,
354 _In_ PVOID Buffer,
355 _In_ ULONG Size,
356 _In_ ULONG Operation,
357 _Out_ PULONG BytesRead
358 )
359 {
360 return STATUS_NOT_IMPLEMENTED;
361 }
362
363 NTSTATUS
364 BlockIopReadPhysicalDevice (
365 _In_ PBL_DEVICE_ENTRY DeviceEntry,
366 _In_ PVOID Buffer,
367 _In_ ULONG Size,
368 _Out_ PULONG BytesRead
369 )
370 {
371 PBL_BLOCK_DEVICE BlockDevice;
372 PVOID ReadBuffer;
373 ULONGLONG OffsetEnd, AlignedOffsetEnd, Offset;
374 NTSTATUS Status;
375
376 BlockDevice = DeviceEntry->DeviceSpecificData;
377 ReadBuffer = Buffer;
378 OffsetEnd = Size + BlockDevice->Offset;
379 if (OffsetEnd < Size)
380 {
381 OffsetEnd = -1;
382 return STATUS_INTEGER_OVERFLOW;
383 }
384
385 AlignedOffsetEnd = ~(BlockDevice->BlockSize - 1) & (OffsetEnd + BlockDevice->BlockSize - 1);
386 if (AlignedOffsetEnd < OffsetEnd)
387 {
388 return STATUS_INTEGER_OVERFLOW;
389 }
390
391 if ((BlockDevice->Offset) || (Size != AlignedOffsetEnd))
392 {
393 Status = BlockIopAllocateAlignedBuffer(&BlockIopReadBlockBuffer,
394 &BlockIopReadBlockBufferSize,
395 AlignedOffsetEnd,
396 BlockDevice->Alignment);
397 if (!NT_SUCCESS(Status))
398 {
399 EfiPrintf(L"Failed to allocate buffer: %lx\r\n", Status);
400 return Status;
401 }
402
403 ReadBuffer = BlockIopReadBlockBuffer;
404 }
405
406 Offset = AlignedOffsetEnd / BlockDevice->BlockSize;
407
408 if (BlockDevice->Unknown & 2)
409 {
410 Status = BlockIopReadUsingPrefetch(DeviceEntry,
411 ReadBuffer,
412 AlignedOffsetEnd / BlockDevice->BlockSize);
413 if (NT_SUCCESS(Status))
414 {
415 goto ReadComplete;
416 }
417 }
418
419 Status = BlockIopOperation(DeviceEntry, ReadBuffer, Offset, 0);
420 if (!NT_SUCCESS(Status))
421 {
422 EfiPrintf(L"Block I/O failed: %lx\r\n", Status);
423 return Status;
424 }
425
426 BlockDevice->Block += Offset;
427
428 ReadComplete:
429 if (ReadBuffer != Buffer)
430 {
431 RtlCopyMemory(Buffer,
432 (PVOID)((ULONG_PTR)ReadBuffer +
433 (ULONG_PTR)BlockDevice->Offset),
434 Size);
435 }
436
437 if (BytesRead)
438 {
439 *BytesRead = Size;
440 }
441
442 return STATUS_SUCCESS;
443 }
444
445 NTSTATUS
446 BlockIopBlockInformationCheck (
447 _In_ PBL_BLOCK_DEVICE BlockDevice,
448 _In_opt_ PULONG DesiredSize,
449 _Out_opt_ PULONG Size,
450 _Out_opt_ PULONG OutputAdjustedSize
451 )
452 {
453 ULONG RealSize;
454 ULONGLONG Offset, LastOffset, RemainingOffset, MaxOffset;
455 NTSTATUS Status;
456
457 RealSize = 0;
458
459 Offset = (BlockDevice->Offset * BlockDevice->BlockSize) + BlockDevice->Block;
460
461 if (Offset > ((BlockDevice->LastBlock + 1) * BlockDevice->BlockSize))
462 {
463 Status = STATUS_INVALID_PARAMETER;
464 goto Quickie;
465 }
466
467 LastOffset = (BlockDevice->LastBlock * BlockDevice->BlockSize) + BlockDevice->BlockSize - 1;
468
469 MaxOffset = BlockDevice->LastBlock;
470 if (MaxOffset < BlockDevice->BlockSize)
471 {
472 MaxOffset = BlockDevice->BlockSize;
473 }
474
475 if (LastOffset < MaxOffset)
476 {
477
478 Status = STATUS_INVALID_PARAMETER;
479 goto Quickie;
480 }
481
482 if (Offset > LastOffset)
483 {
484 Status = STATUS_INVALID_PARAMETER;
485 goto Quickie;
486 }
487
488 RemainingOffset = LastOffset - Offset + 1;
489
490 if (DesiredSize != FALSE)
491 {
492 RealSize = *DesiredSize;
493 }
494 else
495 {
496 RealSize = ULONG_MAX;
497 }
498
499 if (RemainingOffset < RealSize)
500 {
501 if (Size == FALSE)
502 {
503 RealSize = 0;
504 Status = STATUS_INVALID_PARAMETER;
505 goto Quickie;
506 }
507
508 RealSize = RemainingOffset;
509 }
510
511 Status = STATUS_SUCCESS;
512
513 Quickie:
514 if (Size)
515 {
516 *Size = RealSize;
517 }
518
519 return Status;
520 }
521
522 NTSTATUS
523 BlockIoRead (
524 _In_ PBL_DEVICE_ENTRY DeviceEntry,
525 _In_ PVOID Buffer,
526 _In_ ULONG Size,
527 _Out_ PULONG BytesRead
528 )
529 {
530 PBL_BLOCK_DEVICE BlockDevice;
531 NTSTATUS Status;
532
533 /* Get the device-specific data, which is our block device descriptor */
534 BlockDevice = DeviceEntry->DeviceSpecificData;
535
536 /* Make sure that the buffer and size is valid */
537 Status = BlockIopBlockInformationCheck(BlockDevice, &Size, BytesRead, &Size);
538 if (NT_SUCCESS(Status))
539 {
540 /* Check if this is a virtual device or a physical device */
541 if (BlockDevice->DeviceFlags & BL_BLOCK_DEVICE_VIRTUAL_FLAG)
542 {
543 /* Do a virtual read or write */
544 Status = BlockIopReadWriteVirtualDevice(DeviceEntry, Buffer, Size, 0, BytesRead);
545 }
546 else
547 {
548 /* Do a physical read or write */
549 Status = BlockIopReadPhysicalDevice(DeviceEntry, Buffer, Size, BytesRead);
550 }
551 }
552 else if (BytesRead)
553 {
554 /* We failed, if the caller wanted bytes read, return 0 */
555 *BytesRead = 0;
556 }
557
558 /* Return back to the caller */
559 return Status;
560 }
561
562 NTSTATUS
563 BlockIoSetInformation (
564 _In_ PBL_DEVICE_ENTRY DeviceEntry,
565 _Out_ PBL_DEVICE_INFORMATION DeviceInformation
566 )
567 {
568 PBL_BLOCK_DEVICE BlockDevice;
569 ULONGLONG Offset;
570
571 BlockDevice = DeviceEntry->DeviceSpecificData;
572
573 /* Take the current block number and block-offset and conver to full offset */
574 Offset = DeviceInformation->BlockDeviceInfo.Block * BlockDevice->BlockSize +
575 DeviceInformation->BlockDeviceInfo.Offset;
576
577 /* Make sure that the full offset is still within the bounds of the device */
578 if (Offset > ((BlockDevice->LastBlock + 1) * BlockDevice->BlockSize - 1))
579 {
580 EfiPrintf(L"Offset out of bounds\r\n");
581 return STATUS_INVALID_PARAMETER;
582 }
583
584 /* Convery the full raw offset into a block number and block-offset */
585 BlockDevice->Block = Offset / BlockDevice->BlockSize;
586 BlockDevice->Offset = Offset % BlockDevice->BlockSize;
587
588 /* Return the unknown */
589 BlockDevice->Unknown = DeviceInformation->BlockDeviceInfo.Unknown;
590
591 /* All done */
592 return STATUS_SUCCESS;
593 }
594
595 NTSTATUS
596 BlockIoGetInformation (
597 _In_ PBL_DEVICE_ENTRY DeviceEntry,
598 _Out_ PBL_DEVICE_INFORMATION DeviceInformation
599 )
600 {
601 /* Copy the device specific data into the block device information */
602 RtlCopyMemory(&DeviceInformation->BlockDeviceInfo,
603 DeviceEntry->DeviceSpecificData,
604 sizeof(DeviceInformation->BlockDeviceInfo));
605
606 /* Hardcode the device type */
607 DeviceInformation->DeviceType = DiskDevice;
608 return STATUS_SUCCESS;
609 }
610
611 BOOLEAN
612 BlDeviceIsVirtualPartitionDevice (
613 _In_ PBL_DEVICE_DESCRIPTOR InputDevice,
614 _Outptr_ PBL_DEVICE_DESCRIPTOR* VirtualDevice
615 )
616 {
617 BOOLEAN IsVirtual;
618 PBL_LOCAL_DEVICE ParentDisk;
619
620 /* Assume it isn't */
621 IsVirtual = FALSE;
622
623 /* Check if this is a partition device */
624 if ((InputDevice->DeviceType == LegacyPartitionDevice) ||
625 (InputDevice->DeviceType == PartitionDevice))
626 {
627 /* Check if the parent disk is a VHD */
628 ParentDisk = &InputDevice->Partition.Disk;
629 if (ParentDisk->Type == VirtualDiskDevice)
630 {
631 /* This is a virtual partition device -- does the caller want it? */
632 IsVirtual = TRUE;
633 if (VirtualDevice)
634 {
635 *VirtualDevice = (PBL_DEVICE_DESCRIPTOR)(&ParentDisk->VirtualHardDisk + 1);
636 }
637 }
638 }
639
640 /* Return */
641 return IsVirtual;
642 }
643
644 NTSTATUS
645 BlDeviceSetInformation (
646 _In_ ULONG DeviceId,
647 _Out_ PBL_DEVICE_INFORMATION DeviceInformation
648 )
649 {
650 PBL_DEVICE_ENTRY DeviceEntry;
651
652 /* This parameter is not optional */
653 if (!DeviceInformation)
654 {
655 return STATUS_INVALID_PARAMETER;
656 }
657
658 /* Make sure the device ID is valid */
659 if (DmTableEntries <= DeviceId)
660 {
661 return STATUS_INVALID_PARAMETER;
662 }
663
664 /* Get the device entry */
665 DeviceEntry = DmDeviceTable[DeviceId];
666 if (!DeviceEntry)
667 {
668 return STATUS_INVALID_PARAMETER;
669 }
670
671 /* Make sure the device is open */
672 if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED))
673 {
674 return STATUS_INVALID_PARAMETER;
675 }
676
677 /* Set the device information */
678 return DeviceEntry->Callbacks.SetInformation(DeviceEntry, DeviceInformation);
679 }
680
681 NTSTATUS
682 BlDeviceGetInformation (
683 _In_ ULONG DeviceId,
684 _Out_ PBL_DEVICE_INFORMATION DeviceInformation
685 )
686 {
687 PBL_DEVICE_ENTRY DeviceEntry;
688
689 /* This parameter is not optional */
690 if (!DeviceInformation)
691 {
692 return STATUS_INVALID_PARAMETER;
693 }
694
695 /* Make sure the device ID is valid */
696 if (DmTableEntries <= DeviceId)
697 {
698 return STATUS_INVALID_PARAMETER;
699 }
700
701 /* Get the device entry */
702 DeviceEntry = DmDeviceTable[DeviceId];
703 if (!DeviceEntry)
704 {
705 return STATUS_INVALID_PARAMETER;
706 }
707
708 /* Make sure the device is open */
709 if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED))
710 {
711 return STATUS_INVALID_PARAMETER;
712 }
713
714 /* Return the device information */
715 DeviceInformation->DeviceType = DeviceEntry->DeviceDescriptor->DeviceType;
716 return DeviceEntry->Callbacks.GetInformation(DeviceEntry, DeviceInformation);
717 }
718
719 NTSTATUS
720 BlDeviceRead (
721 _In_ ULONG DeviceId,
722 _In_ PVOID Buffer,
723 _In_ ULONG Size,
724 _Out_opt_ PULONG BytesRead
725 )
726 {
727 PBL_DEVICE_ENTRY DeviceEntry;
728 NTSTATUS Status;
729 ULONG BytesTransferred;
730
731 /* Make sure we have a buffer, and the device ID is valid */
732 if (!(Buffer) || (DmTableEntries <= DeviceId))
733 {
734 return STATUS_INVALID_PARAMETER;
735 }
736
737 /* Get the device entry for it */
738 DeviceEntry = DmDeviceTable[DeviceId];
739 if (!DeviceEntry)
740 {
741 return STATUS_INVALID_PARAMETER;
742 }
743
744 /* Make sure this is a device opened for read access */
745 if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED) ||
746 !(DeviceEntry->Flags & BL_DEVICE_ENTRY_READ_ACCESS))
747 {
748 return STATUS_INVALID_PARAMETER;
749 }
750
751 /* Issue the read */
752 Status = DeviceEntry->Callbacks.Read(DeviceEntry,
753 Buffer,
754 Size,
755 &BytesTransferred);
756 if (!DeviceEntry->Unknown)
757 {
758 /* Update performance counters */
759 DmDeviceIoInformation.ReadCount += BytesTransferred;
760 }
761
762 /* Return back how many bytes were read, if caller wants to know */
763 if (BytesRead)
764 {
765 *BytesRead = BytesTransferred;
766 }
767
768 /* Return read result */
769 return Status;
770 }
771
772 NTSTATUS
773 BlDeviceReadAtOffset (
774 _In_ ULONG DeviceId,
775 _In_ ULONG Size,
776 _In_ ULONGLONG Offset,
777 _In_ PVOID Buffer,
778 _Out_ PULONG BytesRead
779 )
780 {
781 NTSTATUS Status;
782 BL_DEVICE_INFORMATION DeviceInfo;
783
784 /* Get the current block and offset */
785 Status = BlDeviceGetInformation(DeviceId, &DeviceInfo);
786 if (!NT_SUCCESS(Status))
787 {
788 return Status;
789 }
790
791 /* Get the block and block-offset based on the new raw offset */
792 DeviceInfo.BlockDeviceInfo.Block = Offset / DeviceInfo.BlockDeviceInfo.BlockSize;
793 DeviceInfo.BlockDeviceInfo.Offset = Offset % DeviceInfo.BlockDeviceInfo.BlockSize;
794
795 /* Update the block and offset */
796 Status = BlDeviceSetInformation(DeviceId, &DeviceInfo);
797 if (NT_SUCCESS(Status))
798 {
799 /* Now issue a read, with this block and offset configured */
800 Status = BlDeviceRead(DeviceId, Buffer, Size, BytesRead);
801 }
802
803 /* All good, return the caller */
804 return Status;
805 }
806
807 BOOLEAN
808 BlpDeviceCompare (
809 _In_ PBL_DEVICE_DESCRIPTOR Device1,
810 _In_ PBL_DEVICE_DESCRIPTOR Device2
811 )
812 {
813 BOOLEAN DeviceMatch;
814 ULONG DeviceSize;
815
816 /* Assume failure */
817 DeviceMatch = FALSE;
818
819 /* Check if the two devices exist and are identical in type */
820 if ((Device1) && (Device2) && (Device1->DeviceType == Device2->DeviceType))
821 {
822 /* Take the bigger of the two sizes */
823 DeviceSize = max(Device1->Size, Device2->Size);
824 if (DeviceSize >= (ULONG)FIELD_OFFSET(BL_DEVICE_DESCRIPTOR, Local))
825 {
826 /* Compare the two devices up to their size */
827 if (RtlEqualMemory(&Device1->Local,
828 &Device2->Local,
829 DeviceSize - FIELD_OFFSET(BL_DEVICE_DESCRIPTOR, Local)))
830 {
831 /* They match! */
832 DeviceMatch = TRUE;
833 }
834 }
835 }
836
837 /* Return matching state */
838 return DeviceMatch;
839 }
840
841 NTSTATUS
842 BlockIopFreeAllocations (
843 _In_ PBL_BLOCK_DEVICE BlockDevice
844 )
845 {
846 /* If a block device was passed in, free it */
847 if (BlockDevice)
848 {
849 BlMmFreeHeap(BlockDevice);
850 }
851
852 /* Nothing else to do */
853 return STATUS_SUCCESS;
854 }
855
856 NTSTATUS
857 BlockIoEfiGetBlockIoInformation (
858 _In_ PBL_BLOCK_DEVICE BlockDevice
859 )
860 {
861 NTSTATUS Status;
862 EFI_BLOCK_IO_MEDIA *Media;
863
864 /* Open the Block I/O protocol on this device */
865 Status = EfiOpenProtocol(BlockDevice->Handle,
866 &EfiBlockIoProtocol,
867 (PVOID*)&BlockDevice->Protocol);
868 if (!NT_SUCCESS(Status))
869 {
870 return Status;
871 }
872
873 /* Get information on the block media */
874 Media = BlockDevice->Protocol->Media;
875
876 /* Set the appropriate device flags */
877 BlockDevice->DeviceFlags = 0;
878 if (Media->RemovableMedia)
879 {
880 BlockDevice->DeviceFlags = BL_BLOCK_DEVICE_REMOVABLE_FLAG;
881 }
882 if (Media->MediaPresent)
883 {
884 BlockDevice->DeviceFlags |= BL_BLOCK_DEVICE_PRESENT_FLAG;
885 }
886
887 /* No clue */
888 BlockDevice->Unknown = 0;
889
890 /* Set the block size */
891 BlockDevice->BlockSize = Media->BlockSize;
892
893 /* Make sure there's a last block value */
894 if (!Media->LastBlock)
895 {
896 return STATUS_INVALID_PARAMETER;
897 }
898
899 /* Don't let it be too high */
900 if (Media->LastBlock > 0xFFFFFFFFFFE)
901 {
902 BlockDevice->LastBlock = 0xFFFFFFFFFFE;
903 }
904 else
905 {
906 BlockDevice->LastBlock = Media->LastBlock;
907 }
908
909 /* Make the alignment the smaller of the I/O alignment or the block size */
910 if (Media->IoAlign >= Media->BlockSize)
911 {
912 BlockDevice->Alignment = Media->IoAlign;
913 }
914 else
915 {
916 BlockDevice->Alignment = Media->BlockSize;
917 }
918
919 /* All good */
920 return STATUS_SUCCESS;
921 }
922
923 NTSTATUS
924 BlockIoEfiGetChildHandle (
925 _In_ PBL_PROTOCOL_HANDLE ProtocolInterface,
926 _In_ PBL_PROTOCOL_HANDLE ChildProtocolInterface)
927 {
928 NTSTATUS Status;
929 ULONG i, DeviceCount;
930 EFI_DEVICE_PATH *DevicePath, *ParentDevicePath;
931 EFI_HANDLE *DeviceHandles;
932 EFI_HANDLE Handle;
933
934 /* Find all the Block I/O device handles on the system */
935 DeviceCount = 0;
936 DeviceHandles = 0;
937 Status = EfiLocateHandleBuffer(ByProtocol,
938 &EfiBlockIoProtocol,
939 &DeviceCount,
940 &DeviceHandles);
941 if (!NT_SUCCESS(Status))
942 {
943 /* Failed to enumerate, bail out */
944 return Status;
945 }
946
947 /* Loop all the handles */
948 for (i = 0; i < DeviceCount; i++)
949 {
950 /* Check if this is the device itself */
951 Handle = DeviceHandles[i];
952 if (Handle == ProtocolInterface->Handle)
953 {
954 /* Skip it */
955 continue;
956 }
957
958 /* Get the device path of this device */
959 Status = EfiOpenProtocol(Handle,
960 &EfiDevicePathProtocol,
961 (PVOID*)&DevicePath);
962 if (!NT_SUCCESS(Status))
963 {
964 /* We failed, skip it */
965 continue;
966 }
967
968 /* See if we are its parent */
969 ParentDevicePath = EfiIsDevicePathParent(ProtocolInterface->Interface,
970 DevicePath);
971 if (ParentDevicePath == ProtocolInterface->Interface)
972 {
973 /* Yup, return back to caller */
974 ChildProtocolInterface->Handle = Handle;
975 ChildProtocolInterface->Interface = DevicePath;
976 Status = STATUS_SUCCESS;
977 goto Quickie;
978 }
979
980 /* Close the device path */
981 EfiCloseProtocol(Handle, &EfiDevicePathProtocol);
982 }
983
984 /* If we got here, nothing was found */
985 Status = STATUS_NO_SUCH_DEVICE;
986
987 Quickie:
988 /* Free the handle array buffer */
989 BlMmFreeHeap(DeviceHandles);
990 return Status;
991 }
992
993 NTSTATUS
994 BlockIoGetGPTDiskSignature (
995 _In_ PBL_DEVICE_ENTRY DeviceEntry,
996 _Out_ PGUID DiskSignature
997 )
998 {
999 EfiPrintf(L"GPT not supported\r\n");
1000 return STATUS_NOT_IMPLEMENTED;
1001 }
1002
1003 NTSTATUS
1004 BlockIoEfiGetDeviceInformation (
1005 _In_ PBL_DEVICE_ENTRY DeviceEntry
1006 )
1007 {
1008 NTSTATUS Status;
1009 PBL_DEVICE_DESCRIPTOR Device;
1010 PBL_BLOCK_DEVICE BlockDevice;
1011 EFI_DEVICE_PATH *LeafNode;
1012 BL_PROTOCOL_HANDLE Protocol[2];
1013 ACPI_HID_DEVICE_PATH *AcpiPath;
1014 HARDDRIVE_DEVICE_PATH *DiskPath;
1015 BOOLEAN Found;
1016 ULONG i;
1017
1018 /* Extract the identifier, and the block device object */
1019 Device = DeviceEntry->DeviceDescriptor;
1020 BlockDevice = (PBL_BLOCK_DEVICE)DeviceEntry->DeviceSpecificData;
1021
1022 /* Initialize protocol handles */
1023 Protocol[0].Handle = BlockDevice->Handle;
1024 Protocol[1].Handle = 0;
1025
1026 /* Open this device */
1027 Status = EfiOpenProtocol(Protocol[0].Handle,
1028 &EfiDevicePathProtocol,
1029 &Protocol[0].Interface);
1030 if (!NT_SUCCESS(Status))
1031 {
1032 /* Fail */
1033 return Status;
1034 }
1035
1036 /* Iterate twice -- once for the top level, once for the bottom */
1037 for (i = 0, Found = FALSE; Found == FALSE && Protocol[i].Handle; i++)
1038 {
1039 /* Check what kind of leaf node device this is */
1040 LeafNode = EfiGetLeafNode(Protocol[i].Interface);
1041 EfiPrintf(L"Pass %d, Leaf node: %p Type: %d\r\n", i, LeafNode, LeafNode->Type);
1042 if (LeafNode->Type == ACPI_DEVICE_PATH)
1043 {
1044 /* We only support floppy drives */
1045 AcpiPath = (ACPI_HID_DEVICE_PATH*)LeafNode;
1046 if ((AcpiPath->HID == EISA_PNP_ID(0x604)) ||
1047 (AcpiPath->HID == EISA_PNP_ID(0x700)))
1048 {
1049 /* Set the boot library specific device types */
1050 Device->DeviceType = LocalDevice;
1051 Device->Local.Type = FloppyDevice;
1052
1053 /* The ACPI UID is the drive number */
1054 Device->Local.FloppyDisk.DriveNumber = AcpiPath->UID;
1055
1056 /* We found a match */
1057 Found = TRUE;
1058 }
1059 }
1060 else if ((LeafNode->Type == MEDIA_DEVICE_PATH) && (i == 1))
1061 {
1062 /* Extract the disk path and check if it's a physical disk */
1063 DiskPath = (HARDDRIVE_DEVICE_PATH*)LeafNode;
1064 EfiPrintf(L"Disk path: %p Type: %lx\r\n", DiskPath, LeafNode->SubType);
1065 if (LeafNode->SubType == MEDIA_HARDDRIVE_DP)
1066 {
1067 /* Set this as a local device */
1068 Device->Local.Type = LocalDevice;
1069
1070 /* Check if this is an MBR partition */
1071 if (DiskPath->SignatureType == SIGNATURE_TYPE_MBR)
1072 {
1073 /* Set that this is a local partition */
1074 Device->DeviceType = LegacyPartitionDevice;
1075 Device->Partition.Disk.Type = LocalDevice;
1076
1077 /* Write the MBR partition signature */
1078 BlockDevice->PartitionType = MbrPartition;
1079 BlockDevice->Disk.Mbr.Signature = *(PULONG)&DiskPath->Signature[0];
1080 Found = TRUE;
1081 }
1082 else if (DiskPath->SignatureType == SIGNATURE_TYPE_GUID)
1083 {
1084 /* Set this as a GPT partition */
1085 BlockDevice->PartitionType = GptPartition;
1086 Device->Local.HardDisk.PartitionType = GptPartition;
1087
1088 /* Get the GPT signature */
1089 Status = BlockIoGetGPTDiskSignature(DeviceEntry,
1090 &Device->Local.HardDisk.Gpt.PartitionSignature);
1091 if (NT_SUCCESS(Status))
1092 {
1093 /* Copy it */
1094 RtlCopyMemory(&BlockDevice->Disk.Gpt.Signature,
1095 &Device->Local.HardDisk.Gpt.PartitionSignature,
1096 sizeof(BlockDevice->Disk.Gpt.Signature));
1097 Found = TRUE;
1098 }
1099 }
1100
1101 /* Otherwise, this is a raw disk */
1102 BlockDevice->PartitionType = RawPartition;
1103 Device->Local.HardDisk.PartitionType = RawPartition;
1104 Device->Local.HardDisk.Raw.DiskNumber = BlockIoFirmwareRawDiskCount++;
1105 }
1106 else if (LeafNode->SubType == MEDIA_CDROM_DP)
1107 {
1108 /* Set block device information */
1109 EfiPrintf(L"Found CD-ROM\r\n");
1110 BlockDevice->PartitionType = RawPartition;
1111 BlockDevice->Type = CdRomDevice;
1112
1113 /* Set CDROM data */
1114 Device->Local.Type = CdRomDevice;
1115 Device->Local.FloppyDisk.DriveNumber = 0;
1116 Found = TRUE;
1117 }
1118 }
1119 else if ((LeafNode->Type != MEDIA_DEVICE_PATH) &&
1120 (LeafNode->Type != ACPI_DEVICE_PATH) &&
1121 (i == 0))
1122 {
1123 /* This is probably a messaging device node. Are we under it? */
1124 Status = BlockIoEfiGetChildHandle(Protocol, &Protocol[1]);
1125 EfiPrintf(L"Pass 0, non DP/ACPI path. Child handle obtained: %lx\r\n", Protocol[1].Handle);
1126 if (!NT_SUCCESS(Status))
1127 {
1128 /* We're not. So this must be a raw device */
1129 Device->DeviceType = LocalDevice;
1130 Found = TRUE;
1131
1132 /* Is it a removable raw device? */
1133 if (BlockDevice->DeviceFlags & BL_BLOCK_DEVICE_REMOVABLE_FLAG)
1134 {
1135 /* This is a removable (CD or Floppy or USB) device */
1136 BlockDevice->Type = FloppyDevice;
1137 Device->Local.Type = FloppyDevice;
1138 Device->Local.FloppyDisk.DriveNumber = BlockIoFirmwareRemovableDiskCount++;
1139 EfiPrintf(L"Found Floppy\r\n");
1140 }
1141 else
1142 {
1143 /* It's a fixed device */
1144 BlockDevice->Type = DiskDevice;
1145 Device->Local.Type = DiskDevice;
1146
1147 /* Set it as a raw partition */
1148 Device->Local.HardDisk.PartitionType = RawPartition;
1149 Device->Local.HardDisk.Mbr.PartitionSignature = BlockIoFirmwareRawDiskCount++;
1150 EfiPrintf(L"Found raw disk\r\n");
1151 }
1152 }
1153 }
1154 }
1155
1156 /* Close any protocols that we opened for each handle */
1157 while (i)
1158 {
1159 EfiCloseProtocol(Protocol[--i].Handle, &EfiDevicePathProtocol);
1160 }
1161
1162 /* Return appropriate status */
1163 return Found ? STATUS_SUCCESS : STATUS_NOT_SUPPORTED;
1164 }
1165
1166 NTSTATUS
1167 BlockIoEfiReset (
1168 VOID
1169 )
1170 {
1171 EfiPrintf(L"not implemented\r\n");
1172 return STATUS_NOT_IMPLEMENTED;
1173 }
1174
1175 NTSTATUS
1176 BlockIoEfiFlush (
1177 VOID
1178 )
1179 {
1180 EfiPrintf(L"not implemented\r\n");
1181 return STATUS_NOT_IMPLEMENTED;
1182 }
1183
1184 NTSTATUS
1185 BlockIoEfiCreateDeviceEntry (
1186 _In_ PBL_DEVICE_ENTRY *DeviceEntry,
1187 _Out_ PVOID Handle
1188 )
1189 {
1190 PBL_DEVICE_ENTRY IoDeviceEntry;
1191 PBL_BLOCK_DEVICE BlockDevice;
1192 NTSTATUS Status;
1193 PBL_DEVICE_DESCRIPTOR Device;
1194
1195 /* Allocate the entry for this device and zero it out */
1196 IoDeviceEntry = BlMmAllocateHeap(sizeof(*IoDeviceEntry));
1197 if (!IoDeviceEntry)
1198 {
1199 return STATUS_NO_MEMORY;
1200 }
1201 RtlZeroMemory(IoDeviceEntry, sizeof(*IoDeviceEntry));
1202
1203 /* Allocate the device descriptor for this device and zero it out */
1204 Device = BlMmAllocateHeap(sizeof(*Device));
1205 if (!Device)
1206 {
1207 return STATUS_NO_MEMORY;
1208 }
1209 RtlZeroMemory(Device, sizeof(*Device));
1210
1211 /* Allocate the block device specific data, and zero it out */
1212 BlockDevice = BlMmAllocateHeap(sizeof(*BlockDevice));
1213 if (!BlockDevice)
1214 {
1215 return STATUS_NO_MEMORY;
1216 }
1217 RtlZeroMemory(BlockDevice, sizeof(*BlockDevice));
1218
1219 /* Save the descriptor and block device specific data */
1220 IoDeviceEntry->DeviceSpecificData = BlockDevice;
1221 IoDeviceEntry->DeviceDescriptor = Device;
1222
1223 /* Set the size of the descriptor */
1224 Device->Size = sizeof(*Device);
1225
1226 /* Copy the standard I/O callbacks */
1227 RtlCopyMemory(&IoDeviceEntry->Callbacks,
1228 &BlockIoDeviceFunctionTable,
1229 sizeof(IoDeviceEntry->Callbacks));
1230
1231 /* Add the two that are firmware specific */
1232 IoDeviceEntry->Callbacks.Reset = BlockIoEfiReset;
1233 IoDeviceEntry->Callbacks.Flush = BlockIoEfiFlush;
1234
1235 /* Save the EFI handle */
1236 BlockDevice->Handle = Handle;
1237
1238 /* Get information on this device from EFI, caching it in the device */
1239 Status = BlockIoEfiGetBlockIoInformation(BlockDevice);
1240 if (NT_SUCCESS(Status))
1241 {
1242 /* Build the descriptor structure for this device */
1243 Status = BlockIoEfiGetDeviceInformation(IoDeviceEntry);
1244 if (NT_SUCCESS(Status))
1245 {
1246 /* We have a fully constructed device, return it */
1247 *DeviceEntry = IoDeviceEntry;
1248 return STATUS_SUCCESS;
1249 }
1250 }
1251
1252 /* Failure path, free the descriptor if we allocated one */
1253 if (IoDeviceEntry->DeviceDescriptor)
1254 {
1255 BlMmFreeHeap(IoDeviceEntry->DeviceDescriptor);
1256 }
1257
1258 /* Free any other specific allocations */
1259 BlockIopFreeAllocations(IoDeviceEntry->DeviceSpecificData);
1260
1261 /* Free the device entry itself and return the failure code */
1262 BlMmFreeHeap(IoDeviceEntry);
1263 EfiPrintf(L"Failed: %lx\r\n", Status);
1264 return Status;
1265 }
1266
1267 NTSTATUS
1268 BlockIoEfiCompareDevice (
1269 _In_ PBL_DEVICE_DESCRIPTOR Device,
1270 _In_ EFI_HANDLE Handle
1271 )
1272 {
1273 PBL_LOCAL_DEVICE LocalDeviceInfo, EfiLocalDeviceInfo;
1274 PBL_DEVICE_ENTRY DeviceEntry;
1275 PBL_DEVICE_DESCRIPTOR EfiDevice;
1276 NTSTATUS Status;
1277
1278 DeviceEntry = NULL;
1279
1280 /* Check if no device was given */
1281 if (!Device)
1282 {
1283 /* Fail the comparison */
1284 Status = STATUS_INVALID_PARAMETER;
1285 goto Quickie;
1286 }
1287
1288 /* Check if this is a local disk device */
1289 if (Device->DeviceType != DiskDevice)
1290 {
1291 /* Nope -- is it a partition device? */
1292 if ((Device->DeviceType != LegacyPartitionDevice) &&
1293 (Device->DeviceType != PartitionDevice))
1294 {
1295 /* Nope, so we can't compare */
1296 Status = STATUS_INVALID_PARAMETER;
1297 goto Quickie;
1298 }
1299
1300 /* If so, return the device information for the parent disk */
1301 LocalDeviceInfo = &Device->Partition.Disk;
1302 }
1303 else
1304 {
1305 /* Just return the disk information itself */
1306 LocalDeviceInfo = &Device->Local;
1307 }
1308
1309 /* Create an EFI device entry for the EFI device handle */
1310 Status = BlockIoEfiCreateDeviceEntry(&DeviceEntry, Handle);
1311 if (!NT_SUCCESS(Status))
1312 {
1313 goto Quickie;
1314 }
1315
1316 /* Read the descriptor and assume failure for now */
1317 EfiDevice = DeviceEntry->DeviceDescriptor;
1318 Status = STATUS_UNSUCCESSFUL;
1319
1320 /* Check if the EFI device is a disk */
1321 if (EfiDevice->DeviceType != DiskDevice)
1322 {
1323 /* Nope, is it a partition? */
1324 if ((EfiDevice->DeviceType != LegacyPartitionDevice) &&
1325 (EfiDevice->DeviceType != PartitionDevice))
1326 {
1327 /* Neither, invalid handle so bail out */
1328 Status = STATUS_INVALID_PARAMETER;
1329 goto Quickie;
1330 }
1331
1332 /* Yes, so get the information of the parent disk */
1333 EfiLocalDeviceInfo = &EfiDevice->Partition.Disk;
1334 }
1335 else
1336 {
1337 /* It's a disk, so get the disk information itself */
1338 EfiLocalDeviceInfo = &EfiDevice->Local;
1339 }
1340
1341 /* Are the two devices the same type? */
1342 if (EfiLocalDeviceInfo->Type != LocalDeviceInfo->Type)
1343 {
1344 /* Nope, that was easy */
1345 goto Quickie;
1346 }
1347
1348 /* Yes, what kind of device is the EFI side? */
1349 switch (EfiLocalDeviceInfo->Type)
1350 {
1351 case LocalDevice:
1352
1353 /* Local hard drive, compare the signature */
1354 if (RtlCompareMemory(&EfiLocalDeviceInfo->HardDisk,
1355 &LocalDeviceInfo->HardDisk,
1356 sizeof(LocalDeviceInfo->HardDisk)) ==
1357 sizeof(LocalDeviceInfo->HardDisk))
1358 {
1359 Status = STATUS_SUCCESS;
1360 }
1361 break;
1362
1363 case FloppyDevice:
1364 case CdRomDevice:
1365
1366 /* Removable floppy or CD, compare the disk number */
1367 if (RtlCompareMemory(&EfiLocalDeviceInfo->FloppyDisk,
1368 &LocalDeviceInfo->FloppyDisk,
1369 sizeof(LocalDeviceInfo->FloppyDisk)) ==
1370 sizeof(LocalDeviceInfo->FloppyDisk))
1371 {
1372 Status = STATUS_SUCCESS;
1373 }
1374 break;
1375
1376 case RamDiskDevice:
1377
1378 /* RAM disk, compare the size and base information */
1379 if (RtlCompareMemory(&EfiLocalDeviceInfo->RamDisk,
1380 &LocalDeviceInfo->RamDisk,
1381 sizeof(LocalDeviceInfo->RamDisk)) ==
1382 sizeof(LocalDeviceInfo->RamDisk))
1383 {
1384 Status = STATUS_SUCCESS;
1385 }
1386 break;
1387
1388 case FileDevice:
1389
1390 /* File, compare the file identifier */
1391 if (RtlCompareMemory(&EfiLocalDeviceInfo->File,
1392 &LocalDeviceInfo->File,
1393 sizeof(LocalDeviceInfo->File)) ==
1394 sizeof(LocalDeviceInfo->File))
1395 {
1396 Status = STATUS_SUCCESS;
1397 }
1398 break;
1399
1400 /* Something else we don't support */
1401 default:
1402 break;
1403 }
1404
1405 Quickie:
1406 /* All done, did we have an EFI device entry? */
1407 if (DeviceEntry)
1408 {
1409 /* Free it, since we only needed it locally for comparison */
1410 BlMmFreeHeap(DeviceEntry->DeviceDescriptor);
1411 BlockIopFreeAllocations(DeviceEntry->DeviceSpecificData);
1412 BlMmFreeHeap(DeviceEntry);
1413 }
1414
1415 /* Return back to the caller */
1416 return Status;
1417 }
1418
1419 NTSTATUS
1420 BlockIoFirmwareOpen (
1421 _In_ PBL_DEVICE_DESCRIPTOR Device,
1422 _In_ PBL_BLOCK_DEVICE BlockIoDevice
1423 )
1424 {
1425 NTSTATUS Status;
1426 BOOLEAN DeviceMatch;
1427 BL_HASH_ENTRY HashEntry;
1428 ULONG i, Id, DeviceCount;
1429 PBL_DEVICE_ENTRY DeviceEntry;
1430 EFI_HANDLE* DeviceHandles;
1431
1432 /* Initialize everything */
1433 DeviceEntry = NULL;
1434 DeviceCount = 0;
1435 DeviceHandles = 0;
1436 DeviceEntry = NULL;
1437
1438 /* Ask EFI for handles to all block devices */
1439 Status = EfiLocateHandleBuffer(ByProtocol,
1440 &EfiBlockIoProtocol,
1441 &DeviceCount,
1442 &DeviceHandles);
1443 if (!NT_SUCCESS(Status))
1444 {
1445 return STATUS_NO_SUCH_DEVICE;
1446 }
1447
1448 /* Build a hash entry, with the value inline */
1449 HashEntry.Flags = BL_HT_VALUE_IS_INLINE;
1450 HashEntry.Size = sizeof(EFI_HANDLE);
1451
1452 /* Loop each device we got */
1453 DeviceMatch = FALSE;
1454 Status = STATUS_NO_SUCH_DEVICE;
1455 for (i = 0; i < DeviceCount; i++)
1456 {
1457 /* Check if we have a match in the device hash table */
1458 HashEntry.Value = DeviceHandles[i];
1459 Status = BlHtLookup(HashTableId, &HashEntry, 0);
1460 if (NT_SUCCESS(Status))
1461 {
1462 /* We already know about this device */
1463 EfiPrintf(L"Device is known\r\n");
1464 continue;
1465 }
1466
1467 /* New device, store it in the hash table */
1468 Status = BlHtStore(HashTableId,
1469 &HashEntry,
1470 DeviceHandles[i],
1471 sizeof(DeviceHandles[i]));
1472 if (!NT_SUCCESS(Status))
1473 {
1474 /* Free the array and fail */
1475 BlMmFreeHeap(DeviceHandles);
1476 break;
1477 }
1478
1479 /* Create an entry for this device*/
1480 Status = BlockIoEfiCreateDeviceEntry(&DeviceEntry, DeviceHandles[i]);
1481 if (!NT_SUCCESS(Status))
1482 {
1483 EfiPrintf(L"EFI create failed: %lx\r\n", Status);
1484 continue;
1485 }
1486
1487 /* Add the device entry to the device table */
1488 Status = BlTblSetEntry(&BlockIoDeviceTable,
1489 &BlockIoDeviceTableEntries,
1490 DeviceEntry,
1491 &Id,
1492 TblDoNotPurgeEntry);
1493 if (!NT_SUCCESS(Status))
1494 {
1495 /* Remove it from teh hash table */
1496 BlHtDelete(HashTableId, &HashEntry);
1497
1498 /* Free the block I/O device data */
1499 BlockIopFreeAllocations(DeviceEntry->DeviceSpecificData);
1500
1501 /* Free the descriptor */
1502 BlMmFreeHeap(DeviceEntry->DeviceDescriptor);
1503
1504 /* Free the entry */
1505 BlMmFreeHeap(DeviceEntry);
1506 break;
1507 }
1508
1509 /* Does this device match what we're looking for? */
1510 DeviceMatch = BlpDeviceCompare(DeviceEntry->DeviceDescriptor, Device);
1511 if (DeviceMatch)
1512 {
1513 /* Yep, return the data back */
1514 RtlCopyMemory(BlockIoDevice,
1515 DeviceEntry->DeviceSpecificData,
1516 sizeof(*BlockIoDevice));
1517 Status = STATUS_SUCCESS;
1518 break;
1519 }
1520 }
1521
1522 /* Free the device handle buffer array */
1523 BlMmFreeHeap(DeviceHandles);
1524
1525 /* Return status */
1526 return Status;
1527 }
1528
1529 NTSTATUS
1530 PartitionOpen (
1531 _In_ PBL_DEVICE_DESCRIPTOR Device,
1532 _In_ PBL_DEVICE_ENTRY DeviceEntry
1533 )
1534 {
1535 EfiPrintf(L"Not implemented!\r\n");
1536 return STATUS_NOT_IMPLEMENTED;
1537 }
1538
1539 NTSTATUS
1540 VhdFileDeviceOpen (
1541 _In_ PBL_DEVICE_DESCRIPTOR Device,
1542 _In_ PBL_DEVICE_ENTRY DeviceEntry
1543 )
1544 {
1545 EfiPrintf(L"Not implemented!\r\n");
1546 return STATUS_NOT_IMPLEMENTED;
1547 }
1548
1549 NTSTATUS
1550 DiskClose (
1551 _In_ PBL_DEVICE_ENTRY DeviceEntry
1552 )
1553 {
1554 NTSTATUS Status, LocalStatus;
1555 PBL_BLOCK_DEVICE BlockDevice;
1556
1557 /* Assume success */
1558 Status = STATUS_SUCCESS;
1559 BlockDevice = DeviceEntry->DeviceSpecificData;
1560
1561 /* Close the protocol */
1562 LocalStatus = EfiCloseProtocol(BlockDevice->Handle, &EfiBlockIoProtocol);
1563 if (!NT_SUCCESS(LocalStatus))
1564 {
1565 /* Only inherit failures */
1566 Status = LocalStatus;
1567 }
1568
1569 /* Free the block device allocations */
1570 LocalStatus = BlockIopFreeAllocations(BlockDevice);
1571 if (!NT_SUCCESS(LocalStatus))
1572 {
1573 /* Only inherit failures */
1574 Status = LocalStatus;
1575 }
1576
1577 /* Return back to caller */
1578 return Status;
1579 }
1580
1581 NTSTATUS
1582 DiskOpen (
1583 _In_ PBL_DEVICE_DESCRIPTOR Device,
1584 _In_ PBL_DEVICE_ENTRY DeviceEntry
1585 )
1586 {
1587 NTSTATUS Status;
1588
1589 /* Use firmware-specific functions to open the disk */
1590 Status = BlockIoFirmwareOpen(Device, DeviceEntry->DeviceSpecificData);
1591 if (NT_SUCCESS(Status))
1592 {
1593 /* Overwrite with our own close routine */
1594 DeviceEntry->Callbacks.Close = DiskClose;
1595 }
1596
1597 /* Return back to caller */
1598 return Status;
1599 }
1600
1601 NTSTATUS
1602 RdDeviceOpen (
1603 _In_ PBL_DEVICE_DESCRIPTOR Device,
1604 _In_ PBL_DEVICE_ENTRY DeviceEntry
1605 )
1606 {
1607 EfiPrintf(L"Not implemented!\r\n");
1608 return STATUS_NOT_IMPLEMENTED;
1609 }
1610
1611 NTSTATUS
1612 FileDeviceOpen (
1613 _In_ PBL_DEVICE_DESCRIPTOR Device,
1614 _In_ PBL_DEVICE_ENTRY DeviceEntry
1615 )
1616 {
1617 EfiPrintf(L"Not implemented!\r\n");
1618 return STATUS_NOT_IMPLEMENTED;
1619 }
1620
1621 NTSTATUS
1622 SpOpen (
1623 _In_ PBL_DEVICE_DESCRIPTOR Device,
1624 _In_ PBL_DEVICE_ENTRY DeviceEntry
1625 )
1626 {
1627 EfiPrintf(L"Not implemented!\r\n");
1628 return STATUS_NOT_IMPLEMENTED;
1629 }
1630
1631 NTSTATUS
1632 UdpOpen (
1633 _In_ PBL_DEVICE_DESCRIPTOR Device,
1634 _In_ PBL_DEVICE_ENTRY DeviceEntry
1635 )
1636 {
1637 EfiPrintf(L"Not implemented!\r\n");
1638 return STATUS_NOT_IMPLEMENTED;
1639 }
1640
1641 BL_DEVICE_CALLBACKS FileDeviceFunctionTable =
1642 {
1643 NULL,
1644 FileDeviceOpen,
1645 NULL,
1646 };
1647
1648 BL_DEVICE_CALLBACKS PartitionDeviceFunctionTable =
1649 {
1650 NULL,
1651 PartitionOpen,
1652 NULL,
1653 };
1654
1655 BL_DEVICE_CALLBACKS RamDiskDeviceFunctionTable =
1656 {
1657 NULL,
1658 RdDeviceOpen,
1659 NULL,
1660 };
1661
1662 BL_DEVICE_CALLBACKS DiskDeviceFunctionTable =
1663 {
1664 NULL,
1665 DiskOpen,
1666 NULL,
1667 };
1668
1669 BL_DEVICE_CALLBACKS VirtualDiskDeviceFunctionTable =
1670 {
1671 NULL,
1672 VhdFileDeviceOpen,
1673 NULL,
1674 };
1675
1676 BL_DEVICE_CALLBACKS UdpFunctionTable =
1677 {
1678 NULL,
1679 UdpOpen,
1680 NULL,
1681 };
1682
1683 BL_DEVICE_CALLBACKS SerialPortFunctionTable =
1684 {
1685 NULL,
1686 SpOpen,
1687 NULL,
1688 };
1689
1690 BOOLEAN
1691 DeviceTableCompare (
1692 _In_ PVOID Entry,
1693 _In_ PVOID Argument1,
1694 _In_ PVOID Argument2,
1695 _Inout_ PVOID Argument3,
1696 _Inout_ PVOID Argument4
1697 )
1698 {
1699 BOOLEAN Found;
1700 PBL_DEVICE_DESCRIPTOR Device = (PBL_DEVICE_DESCRIPTOR)Argument1;
1701 PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry;
1702 ULONG Flags = *(PULONG)Argument2;
1703 ULONG Unknown = *(PULONG)Argument3;
1704
1705 /* Assume failure */
1706 Found = FALSE;
1707
1708 /* Compare the device descriptor */
1709 if (BlpDeviceCompare(DeviceEntry->DeviceDescriptor, Device))
1710 {
1711 /* Compare something */
1712 if (DeviceEntry->Unknown == Unknown)
1713 {
1714 /* Compare flags */
1715 if ((!(Flags & BL_DEVICE_READ_ACCESS) || (DeviceEntry->Flags & BL_DEVICE_ENTRY_READ_ACCESS)) &&
1716 (!(Flags & BL_DEVICE_WRITE_ACCESS) || (DeviceEntry->Flags & BL_DEVICE_ENTRY_WRITE_ACCESS)))
1717 {
1718 /* And more flags */
1719 if (((Flags & 8) || !(DeviceEntry->Flags & 8)) &&
1720 (!(Flags & 8) || (DeviceEntry->Flags & 8)))
1721 {
1722 /* Found a match! */
1723 Found = TRUE;
1724 }
1725 }
1726 }
1727 }
1728
1729 /* Return matching state */
1730 return Found;
1731 }
1732
1733 NTSTATUS
1734 DeviceTableDestroyEntry (
1735 _In_ PVOID Entry,
1736 _In_ ULONG DeviceId
1737 )
1738 {
1739 PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry;
1740 NTSTATUS Status;
1741
1742 /* Call the close routine for this entry */
1743 Status = DeviceEntry->Callbacks.Close(DmDeviceTable[DeviceId]);
1744
1745 /* Free the descriptor, and the device itself */
1746 BlMmFreeHeap(DeviceEntry->DeviceDescriptor);
1747 BlMmFreeHeap(DeviceEntry);
1748
1749 /* Clear out the netry, and return */
1750 DmDeviceTable[DeviceId] = NULL;
1751 return Status;
1752 }
1753
1754 NTSTATUS
1755 DeviceTablePurge (
1756 _In_ PVOID Entry
1757 )
1758 {
1759 PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry;
1760 NTSTATUS Status;
1761
1762 /* Check if the device is opened */
1763 if (DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED)
1764 {
1765 /* It is, so can't purge it */
1766 Status = STATUS_UNSUCCESSFUL;
1767 }
1768 else
1769 {
1770 /* It isn't, so destroy the entry */
1771 Status = DeviceTableDestroyEntry(DeviceEntry, DeviceEntry->DeviceId);
1772 }
1773
1774 /* Return back to caller */
1775 return Status;
1776 }
1777
1778 NTSTATUS
1779 BlockIoDeviceTableDestroyEntry (
1780 _In_ PVOID Entry,
1781 _In_ ULONG DeviceId
1782 )
1783 {
1784 PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry;
1785 NTSTATUS Status;
1786
1787 /* Call the close routine for this entry */
1788 Status = DeviceEntry->Callbacks.Close(DeviceEntry);
1789
1790 /* Free the descriptor, and the device itself */
1791 BlMmFreeHeap(DeviceEntry->DeviceDescriptor);
1792 BlMmFreeHeap(DeviceEntry);
1793
1794 /* Clear out the netry, and return */
1795 BlockIoDeviceTable[DeviceId] = NULL;
1796 return Status;
1797 }
1798
1799 NTSTATUS
1800 BlockIoDeviceTableDestroy (
1801 VOID
1802 )
1803 {
1804 NTSTATUS Status;
1805
1806 /* Call the entry destructor on each entry in the table */
1807 Status = BlTblMap(BlockIoDeviceTable,
1808 BlockIoDeviceTableEntries,
1809 BlockIoDeviceTableDestroyEntry);
1810
1811 /* Free the table and return */
1812 BlMmFreeHeap(BlockIoDeviceTable);
1813 return Status;
1814 }
1815
1816 NTSTATUS
1817 BlockIopDestroy (
1818 VOID
1819 )
1820 {
1821 /* Free the prefetch buffer */
1822 BlMmFreeHeap(BlockIopPrefetchBuffer);
1823
1824 /* Set state to non initialized */
1825 BlockIoInitialized = FALSE;
1826
1827 /* Return back */
1828 return STATUS_SUCCESS;
1829 }
1830
1831 ULONG
1832 BlockIoEfiHashFunction (
1833 _In_ PBL_HASH_ENTRY Entry,
1834 _In_ ULONG TableSize
1835 )
1836 {
1837 /* Get rid of the alignment bits to have a more unique number */
1838 return ((ULONG)Entry->Value >> 3) % TableSize;
1839 }
1840
1841 NTSTATUS
1842 BlockIopInitialize (
1843 VOID
1844 )
1845 {
1846 NTSTATUS Status;
1847
1848 /* Allocate the block device table and zero it out */
1849 BlockIoDeviceTableEntries = 8;
1850 BlockIoDeviceTable = BlMmAllocateHeap(sizeof(PVOID) *
1851 BlockIoDeviceTableEntries);
1852 if (!BlockIoDeviceTableEntries)
1853 {
1854 return STATUS_NO_MEMORY;
1855 }
1856 RtlZeroMemory(BlockIoDeviceTable, sizeof(PVOID) * BlockIoDeviceTableEntries);
1857
1858 /* Register our destructor */
1859 Status = BlpIoRegisterDestroyRoutine(BlockIoDeviceTableDestroy);
1860 if (!NT_SUCCESS(Status))
1861 {
1862 return Status;
1863 }
1864
1865 /* Initialize all counters */
1866 BlockIoFirmwareRemovableDiskCount = 0;
1867 BlockIoFirmwareRawDiskCount = 0;
1868 BlockIoFirmwareCdromCount = 0;
1869
1870 /* Initialize the buffers and their sizes */
1871 BlockIopAlignedBuffer = NULL;
1872 BlockIopAlignedBufferSize = 0;
1873 BlockIopPartialBlockBuffer = NULL;
1874 BlockIopPartialBlockBufferSize = 0;
1875 BlockIopPrefetchBuffer = NULL;
1876 BlockIopReadBlockBuffer = NULL;
1877 BlockIopReadBlockBufferSize = 0;
1878
1879 /* Allocate the prefetch buffer */
1880 Status = MmPapAllocatePagesInRange(&BlockIopPrefetchBuffer,
1881 BlLoaderDeviceMemory,
1882 0x100,
1883 0,
1884 0,
1885 NULL,
1886 0);
1887 if (NT_SUCCESS(Status))
1888 {
1889 /* Initialize the block cache */
1890 Status = BcInitialize();
1891 if (NT_SUCCESS(Status))
1892 {
1893 /* Initialize the block device hash table */
1894 Status = BlHtCreate(29, BlockIoEfiHashFunction, NULL, &HashTableId);
1895 if (NT_SUCCESS(Status))
1896 {
1897 /* Register our destructor */
1898 Status = BlpIoRegisterDestroyRoutine(BlockIopDestroy);
1899 if (NT_SUCCESS(Status))
1900 {
1901 /* We're good */
1902 BlockIoInitialized = TRUE;
1903 }
1904 }
1905 }
1906 }
1907
1908 /* Check if this is the failure path */
1909 if (!NT_SUCCESS(Status))
1910 {
1911 /* Free the prefetch buffer is one was allocated */
1912 if (BlockIopPrefetchBuffer)
1913 {
1914 MmPapFreePages(BlockIopPrefetchBuffer, BL_MM_INCLUDE_MAPPED_ALLOCATED);
1915 }
1916 }
1917
1918 /* Return back to the caller */
1919 return Status;
1920 }
1921
1922 BOOLEAN
1923 BlockIoDeviceTableCompare (
1924 _In_ PVOID Entry,
1925 _In_ PVOID Argument1,
1926 _In_ PVOID Argument2,
1927 _In_ PVOID Argument3,
1928 _In_ PVOID Argument4
1929 )
1930 {
1931 PBL_DEVICE_ENTRY DeviceEntry = (PBL_DEVICE_ENTRY)Entry;
1932 PBL_DEVICE_DESCRIPTOR Device = (PBL_DEVICE_DESCRIPTOR)Argument1;
1933
1934 /* Compare the two devices */
1935 return BlpDeviceCompare(DeviceEntry->DeviceDescriptor, Device);
1936 }
1937
1938 NTSTATUS
1939 BlockIoOpen (
1940 _In_ PBL_DEVICE_DESCRIPTOR Device,
1941 _In_ PBL_DEVICE_ENTRY DeviceEntry
1942 )
1943 {
1944 NTSTATUS Status;
1945 PBL_BLOCK_DEVICE BlockDevice;
1946 PBL_DEVICE_ENTRY FoundDeviceEntry;
1947 ULONG Dummy;
1948
1949 /* Check if the block I/O manager is initialized */
1950 if (!BlockIoInitialized)
1951 {
1952 /* First call, initialize it now */
1953 Status = BlockIopInitialize();
1954 if (!NT_SUCCESS(Status))
1955 {
1956 /* Failed to initialize block I/O */
1957 return Status;
1958 }
1959 }
1960
1961 /* Copy a function table for block I/O devices */
1962 RtlCopyMemory(&DeviceEntry->Callbacks,
1963 &BlockIoDeviceFunctionTable,
1964 sizeof(DeviceEntry->Callbacks));
1965
1966 /* Allocate a block I/O device */
1967 BlockDevice = BlMmAllocateHeap(sizeof(*BlockDevice));
1968 if (!BlockDevice)
1969 {
1970 return STATUS_NO_MEMORY;
1971 }
1972
1973 /* Set this as the device-specific data for this device entry */
1974 Status = STATUS_SUCCESS;
1975 DeviceEntry->DeviceSpecificData = BlockDevice;
1976
1977 /* Check if we already have this device in our device table */
1978 FoundDeviceEntry = BlTblFindEntry(BlockIoDeviceTable,
1979 BlockIoDeviceTableEntries,
1980 &Dummy,
1981 BlockIoDeviceTableCompare,
1982 Device,
1983 NULL,
1984 NULL,
1985 NULL);
1986 if (FoundDeviceEntry)
1987 {
1988 /* We already found a device, so copy its device data and callbacks */
1989 //EfiPrintf(L"Block I/O Device entry found: %p\r\n", FoundDeviceEntry);
1990 RtlCopyMemory(BlockDevice, FoundDeviceEntry->DeviceSpecificData, sizeof(*BlockDevice));
1991 RtlCopyMemory(&DeviceEntry->Callbacks,
1992 &FoundDeviceEntry->Callbacks,
1993 sizeof(DeviceEntry->Callbacks));
1994 return Status;
1995 }
1996
1997 /* Zero out the device for now */
1998 RtlZeroMemory(BlockDevice, sizeof(*BlockDevice));
1999
2000 /* Is this a disk? */
2001 if (Device->DeviceType == DiskDevice)
2002 {
2003 /* What type of disk is it? */
2004 switch (Device->Local.Type)
2005 {
2006 /* Is it a raw physical disk? */
2007 case LocalDevice:
2008 case FloppyDevice:
2009 case CdRomDevice:
2010 /* Open a disk device */
2011 Status = DiskDeviceFunctionTable.Open(Device, DeviceEntry);
2012 break;
2013
2014 /* Is it a RAM disk? */
2015 case RamDiskDevice:
2016 /* Open a RAM disk */
2017 Status = RamDiskDeviceFunctionTable.Open(Device, DeviceEntry);
2018 break;
2019
2020 /* Is it a file? */
2021 case FileDevice:
2022 /* Open a file */
2023 Status = FileDeviceFunctionTable.Open(Device, DeviceEntry);
2024 break;
2025
2026 /* Is it a VHD? */
2027 case VirtualDiskDevice:
2028 /* Open a virtual disk */
2029 Status = VirtualDiskDeviceFunctionTable.Open(Device, DeviceEntry);
2030 break;
2031
2032 /* Is it something else? */
2033 default:
2034 /* Not supported */
2035 Status = STATUS_INVALID_PARAMETER;
2036 break;
2037 }
2038 }
2039 else if ((Device->DeviceType == LegacyPartitionDevice) ||
2040 (Device->DeviceType == PartitionDevice))
2041 {
2042 /* This is a partition on a disk, open it as such */
2043 Status = PartitionDeviceFunctionTable.Open(Device, DeviceEntry);
2044 }
2045 else
2046 {
2047 /* Other devices are not supported */
2048 Status = STATUS_INVALID_PARAMETER;
2049 }
2050
2051 /* Check for failure */
2052 if (!NT_SUCCESS(Status))
2053 {
2054 /* Free any allocations for this device */
2055 BlockIopFreeAllocations(BlockDevice);
2056 }
2057
2058 /* Return back to the caller */
2059 return Status;
2060 }
2061
2062 NTSTATUS
2063 BlpDeviceResolveLocate (
2064 _In_ PBL_DEVICE_DESCRIPTOR InputDevice,
2065 _Out_ PBL_DEVICE_DESCRIPTOR* LocateDevice
2066 )
2067 {
2068 EfiPrintf(L"Not implemented!\r\n");
2069 return STATUS_NOT_IMPLEMENTED;
2070 }
2071
2072 NTSTATUS
2073 BlDeviceClose (
2074 _In_ ULONG DeviceId
2075 )
2076 {
2077 PBL_DEVICE_ENTRY DeviceEntry;
2078
2079 /* Validate the device ID */
2080 if (DmTableEntries <= DeviceId)
2081 {
2082 return STATUS_INVALID_PARAMETER;
2083 }
2084
2085 /* Make sure there's a device there */
2086 DeviceEntry = DmDeviceTable[DeviceId];
2087 if (DeviceEntry == NULL)
2088 {
2089 return STATUS_INVALID_PARAMETER;
2090 }
2091
2092 /* Make sure the device is active */
2093 if (!(DeviceEntry->Flags & BL_DEVICE_ENTRY_OPENED))
2094 {
2095 return STATUS_INVALID_PARAMETER;
2096 }
2097
2098 /* Drop a reference and check if it's the last one */
2099 DeviceEntry->ReferenceCount--;
2100 if (!DeviceEntry->ReferenceCount)
2101 {
2102 /* Mark the device as inactive */
2103 DeviceEntry->Flags = ~BL_DEVICE_ENTRY_OPENED;
2104 }
2105
2106 /* We're good */
2107 return STATUS_SUCCESS;
2108 }
2109
2110 NTSTATUS
2111 BlpDeviceOpen (
2112 _In_ PBL_DEVICE_DESCRIPTOR Device,
2113 _In_ ULONG Flags,
2114 _In_ ULONG Unknown,
2115 _Out_ PULONG DeviceId
2116 )
2117 {
2118 NTSTATUS Status;
2119 PBL_DEVICE_ENTRY DeviceEntry;
2120 PBL_DEVICE_DESCRIPTOR LocateDeviceDescriptor;
2121 PBL_REGISTERED_DEVICE RegisteredDevice;
2122 PLIST_ENTRY NextEntry, ListHead;
2123
2124 DeviceEntry = NULL;
2125
2126 /* Check for missing parameters */
2127 if (!(Device) || !(DeviceId) || !(Device->Size))
2128 {
2129 /* Bail out */
2130 Status = STATUS_INVALID_PARAMETER;
2131 goto Quickie;
2132 }
2133
2134 /* Make sure both read and write access are set */
2135 if (!(Flags & (BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS)))
2136 {
2137 /* Bail out */
2138 Status = STATUS_INVALID_PARAMETER;
2139 goto Quickie;
2140 }
2141
2142 /* Check if the boot device is being opened */
2143 if (Device->DeviceType == BootDevice)
2144 {
2145 /* Select it */
2146 Device = BlpBootDevice;
2147 }
2148
2149 /* Check if the 'locate' device is being opened */
2150 if (Device->DeviceType == LocateDevice)
2151 {
2152 /* Go find it */
2153 Status = BlpDeviceResolveLocate(Device, &LocateDeviceDescriptor);
2154 if (!NT_SUCCESS(Status))
2155 {
2156 /* Not found, bail out */
2157 goto Quickie;
2158 }
2159
2160 /* Select it */
2161 Device = LocateDeviceDescriptor;
2162 }
2163
2164 /* Check if the device isn't ready yet */
2165 if (Device->Flags & 1)
2166 {
2167 /* Return a failure */
2168 Status = STATUS_DEVICE_NOT_READY;
2169 goto Quickie;
2170 }
2171
2172 /* Check if we already have an entry for the device */
2173 DeviceEntry = BlTblFindEntry(DmDeviceTable,
2174 DmTableEntries,
2175 DeviceId,
2176 DeviceTableCompare,
2177 Device,
2178 &Flags,
2179 &Unknown,
2180 NULL);
2181 if (DeviceEntry)
2182 {
2183 /* Return it, taking a reference on it */
2184 *DeviceId = DeviceEntry->DeviceId;
2185 ++DeviceEntry->ReferenceCount;
2186 DeviceEntry->Flags |= BL_DEVICE_ENTRY_OPENED;
2187 return STATUS_SUCCESS;
2188 }
2189
2190 /* We don't, allocate one */
2191 DeviceEntry = BlMmAllocateHeap(sizeof(*DeviceEntry));
2192 if (!DeviceEntry)
2193 {
2194 Status = STATUS_NO_MEMORY;
2195 goto Quickie;
2196 }
2197
2198 /* Fill it out */
2199 RtlZeroMemory(DeviceEntry, sizeof(*DeviceEntry));
2200 DeviceEntry->ReferenceCount = 1;
2201 DeviceEntry->Flags |= (BL_DEVICE_ENTRY_OPENED |
2202 BL_DEVICE_ENTRY_READ_ACCESS |
2203 BL_DEVICE_ENTRY_WRITE_ACCESS);
2204 DeviceEntry->Unknown = Unknown;
2205
2206 /* Save flag 8 if needed */
2207 if (Flags & 8)
2208 {
2209 DeviceEntry->Flags |= 8;
2210 }
2211
2212 /* Allocate a device descriptor for the device */
2213 DeviceEntry->DeviceDescriptor = BlMmAllocateHeap(Device->Size);
2214 if (!DeviceEntry->DeviceDescriptor)
2215 {
2216 Status = STATUS_NO_MEMORY;
2217 goto Quickie;
2218 }
2219
2220 /* Copy the descriptor that was passed in */
2221 RtlCopyMemory(DeviceEntry->DeviceDescriptor, Device, Device->Size);
2222
2223 /* Now loop the list of dynamically registered devices */
2224 ListHead = &DmRegisteredDevices;
2225 NextEntry = ListHead->Flink;
2226 while (NextEntry != ListHead)
2227 {
2228 /* Get the device */
2229 RegisteredDevice = CONTAINING_RECORD(NextEntry,
2230 BL_REGISTERED_DEVICE,
2231 ListEntry);
2232
2233 /* Open the device */
2234 Status = RegisteredDevice->Callbacks.Open(Device, DeviceEntry);
2235 if (NT_SUCCESS(Status))
2236 {
2237 /* The device was opened, so we have the right one */
2238 goto DeviceOpened;
2239 }
2240
2241 /* Nope, keep trying */
2242 NextEntry = NextEntry->Flink;
2243 }
2244
2245 /* Well, it wasn't a dynamic device. Is it a block device? */
2246 if ((Device->DeviceType == PartitionDevice) ||
2247 (Device->DeviceType == DiskDevice) ||
2248 (Device->DeviceType == LegacyPartitionDevice))
2249 {
2250 /* Call the Block I/O handler */
2251 Status = BlockIoDeviceFunctionTable.Open(Device, DeviceEntry);
2252 }
2253 else if (Device->DeviceType == SerialDevice)
2254 {
2255 /* It's a serial device, call the serial device handler */
2256 Status = SerialPortFunctionTable.Open(Device, DeviceEntry);
2257 }
2258 else if (Device->DeviceType == UdpDevice)
2259 {
2260 /* It's a network device, call the UDP device handler */
2261 Status = UdpFunctionTable.Open(Device, DeviceEntry);
2262 }
2263 else
2264 {
2265 /* Unsupported type of device */
2266 Status = STATUS_NOT_IMPLEMENTED;
2267 }
2268
2269 /* Check if the device was opened successfully */
2270 if (NT_SUCCESS(Status))
2271 {
2272 DeviceOpened:
2273 /* Save the entry in the device table */
2274 Status = BlTblSetEntry(&DmDeviceTable,
2275 &DmTableEntries,
2276 DeviceEntry,
2277 DeviceId,
2278 DeviceTablePurge);
2279 if (NT_SUCCESS(Status))
2280 {
2281 /* It worked -- return the ID in the table to the caller */
2282 EfiPrintf(L"Device ID: %lx\r\n", *DeviceId);
2283 DeviceEntry->DeviceId = *DeviceId;
2284 return STATUS_SUCCESS;
2285 }
2286 }
2287
2288 Quickie:
2289 /* Failure path -- did we allocate a device entry? */
2290 EfiPrintf(L"Block failure: %lx\r\n", Status);
2291 if (DeviceEntry)
2292 {
2293 /* Yep -- did it have a descriptor? */
2294 if (DeviceEntry->DeviceDescriptor)
2295 {
2296 /* Free it */
2297 BlMmFreeHeap(DeviceEntry->DeviceDescriptor);
2298 }
2299
2300 /* Free the entry */
2301 BlMmFreeHeap(DeviceEntry);
2302 }
2303
2304 /* Return the failure */
2305 return Status;
2306 }
2307
2308 NTSTATUS
2309 BlpDeviceInitialize (
2310 VOID
2311 )
2312 {
2313 NTSTATUS Status;
2314
2315 /* Initialize the table count and list of devices */
2316 DmTableEntries = 8;
2317 InitializeListHead(&DmRegisteredDevices);
2318
2319 /* Initialize device information */
2320 DmDeviceIoInformation.ReadCount = 0;
2321 DmDeviceIoInformation.WriteCount = 0;
2322
2323 /* Allocate the device table */
2324 DmDeviceTable = BlMmAllocateHeap(DmTableEntries * sizeof(PVOID));
2325 if (DmDeviceTable)
2326 {
2327 /* Clear it */
2328 RtlZeroMemory(DmDeviceTable, DmTableEntries * sizeof(PVOID));
2329 #if BL_BITLOCKER_SUPPORT
2330 /* Initialize BitLocker support */
2331 Status = FvebInitialize();
2332 #else
2333 Status = STATUS_SUCCESS;
2334 #endif
2335 }
2336 else
2337 {
2338 /* No memory, we'll fail */
2339 Status = STATUS_NO_MEMORY;
2340 }
2341
2342 /* Return initialization state */
2343 return Status;
2344 }
2345