]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/QemuVideoDxe/VbeShim.c
OvmfPkg/QemuVideoDxe: Bypass NULL pointer detection during VBE SHIM installing
[mirror_edk2.git] / OvmfPkg / QemuVideoDxe / VbeShim.c
CommitLineData
90803342
LE
1/** @file\r
2 Install a fake VGABIOS service handler (real mode Int10h) for the buggy\r
3 Windows 2008 R2 SP1 UEFI guest.\r
4\r
5 The handler is never meant to be directly executed by a VCPU; it's there for\r
6 the internal real mode emulator of Windows 2008 R2 SP1.\r
7\r
8 The code is based on Ralf Brown's Interrupt List:\r
9 <http://www.cs.cmu.edu/~ralf/files.html>\r
10 <http://www.ctyme.com/rbrown.htm>\r
11\r
12 Copyright (C) 2014, Red Hat, Inc.\r
13 Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>\r
14\r
15 This program and the accompanying materials are licensed and made available\r
16 under the terms and conditions of the BSD License which accompanies this\r
17 distribution. The full text of the license may be found at\r
18 http://opensource.org/licenses/bsd-license.php\r
19\r
20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
21 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
22**/\r
23\r
24#include <IndustryStandard/LegacyVgaBios.h>\r
25#include <Library/DebugLib.h>\r
26#include <Library/PciLib.h>\r
27#include <Library/PrintLib.h>\r
947f3737 28#include <OvmfPlatforms.h>\r
90803342
LE
29\r
30#include "Qemu.h"\r
31#include "VbeShim.h"\r
32\r
33#pragma pack (1)\r
34typedef struct {\r
35 UINT16 Offset;\r
36 UINT16 Segment;\r
37} IVT_ENTRY;\r
38#pragma pack ()\r
39\r
40//\r
41// This string is displayed by Windows 2008 R2 SP1 in the Screen Resolution,\r
42// Advanced Settings dialog. It should be short.\r
43//\r
44STATIC CONST CHAR8 mProductRevision[] = "OVMF Int10h (fake)";\r
45\r
46/**\r
47 Install the VBE Info and VBE Mode Info structures, and the VBE service\r
48 handler routine in the C segment. Point the real-mode Int10h interrupt vector\r
49 to the handler. The only advertised mode is 1024x768x32.\r
50\r
51 @param[in] CardName Name of the video card to be exposed in the\r
52 Product Name field of the VBE Info structure. The\r
53 parameter must originate from a\r
54 QEMU_VIDEO_CARD.Name field.\r
55 @param[in] FrameBufferBase Guest-physical base address of the video card's\r
56 frame buffer.\r
57**/\r
58VOID\r
59InstallVbeShim (\r
60 IN CONST CHAR16 *CardName,\r
61 IN EFI_PHYSICAL_ADDRESS FrameBufferBase\r
62 )\r
63{\r
64 EFI_PHYSICAL_ADDRESS Segment0, SegmentC, SegmentF;\r
65 UINTN Segment0Pages;\r
66 IVT_ENTRY *Int0x10;\r
ce461ae2 67 EFI_STATUS Segment0AllocationStatus;\r
947f3737 68 UINT16 HostBridgeDevId;\r
90803342
LE
69 UINTN Pam1Address;\r
70 UINT8 Pam1;\r
71 UINTN SegmentCPages;\r
72 VBE_INFO *VbeInfoFull;\r
73 VBE_INFO_BASE *VbeInfo;\r
74 UINT8 *Ptr;\r
75 UINTN Printed;\r
76 VBE_MODE_INFO *VbeModeInfo;\r
77\r
90f3922b
JW
78 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7)) == BIT0) {\r
79 DEBUG ((\r
80 DEBUG_WARN,\r
81 "%a: page 0 protected, not installing VBE shim\n",\r
82 __FUNCTION__\r
83 ));\r
84 DEBUG ((\r
85 DEBUG_WARN,\r
86 "%a: page 0 protection prevents Windows 7 from booting anyway\n",\r
87 __FUNCTION__\r
88 ));\r
89 return;\r
90 }\r
91\r
90803342
LE
92 Segment0 = 0x00000;\r
93 SegmentC = 0xC0000;\r
94 SegmentF = 0xF0000;\r
95\r
96 //\r
97 // Attempt to cover the real mode IVT with an allocation. This is a UEFI\r
98 // driver, hence the arch protocols have been installed previously. Among\r
99 // those, the CPU arch protocol has configured the IDT, so we can overwrite\r
100 // the IVT used in real mode.\r
101 //\r
102 // The allocation request may fail, eg. if LegacyBiosDxe has already run.\r
103 //\r
104 Segment0Pages = 1;\r
105 Int0x10 = (IVT_ENTRY *)(UINTN)Segment0 + 0x10;\r
ce461ae2
LE
106 Segment0AllocationStatus = gBS->AllocatePages (\r
107 AllocateAddress,\r
108 EfiBootServicesCode,\r
109 Segment0Pages,\r
110 &Segment0\r
111 );\r
112\r
113 if (EFI_ERROR (Segment0AllocationStatus)) {\r
90803342
LE
114 EFI_PHYSICAL_ADDRESS Handler;\r
115\r
116 //\r
117 // Check if a video BIOS handler has been installed previously -- we\r
118 // shouldn't override a real video BIOS with our shim, nor our own shim if\r
119 // it's already present.\r
120 //\r
121 Handler = (Int0x10->Segment << 4) + Int0x10->Offset;\r
122 if (Handler >= SegmentC && Handler < SegmentF) {\r
4dd8787a 123 DEBUG ((EFI_D_INFO, "%a: Video BIOS handler found at %04x:%04x\n",\r
90803342
LE
124 __FUNCTION__, Int0x10->Segment, Int0x10->Offset));\r
125 return;\r
126 }\r
127\r
128 //\r
129 // Otherwise we'll overwrite the Int10h vector, even though we may not own\r
130 // the page at zero.\r
131 //\r
ce461ae2
LE
132 DEBUG ((\r
133 DEBUG_INFO,\r
134 "%a: failed to allocate page at zero: %r\n",\r
135 __FUNCTION__,\r
136 Segment0AllocationStatus\r
137 ));\r
90803342
LE
138 } else {\r
139 //\r
140 // We managed to allocate the page at zero. SVN r14218 guarantees that it\r
141 // is NUL-filled.\r
142 //\r
143 ASSERT (Int0x10->Segment == 0x0000);\r
144 ASSERT (Int0x10->Offset == 0x0000);\r
145 }\r
146\r
147 //\r
148 // Put the shim in place first.\r
149 //\r
947f3737
LE
150 // Start by determining the address of the PAM1 register.\r
151 //\r
152 HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);\r
153 switch (HostBridgeDevId) {\r
154 case INTEL_82441_DEVICE_ID:\r
155 Pam1Address = PMC_REGISTER_PIIX4 (PIIX4_PAM1);\r
156 break;\r
157 case INTEL_Q35_MCH_DEVICE_ID:\r
158 Pam1Address = DRAMC_REGISTER_Q35 (MCH_PAM1);\r
159 break;\r
160 default:\r
161 DEBUG ((\r
162 DEBUG_ERROR,\r
163 "%a: unknown host bridge device ID: 0x%04x\n",\r
164 __FUNCTION__,\r
165 HostBridgeDevId\r
166 ));\r
167 ASSERT (FALSE);\r
168\r
169 if (!EFI_ERROR (Segment0AllocationStatus)) {\r
170 gBS->FreePages (Segment0, Segment0Pages);\r
171 }\r
172 return;\r
173 }\r
90803342
LE
174 //\r
175 // low nibble covers 0xC0000 to 0xC3FFF\r
176 // high nibble covers 0xC4000 to 0xC7FFF\r
177 // bit1 in each nibble is Write Enable\r
178 // bit0 in each nibble is Read Enable\r
179 //\r
180 Pam1 = PciRead8 (Pam1Address);\r
181 PciWrite8 (Pam1Address, Pam1 | (BIT1 | BIT0));\r
182\r
183 //\r
8c0b0b34 184 // We never added memory space during PEI or DXE for the C segment, so we\r
90803342
LE
185 // don't need to (and can't) allocate from there. Also, guest operating\r
186 // systems will see a hole in the UEFI memory map there.\r
187 //\r
188 SegmentCPages = 4;\r
189\r
190 ASSERT (sizeof mVbeShim <= EFI_PAGES_TO_SIZE (SegmentCPages));\r
191 CopyMem ((VOID *)(UINTN)SegmentC, mVbeShim, sizeof mVbeShim);\r
192\r
193 //\r
194 // Fill in the VBE INFO structure.\r
195 //\r
196 VbeInfoFull = (VBE_INFO *)(UINTN)SegmentC;\r
197 VbeInfo = &VbeInfoFull->Base;\r
198 Ptr = VbeInfoFull->Buffer;\r
199\r
200 CopyMem (VbeInfo->Signature, "VESA", 4);\r
201 VbeInfo->VesaVersion = 0x0300;\r
202\r
75f8e3aa 203 VbeInfo->OemNameAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
204 CopyMem (Ptr, "QEMU", 5);\r
205 Ptr += 5;\r
206\r
207 VbeInfo->Capabilities = BIT0; // DAC can be switched into 8-bit mode\r
208\r
75f8e3aa 209 VbeInfo->ModeListAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
210 *(UINT16*)Ptr = 0x00f1; // mode number\r
211 Ptr += 2;\r
212 *(UINT16*)Ptr = 0xFFFF; // mode list terminator\r
213 Ptr += 2;\r
214\r
215 VbeInfo->VideoMem64K = (UINT16)((1024 * 768 * 4 + 65535) / 65536);\r
216 VbeInfo->OemSoftwareVersion = 0x0000;\r
217\r
75f8e3aa 218 VbeInfo->VendorNameAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
219 CopyMem (Ptr, "OVMF", 5);\r
220 Ptr += 5;\r
221\r
75f8e3aa 222 VbeInfo->ProductNameAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
223 Printed = AsciiSPrint ((CHAR8 *)Ptr,\r
224 sizeof VbeInfoFull->Buffer - (Ptr - VbeInfoFull->Buffer), "%s",\r
225 CardName);\r
226 Ptr += Printed + 1;\r
227\r
75f8e3aa 228 VbeInfo->ProductRevAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
229 CopyMem (Ptr, mProductRevision, sizeof mProductRevision);\r
230 Ptr += sizeof mProductRevision;\r
231\r
232 ASSERT (sizeof VbeInfoFull->Buffer >= Ptr - VbeInfoFull->Buffer);\r
233 ZeroMem (Ptr, sizeof VbeInfoFull->Buffer - (Ptr - VbeInfoFull->Buffer));\r
234\r
235 //\r
236 // Fil in the VBE MODE INFO structure.\r
237 //\r
238 VbeModeInfo = (VBE_MODE_INFO *)(VbeInfoFull + 1);\r
239\r
240 //\r
241 // bit0: mode supported by present hardware configuration\r
242 // bit1: optional information available (must be =1 for VBE v1.2+)\r
243 // bit3: set if color, clear if monochrome\r
244 // bit4: set if graphics mode, clear if text mode\r
245 // bit5: mode is not VGA-compatible\r
246 // bit7: linear framebuffer mode supported\r
247 //\r
248 VbeModeInfo->ModeAttr = BIT7 | BIT5 | BIT4 | BIT3 | BIT1 | BIT0;\r
249\r
250 //\r
251 // bit0: exists\r
252 // bit1: bit1: readable\r
253 // bit2: writeable\r
254 //\r
255 VbeModeInfo->WindowAAttr = BIT2 | BIT1 | BIT0;\r
256\r
257 VbeModeInfo->WindowBAttr = 0x00;\r
258 VbeModeInfo->WindowGranularityKB = 0x0040;\r
259 VbeModeInfo->WindowSizeKB = 0x0040;\r
260 VbeModeInfo->WindowAStartSegment = 0xA000;\r
261 VbeModeInfo->WindowBStartSegment = 0x0000;\r
262 VbeModeInfo->WindowPositioningAddress = 0x0000;\r
263 VbeModeInfo->BytesPerScanLine = 1024 * 4;\r
264\r
265 VbeModeInfo->Width = 1024;\r
266 VbeModeInfo->Height = 768;\r
267 VbeModeInfo->CharCellWidth = 8;\r
268 VbeModeInfo->CharCellHeight = 16;\r
269 VbeModeInfo->NumPlanes = 1;\r
270 VbeModeInfo->BitsPerPixel = 32;\r
271 VbeModeInfo->NumBanks = 1;\r
272 VbeModeInfo->MemoryModel = 6; // direct color\r
273 VbeModeInfo->BankSizeKB = 0;\r
274 VbeModeInfo->NumImagePagesLessOne = 0;\r
275 VbeModeInfo->Vbe3 = 0x01;\r
276\r
277 VbeModeInfo->RedMaskSize = 8;\r
278 VbeModeInfo->RedMaskPos = 16;\r
279 VbeModeInfo->GreenMaskSize = 8;\r
280 VbeModeInfo->GreenMaskPos = 8;\r
281 VbeModeInfo->BlueMaskSize = 8;\r
282 VbeModeInfo->BlueMaskPos = 0;\r
283 VbeModeInfo->ReservedMaskSize = 8;\r
284 VbeModeInfo->ReservedMaskPos = 24;\r
285\r
286 //\r
287 // bit1: Bytes in reserved field may be used by application\r
288 //\r
289 VbeModeInfo->DirectColorModeInfo = BIT1;\r
290\r
291 VbeModeInfo->LfbAddress = (UINT32)FrameBufferBase;\r
292 VbeModeInfo->OffScreenAddress = 0;\r
293 VbeModeInfo->OffScreenSizeKB = 0;\r
294\r
295 VbeModeInfo->BytesPerScanLineLinear = 1024 * 4;\r
296 VbeModeInfo->NumImagesLessOneBanked = 0;\r
297 VbeModeInfo->NumImagesLessOneLinear = 0;\r
298 VbeModeInfo->RedMaskSizeLinear = 8;\r
299 VbeModeInfo->RedMaskPosLinear = 16;\r
300 VbeModeInfo->GreenMaskSizeLinear = 8;\r
301 VbeModeInfo->GreenMaskPosLinear = 8;\r
302 VbeModeInfo->BlueMaskSizeLinear = 8;\r
303 VbeModeInfo->BlueMaskPosLinear = 0;\r
304 VbeModeInfo->ReservedMaskSizeLinear = 8;\r
305 VbeModeInfo->ReservedMaskPosLinear = 24;\r
306 VbeModeInfo->MaxPixelClockHz = 0;\r
307\r
308 ZeroMem (VbeModeInfo->Reserved, sizeof VbeModeInfo->Reserved);\r
309\r
310 //\r
311 // Clear Write Enable (bit1), keep Read Enable (bit0) set\r
312 //\r
313 PciWrite8 (Pam1Address, (Pam1 & ~BIT1) | BIT0);\r
314\r
315 //\r
316 // Second, point the Int10h vector at the shim.\r
317 //\r
75f8e3aa 318 Int0x10->Segment = (UINT16) ((UINT32)SegmentC >> 4);\r
ea5396f3 319 Int0x10->Offset = (UINT16) ((UINTN) (VbeModeInfo + 1) - SegmentC);\r
90803342
LE
320\r
321 DEBUG ((EFI_D_INFO, "%a: VBE shim installed\n", __FUNCTION__));\r
322}\r