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
) {
117 DEBUG ((EFI_D_INFO
, "%a: Video BIOS handler found at %04x:%04x\n",
118 __FUNCTION__
, Int0x10
->Segment
, Int0x10
->Offset
));
123 // Otherwise we'll overwrite the Int10h vector, even though we may not own
128 "%a: failed to allocate page at zero: %r\n",
130 Segment0AllocationStatus
134 // We managed to allocate the page at zero. SVN r14218 guarantees that it
137 ASSERT (Int0x10
->Segment
== 0x0000);
138 ASSERT (Int0x10
->Offset
== 0x0000);
142 // Put the shim in place first.
144 // Start by determining the address of the PAM1 register.
146 HostBridgeDevId
= PcdGet16 (PcdOvmfHostBridgePciDevId
);
147 switch (HostBridgeDevId
) {
148 case INTEL_82441_DEVICE_ID
:
149 Pam1Address
= PMC_REGISTER_PIIX4 (PIIX4_PAM1
);
151 case INTEL_Q35_MCH_DEVICE_ID
:
152 Pam1Address
= DRAMC_REGISTER_Q35 (MCH_PAM1
);
157 "%a: unknown host bridge device ID: 0x%04x\n",
163 if (!EFI_ERROR (Segment0AllocationStatus
)) {
164 gBS
->FreePages (Segment0
, Segment0Pages
);
169 // low nibble covers 0xC0000 to 0xC3FFF
170 // high nibble covers 0xC4000 to 0xC7FFF
171 // bit1 in each nibble is Write Enable
172 // bit0 in each nibble is Read Enable
174 Pam1
= PciRead8 (Pam1Address
);
175 PciWrite8 (Pam1Address
, Pam1
| (BIT1
| BIT0
));
178 // We never added memory space during PEI or DXE for the C segment, so we
179 // don't need to (and can't) allocate from there. Also, guest operating
180 // systems will see a hole in the UEFI memory map there.
184 ASSERT (sizeof mVbeShim
<= EFI_PAGES_TO_SIZE (SegmentCPages
));
185 CopyMem ((VOID
*)(UINTN
)SegmentC
, mVbeShim
, sizeof mVbeShim
);
188 // Fill in the VBE INFO structure.
190 VbeInfoFull
= (VBE_INFO
*)(UINTN
)SegmentC
;
191 VbeInfo
= &VbeInfoFull
->Base
;
192 Ptr
= VbeInfoFull
->Buffer
;
194 CopyMem (VbeInfo
->Signature
, "VESA", 4);
195 VbeInfo
->VesaVersion
= 0x0300;
197 VbeInfo
->OemNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
198 CopyMem (Ptr
, "QEMU", 5);
201 VbeInfo
->Capabilities
= BIT0
; // DAC can be switched into 8-bit mode
203 VbeInfo
->ModeListAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
204 *(UINT16
*)Ptr
= 0x00f1; // mode number
206 *(UINT16
*)Ptr
= 0xFFFF; // mode list terminator
209 VbeInfo
->VideoMem64K
= (UINT16
)((1024 * 768 * 4 + 65535) / 65536);
210 VbeInfo
->OemSoftwareVersion
= 0x0000;
212 VbeInfo
->VendorNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
213 CopyMem (Ptr
, "OVMF", 5);
216 VbeInfo
->ProductNameAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
217 Printed
= AsciiSPrint ((CHAR8
*)Ptr
,
218 sizeof VbeInfoFull
->Buffer
- (Ptr
- VbeInfoFull
->Buffer
), "%s",
222 VbeInfo
->ProductRevAddress
= (UINT32
)SegmentC
<< 12 | (UINT16
)(UINTN
)Ptr
;
223 CopyMem (Ptr
, mProductRevision
, sizeof mProductRevision
);
224 Ptr
+= sizeof mProductRevision
;
226 ASSERT (sizeof VbeInfoFull
->Buffer
>= Ptr
- VbeInfoFull
->Buffer
);
227 ZeroMem (Ptr
, sizeof VbeInfoFull
->Buffer
- (Ptr
- VbeInfoFull
->Buffer
));
230 // Fil in the VBE MODE INFO structure.
232 VbeModeInfo
= (VBE_MODE_INFO
*)(VbeInfoFull
+ 1);
235 // bit0: mode supported by present hardware configuration
236 // bit1: optional information available (must be =1 for VBE v1.2+)
237 // bit3: set if color, clear if monochrome
238 // bit4: set if graphics mode, clear if text mode
239 // bit5: mode is not VGA-compatible
240 // bit7: linear framebuffer mode supported
242 VbeModeInfo
->ModeAttr
= BIT7
| BIT5
| BIT4
| BIT3
| BIT1
| BIT0
;
246 // bit1: bit1: readable
249 VbeModeInfo
->WindowAAttr
= BIT2
| BIT1
| BIT0
;
251 VbeModeInfo
->WindowBAttr
= 0x00;
252 VbeModeInfo
->WindowGranularityKB
= 0x0040;
253 VbeModeInfo
->WindowSizeKB
= 0x0040;
254 VbeModeInfo
->WindowAStartSegment
= 0xA000;
255 VbeModeInfo
->WindowBStartSegment
= 0x0000;
256 VbeModeInfo
->WindowPositioningAddress
= 0x0000;
257 VbeModeInfo
->BytesPerScanLine
= 1024 * 4;
259 VbeModeInfo
->Width
= 1024;
260 VbeModeInfo
->Height
= 768;
261 VbeModeInfo
->CharCellWidth
= 8;
262 VbeModeInfo
->CharCellHeight
= 16;
263 VbeModeInfo
->NumPlanes
= 1;
264 VbeModeInfo
->BitsPerPixel
= 32;
265 VbeModeInfo
->NumBanks
= 1;
266 VbeModeInfo
->MemoryModel
= 6; // direct color
267 VbeModeInfo
->BankSizeKB
= 0;
268 VbeModeInfo
->NumImagePagesLessOne
= 0;
269 VbeModeInfo
->Vbe3
= 0x01;
271 VbeModeInfo
->RedMaskSize
= 8;
272 VbeModeInfo
->RedMaskPos
= 16;
273 VbeModeInfo
->GreenMaskSize
= 8;
274 VbeModeInfo
->GreenMaskPos
= 8;
275 VbeModeInfo
->BlueMaskSize
= 8;
276 VbeModeInfo
->BlueMaskPos
= 0;
277 VbeModeInfo
->ReservedMaskSize
= 8;
278 VbeModeInfo
->ReservedMaskPos
= 24;
281 // bit1: Bytes in reserved field may be used by application
283 VbeModeInfo
->DirectColorModeInfo
= BIT1
;
285 VbeModeInfo
->LfbAddress
= (UINT32
)FrameBufferBase
;
286 VbeModeInfo
->OffScreenAddress
= 0;
287 VbeModeInfo
->OffScreenSizeKB
= 0;
289 VbeModeInfo
->BytesPerScanLineLinear
= 1024 * 4;
290 VbeModeInfo
->NumImagesLessOneBanked
= 0;
291 VbeModeInfo
->NumImagesLessOneLinear
= 0;
292 VbeModeInfo
->RedMaskSizeLinear
= 8;
293 VbeModeInfo
->RedMaskPosLinear
= 16;
294 VbeModeInfo
->GreenMaskSizeLinear
= 8;
295 VbeModeInfo
->GreenMaskPosLinear
= 8;
296 VbeModeInfo
->BlueMaskSizeLinear
= 8;
297 VbeModeInfo
->BlueMaskPosLinear
= 0;
298 VbeModeInfo
->ReservedMaskSizeLinear
= 8;
299 VbeModeInfo
->ReservedMaskPosLinear
= 24;
300 VbeModeInfo
->MaxPixelClockHz
= 0;
302 ZeroMem (VbeModeInfo
->Reserved
, sizeof VbeModeInfo
->Reserved
);
305 // Clear Write Enable (bit1), keep Read Enable (bit0) set
307 PciWrite8 (Pam1Address
, (Pam1
& ~BIT1
) | BIT0
);
310 // Second, point the Int10h vector at the shim.
312 Int0x10
->Segment
= (UINT16
) ((UINT32
)SegmentC
>> 4);
313 Int0x10
->Offset
= (UINT16
) ((UINTN
) (VbeModeInfo
+ 1) - SegmentC
);
315 DEBUG ((EFI_D_INFO
, "%a: VBE shim installed\n", __FUNCTION__
));