2 Install a fake VGABIOS service handler (real mode Int10h) for the buggy
3 Windows 2008 R2 SP1 UEFI guest.
5 The handler is never meant to be directly executed by a VCPU; it's there for
6 the internal real mode emulator of Windows 2008 R2 SP1.
8 The code is based on Ralf Brown's Interrupt List:
9 <http://www.cs.cmu.edu/~ralf/files.html>
10 <http://www.ctyme.com/rbrown.htm>
12 Copyright (C) 2020, Rebecca Cran <rebecca@bsdio.com>
13 Copyright (C) 2015, Nahanni Systems, Inc.
14 Copyright (C) 2014, Red Hat, Inc.
15 Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
17 SPDX-License-Identifier: BSD-2-Clause-Patent
21 #include <IndustryStandard/LegacyVgaBios.h>
22 #include <Library/DebugLib.h>
23 #include <Library/PciLib.h>
24 #include <Library/PrintLib.h>
37 // This string is displayed by Windows 2008 R2 SP1 in the Screen Resolution,
38 // Advanced Settings dialog. It should be short.
40 STATIC CONST CHAR8 mProductRevision
[] = "2.0";
42 #define NUM_VBE_MODES 3
43 STATIC CONST UINT16 vbeModeIds
[] = {
49 // Modes can be toggled with bit-0
50 #define VBE_MODE_ENABLED 0x00BB
51 #define VBE_MODE_DISABLED 0x00BA
53 STATIC VBE2_MODE_INFO vbeModes
[] = {
55 // ModeAttr - BytesPerScanLine
56 VBE_MODE_DISABLED
, 0x07, 0x00, 0x40, 0x40, 0xA000, 0x00, 0x0000, 640*4,
57 // Width, Height..., Vbe3
58 640, 480, 16, 8, 1, 32, 1, 0x06, 0, 0, 1,
60 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x08, 0x18, 0x00,
62 0xdeadbeef, 0x0000, 0x0000
65 // ModeAttr - BytesPerScanLine
66 VBE_MODE_DISABLED
, 0x07, 0x00, 0x40, 0x40, 0xA000, 0x00, 0x0000, 800*4,
67 // Width, Height..., Vbe3
68 800, 600, 16, 8, 1, 32, 1, 0x06, 0, 0, 1,
70 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x08, 0x18, 0x00,
72 0xdeadbeef, 0x0000, 0x0000
74 { // 0x141 1024x768x32
75 // ModeAttr - BytesPerScanLine
76 VBE_MODE_ENABLED
, 0x07, 0x00, 0x40, 0x40, 0xA000, 0x00, 0x0000, 1024*4,
77 // Width, Height..., Vbe3
78 1024, 768, 16, 8, 1, 32, 1, 0x06, 0, 0, 1,
80 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x08, 0x18, 0x00,
82 0xdeadbeef, 0x0000, 0x0000
87 Install the VBE Info and VBE Mode Info structures, and the VBE service
88 handler routine in the C segment. Point the real-mode Int10h interrupt vector
89 to the handler. The only advertised mode is 1024x768x32.
91 @param[in] CardName Name of the video card to be exposed in the
92 Product Name field of the VBE Info structure.
93 @param[in] FrameBufferBase Guest-physical base address of the video card's
98 IN CONST CHAR16
*CardName
,
99 IN EFI_PHYSICAL_ADDRESS FrameBufferBase
102 EFI_PHYSICAL_ADDRESS Segment0
, SegmentC
, SegmentF
;
109 VBE_INFO
*VbeInfoFull
;
110 VBE_INFO_BASE
*VbeInfo
;
113 VBE_MODE_INFO
*VbeModeInfo
;
121 // Attempt to cover the real mode IVT with an allocation. This is a UEFI
122 // driver, hence the arch protocols have been installed previously. Among
123 // those, the CPU arch protocol has configured the IDT, so we can overwrite
124 // the IVT used in real mode.
126 // The allocation request may fail, eg. if LegacyBiosDxe has already run.
129 Int0x10
= (IVT_ENTRY
*)(UINTN
)Segment0
+ 0x10;
130 Status
= gBS
->AllocatePages (
137 if (EFI_ERROR (Status
)) {
138 EFI_PHYSICAL_ADDRESS Handler
;
141 // Check if a video BIOS handler has been installed previously -- we
142 // shouldn't override a real video BIOS with our shim, nor our own shim if
143 // it's already present.
145 Handler
= (Int0x10
->Segment
<< 4) + Int0x10
->Offset
;
146 if ((Handler
>= SegmentC
) && (Handler
< SegmentF
)) {
149 "%a: Video BIOS handler found at %04x:%04x\n",
158 // Otherwise we'll overwrite the Int10h vector, even though we may not own
163 "%a: failed to allocate page at zero: %r\n",
169 // We managed to allocate the page at zero. SVN r14218 guarantees that it
172 ASSERT (Int0x10
->Segment
== 0x0000);
173 ASSERT (Int0x10
->Offset
== 0x0000);
177 // Put the shim in place first.
179 Pam1Address
= PCI_LIB_ADDRESS (0, 0, 0, 0x5A);
181 // low nibble covers 0xC0000 to 0xC3FFF
182 // high nibble covers 0xC4000 to 0xC7FFF
183 // bit1 in each nibble is Write Enable
184 // bit0 in each nibble is Read Enable
186 Pam1
= PciRead8 (Pam1Address
);
187 PciWrite8 (Pam1Address
, Pam1
| (BIT1
| BIT0
));
190 // We never added memory space durig PEI or DXE for the C segment, so we
191 // don't need to (and can't) allocate from there. Also, guest operating
192 // systems will see a hole in the UEFI memory map there.
196 ASSERT (sizeof mVbeShim
<= EFI_PAGES_TO_SIZE (SegmentCPages
));
197 CopyMem ((VOID
*)(UINTN
)SegmentC
, mVbeShim
, sizeof mVbeShim
);
200 // Fill in the VBE INFO structure.
202 VbeInfoFull
= (VBE_INFO
*)(UINTN
)SegmentC
;
203 VbeInfo
= &VbeInfoFull
->Base
;
204 Ptr
= VbeInfoFull
->Buffer
;
206 CopyMem (VbeInfo
->Signature
, "VESA", 4);
207 VbeInfo
->VesaVersion
= 0x0200;
209 VbeInfo
->OemNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)((UINTN
)Ptr
-SegmentC
);
210 CopyMem (Ptr
, "FBSD", 5);
213 VbeInfo
->Capabilities
= BIT1
| BIT0
; // DAC can be switched into 8-bit mode
215 VbeInfo
->ModeListAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)((UINTN
)Ptr
-SegmentC
);
216 for (i
= 0; i
< NUM_VBE_MODES
; i
++) {
217 *(UINT16
*)Ptr
= vbeModeIds
[i
]; // mode number
221 *(UINT16
*)Ptr
= 0xFFFF; // mode list terminator
224 VbeInfo
->VideoMem64K
= (UINT16
)((1024 * 768 * 4 + 65535) / 65536);
225 VbeInfo
->OemSoftwareVersion
= 0x0200;
227 VbeInfo
->VendorNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)((UINTN
)Ptr
-SegmentC
);
228 CopyMem (Ptr
, "FBSD", 5);
231 VbeInfo
->ProductNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)((UINTN
)Ptr
-SegmentC
);
232 Printed
= AsciiSPrint (
234 sizeof VbeInfoFull
->Buffer
- (Ptr
- VbeInfoFull
->Buffer
),
240 VbeInfo
->ProductRevAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)((UINTN
)Ptr
-SegmentC
);
241 CopyMem (Ptr
, mProductRevision
, sizeof mProductRevision
);
242 Ptr
+= sizeof mProductRevision
;
244 ASSERT (sizeof VbeInfoFull
->Buffer
>= Ptr
- VbeInfoFull
->Buffer
);
245 ZeroMem (Ptr
, sizeof VbeInfoFull
->Buffer
- (Ptr
- VbeInfoFull
->Buffer
));
248 // Fill in the VBE MODE INFO structure list
250 VbeModeInfo
= (VBE_MODE_INFO
*)(VbeInfoFull
+ 1);
251 Ptr
= (UINT8
*)VbeModeInfo
;
252 for (i
= 0; i
< NUM_VBE_MODES
; i
++) {
253 vbeModes
[i
].LfbAddress
= (UINT32
)FrameBufferBase
;
254 CopyMem (Ptr
, &vbeModes
[i
], 0x32);
258 ZeroMem (Ptr
, 56); // Clear remaining bytes
261 // Clear Write Enable (bit1), keep Read Enable (bit0) set
263 PciWrite8 (Pam1Address
, (Pam1
& ~BIT1
) | BIT0
);
266 // Second, point the Int10h vector at the shim.
268 Int0x10
->Segment
= (UINT16
)((UINT32
)SegmentC
>> 4);
269 Int0x10
->Offset
= (UINT16
)((UINTN
)(VbeModeInfo
+ 1) - SegmentC
);
273 "%a: VBE shim installed to %x:%x\n",