xref: /reactos/boot/environ/lib/io/device.c (revision 3f976713)
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_PTR)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 
2346