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) 2014, Red Hat, Inc.
13 Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
15 SPDX-License-Identifier: BSD-2-Clause-Patent
18 #include <IndustryStandard/LegacyVgaBios.h>
19 #include <Library/DebugLib.h>
20 #include <Library/PciLib.h>
21 #include <Library/PrintLib.h>
22 #include <OvmfPlatforms.h>
35 // This string is displayed by Windows 2008 R2 SP1 in the Screen Resolution,
36 // Advanced Settings dialog. It should be short.
38 STATIC CONST CHAR8 mProductRevision
[] = "OVMF Int10h (fake)";
41 Install the VBE Info and VBE Mode Info structures, and the VBE service
42 handler routine in the C segment. Point the real-mode Int10h interrupt vector
43 to the handler. The only advertised mode is 1024x768x32.
45 @param[in] CardName Name of the video card to be exposed in the
46 Product Name field of the VBE Info structure. The
47 parameter must originate from a
48 QEMU_VIDEO_CARD.Name field.
49 @param[in] FrameBufferBase Guest-physical base address of the video card's
54 IN CONST CHAR16
*CardName
,
55 IN EFI_PHYSICAL_ADDRESS FrameBufferBase
58 EFI_PHYSICAL_ADDRESS Segment0
, SegmentC
, SegmentF
;
61 EFI_STATUS Segment0AllocationStatus
;
62 UINT16 HostBridgeDevId
;
66 VBE_INFO
*VbeInfoFull
;
67 VBE_INFO_BASE
*VbeInfo
;
70 VBE_MODE_INFO
*VbeModeInfo
;
72 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & (BIT0
|BIT7
)) == BIT0
) {
75 "%a: page 0 protected, not installing VBE shim\n",
80 "%a: page 0 protection prevents Windows 7 from booting anyway\n",
91 // Attempt to cover the real mode IVT with an allocation. This is a UEFI
92 // driver, hence the arch protocols have been installed previously. Among
93 // those, the CPU arch protocol has configured the IDT, so we can overwrite
94 // the IVT used in real mode.
96 // The allocation request may fail, eg. if LegacyBiosDxe has already run.
99 Int0x10
= (IVT_ENTRY
*)(UINTN
)(Segment0
+ 0x10 * sizeof (IVT_ENTRY
));
100 Segment0AllocationStatus
= gBS
->AllocatePages (
107 if (EFI_ERROR (Segment0AllocationStatus
)) {
108 EFI_PHYSICAL_ADDRESS Handler
;
111 // Check if a video BIOS handler has been installed previously -- we
112 // shouldn't override a real video BIOS with our shim, nor our own shim if
113 // it's already present.
115 Handler
= (Int0x10
->Segment
<< 4) + Int0x10
->Offset
;
116 if ((Handler
>= SegmentC
) && (Handler
< SegmentF
)) {
119 "%a: Video BIOS handler found at %04x:%04x\n",
128 // Otherwise we'll overwrite the Int10h vector, even though we may not own
133 "%a: failed to allocate page at zero: %r\n",
135 Segment0AllocationStatus
139 // We managed to allocate the page at zero. SVN r14218 guarantees that it
142 ASSERT (Int0x10
->Segment
== 0x0000);
143 ASSERT (Int0x10
->Offset
== 0x0000);
147 // Put the shim in place first.
149 // Start by determining the address of the PAM1 register.
151 HostBridgeDevId
= PcdGet16 (PcdOvmfHostBridgePciDevId
);
152 switch (HostBridgeDevId
) {
153 case INTEL_82441_DEVICE_ID
:
154 Pam1Address
= PMC_REGISTER_PIIX4 (PIIX4_PAM1
);
156 case INTEL_Q35_MCH_DEVICE_ID
:
157 Pam1Address
= DRAMC_REGISTER_Q35 (MCH_PAM1
);
159 case MICROVM_PSEUDO_DEVICE_ID
:
164 "%a: unknown host bridge device ID: 0x%04x\n",
170 if (!EFI_ERROR (Segment0AllocationStatus
)) {
171 gBS
->FreePages (Segment0
, Segment0Pages
);
178 // low nibble covers 0xC0000 to 0xC3FFF
179 // high nibble covers 0xC4000 to 0xC7FFF
180 // bit1 in each nibble is Write Enable
181 // bit0 in each nibble is Read Enable
183 Pam1
= PciRead8 (Pam1Address
);
184 PciWrite8 (Pam1Address
, Pam1
| (BIT1
| BIT0
));
187 // We never added memory space during PEI or DXE for the C segment, so we
188 // don't need to (and can't) allocate from there. Also, guest operating
189 // systems will see a hole in the UEFI memory map there.
193 ASSERT (sizeof mVbeShim
<= EFI_PAGES_TO_SIZE (SegmentCPages
));
194 CopyMem ((VOID
*)(UINTN
)SegmentC
, mVbeShim
, sizeof mVbeShim
);
197 // Fill in the VBE INFO structure.
199 VbeInfoFull
= (VBE_INFO
*)(UINTN
)SegmentC
;
200 VbeInfo
= &VbeInfoFull
->Base
;
201 Ptr
= VbeInfoFull
->Buffer
;
203 CopyMem (VbeInfo
->Signature
, "VESA", 4);
204 VbeInfo
->VesaVersion
= 0x0300;
206 VbeInfo
->OemNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
207 CopyMem (Ptr
, "QEMU", 5);
210 VbeInfo
->Capabilities
= BIT0
; // DAC can be switched into 8-bit mode
212 VbeInfo
->ModeListAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
213 *(UINT16
*)Ptr
= 0x00f1; // mode number
215 *(UINT16
*)Ptr
= 0xFFFF; // mode list terminator
218 VbeInfo
->VideoMem64K
= (UINT16
)((1024 * 768 * 4 + 65535) / 65536);
219 VbeInfo
->OemSoftwareVersion
= 0x0000;
221 VbeInfo
->VendorNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
222 CopyMem (Ptr
, "OVMF", 5);
225 VbeInfo
->ProductNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
226 Printed
= AsciiSPrint (
228 sizeof VbeInfoFull
->Buffer
- (Ptr
- VbeInfoFull
->Buffer
),
234 VbeInfo
->ProductRevAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
235 CopyMem (Ptr
, mProductRevision
, sizeof mProductRevision
);
236 Ptr
+= sizeof mProductRevision
;
238 ASSERT (sizeof VbeInfoFull
->Buffer
>= Ptr
- VbeInfoFull
->Buffer
);
239 ZeroMem (Ptr
, sizeof VbeInfoFull
->Buffer
- (Ptr
- VbeInfoFull
->Buffer
));
242 // Fil in the VBE MODE INFO structure.
244 VbeModeInfo
= (VBE_MODE_INFO
*)(VbeInfoFull
+ 1);
247 // bit0: mode supported by present hardware configuration
248 // bit1: optional information available (must be =1 for VBE v1.2+)
249 // bit3: set if color, clear if monochrome
250 // bit4: set if graphics mode, clear if text mode
251 // bit5: mode is not VGA-compatible
252 // bit7: linear framebuffer mode supported
254 VbeModeInfo
->ModeAttr
= BIT7
| BIT5
| BIT4
| BIT3
| BIT1
| BIT0
;
258 // bit1: bit1: readable
261 VbeModeInfo
->WindowAAttr
= BIT2
| BIT1
| BIT0
;
263 VbeModeInfo
->WindowBAttr
= 0x00;
264 VbeModeInfo
->WindowGranularityKB
= 0x0040;
265 VbeModeInfo
->WindowSizeKB
= 0x0040;
266 VbeModeInfo
->WindowAStartSegment
= 0xA000;
267 VbeModeInfo
->WindowBStartSegment
= 0x0000;
268 VbeModeInfo
->WindowPositioningAddress
= 0x0000;
269 VbeModeInfo
->BytesPerScanLine
= 1024 * 4;
271 VbeModeInfo
->Width
= 1024;
272 VbeModeInfo
->Height
= 768;
273 VbeModeInfo
->CharCellWidth
= 8;
274 VbeModeInfo
->CharCellHeight
= 16;
275 VbeModeInfo
->NumPlanes
= 1;
276 VbeModeInfo
->BitsPerPixel
= 32;
277 VbeModeInfo
->NumBanks
= 1;
278 VbeModeInfo
->MemoryModel
= 6; // direct color
279 VbeModeInfo
->BankSizeKB
= 0;
280 VbeModeInfo
->NumImagePagesLessOne
= 0;
281 VbeModeInfo
->Vbe3
= 0x01;
283 VbeModeInfo
->RedMaskSize
= 8;
284 VbeModeInfo
->RedMaskPos
= 16;
285 VbeModeInfo
->GreenMaskSize
= 8;
286 VbeModeInfo
->GreenMaskPos
= 8;
287 VbeModeInfo
->BlueMaskSize
= 8;
288 VbeModeInfo
->BlueMaskPos
= 0;
289 VbeModeInfo
->ReservedMaskSize
= 8;
290 VbeModeInfo
->ReservedMaskPos
= 24;
293 // bit1: Bytes in reserved field may be used by application
295 VbeModeInfo
->DirectColorModeInfo
= BIT1
;
297 VbeModeInfo
->LfbAddress
= (UINT32
)FrameBufferBase
;
298 VbeModeInfo
->OffScreenAddress
= 0;
299 VbeModeInfo
->OffScreenSizeKB
= 0;
301 VbeModeInfo
->BytesPerScanLineLinear
= 1024 * 4;
302 VbeModeInfo
->NumImagesLessOneBanked
= 0;
303 VbeModeInfo
->NumImagesLessOneLinear
= 0;
304 VbeModeInfo
->RedMaskSizeLinear
= 8;
305 VbeModeInfo
->RedMaskPosLinear
= 16;
306 VbeModeInfo
->GreenMaskSizeLinear
= 8;
307 VbeModeInfo
->GreenMaskPosLinear
= 8;
308 VbeModeInfo
->BlueMaskSizeLinear
= 8;
309 VbeModeInfo
->BlueMaskPosLinear
= 0;
310 VbeModeInfo
->ReservedMaskSizeLinear
= 8;
311 VbeModeInfo
->ReservedMaskPosLinear
= 24;
312 VbeModeInfo
->MaxPixelClockHz
= 0;
314 ZeroMem (VbeModeInfo
->Reserved
, sizeof VbeModeInfo
->Reserved
);
317 // Clear Write Enable (bit1), keep Read Enable (bit0) set
319 PciWrite8 (Pam1Address
, (Pam1
& ~BIT1
) | BIT0
);
322 // Second, point the Int10h vector at the shim.
324 Int0x10
->Segment
= (UINT16
)((UINT32
)SegmentC
>> 4);
325 Int0x10
->Offset
= (UINT16
)((UINTN
)(VbeModeInfo
+ 1) - SegmentC
);
327 DEBUG ((DEBUG_INFO
, "%a: VBE shim installed\n", __FUNCTION__
));