OvmfPkg/QemuVideoDxe/VbeShim: handle PAM1 register on Q35 correctly
[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
78 Segment0 = 0x00000;\r
79 SegmentC = 0xC0000;\r
80 SegmentF = 0xF0000;\r
81\r
82 //\r
83 // Attempt to cover the real mode IVT with an allocation. This is a UEFI\r
84 // driver, hence the arch protocols have been installed previously. Among\r
85 // those, the CPU arch protocol has configured the IDT, so we can overwrite\r
86 // the IVT used in real mode.\r
87 //\r
88 // The allocation request may fail, eg. if LegacyBiosDxe has already run.\r
89 //\r
90 Segment0Pages = 1;\r
91 Int0x10 = (IVT_ENTRY *)(UINTN)Segment0 + 0x10;\r
ce461ae2
LE
92 Segment0AllocationStatus = gBS->AllocatePages (\r
93 AllocateAddress,\r
94 EfiBootServicesCode,\r
95 Segment0Pages,\r
96 &Segment0\r
97 );\r
98\r
99 if (EFI_ERROR (Segment0AllocationStatus)) {\r
90803342
LE
100 EFI_PHYSICAL_ADDRESS Handler;\r
101\r
102 //\r
103 // Check if a video BIOS handler has been installed previously -- we\r
104 // shouldn't override a real video BIOS with our shim, nor our own shim if\r
105 // it's already present.\r
106 //\r
107 Handler = (Int0x10->Segment << 4) + Int0x10->Offset;\r
108 if (Handler >= SegmentC && Handler < SegmentF) {\r
4dd8787a 109 DEBUG ((EFI_D_INFO, "%a: Video BIOS handler found at %04x:%04x\n",\r
90803342
LE
110 __FUNCTION__, Int0x10->Segment, Int0x10->Offset));\r
111 return;\r
112 }\r
113\r
114 //\r
115 // Otherwise we'll overwrite the Int10h vector, even though we may not own\r
116 // the page at zero.\r
117 //\r
ce461ae2
LE
118 DEBUG ((\r
119 DEBUG_INFO,\r
120 "%a: failed to allocate page at zero: %r\n",\r
121 __FUNCTION__,\r
122 Segment0AllocationStatus\r
123 ));\r
90803342
LE
124 } else {\r
125 //\r
126 // We managed to allocate the page at zero. SVN r14218 guarantees that it\r
127 // is NUL-filled.\r
128 //\r
129 ASSERT (Int0x10->Segment == 0x0000);\r
130 ASSERT (Int0x10->Offset == 0x0000);\r
131 }\r
132\r
133 //\r
134 // Put the shim in place first.\r
135 //\r
947f3737
LE
136 // Start by determining the address of the PAM1 register.\r
137 //\r
138 HostBridgeDevId = PcdGet16 (PcdOvmfHostBridgePciDevId);\r
139 switch (HostBridgeDevId) {\r
140 case INTEL_82441_DEVICE_ID:\r
141 Pam1Address = PMC_REGISTER_PIIX4 (PIIX4_PAM1);\r
142 break;\r
143 case INTEL_Q35_MCH_DEVICE_ID:\r
144 Pam1Address = DRAMC_REGISTER_Q35 (MCH_PAM1);\r
145 break;\r
146 default:\r
147 DEBUG ((\r
148 DEBUG_ERROR,\r
149 "%a: unknown host bridge device ID: 0x%04x\n",\r
150 __FUNCTION__,\r
151 HostBridgeDevId\r
152 ));\r
153 ASSERT (FALSE);\r
154\r
155 if (!EFI_ERROR (Segment0AllocationStatus)) {\r
156 gBS->FreePages (Segment0, Segment0Pages);\r
157 }\r
158 return;\r
159 }\r
90803342
LE
160 //\r
161 // low nibble covers 0xC0000 to 0xC3FFF\r
162 // high nibble covers 0xC4000 to 0xC7FFF\r
163 // bit1 in each nibble is Write Enable\r
164 // bit0 in each nibble is Read Enable\r
165 //\r
166 Pam1 = PciRead8 (Pam1Address);\r
167 PciWrite8 (Pam1Address, Pam1 | (BIT1 | BIT0));\r
168\r
169 //\r
8c0b0b34 170 // We never added memory space during PEI or DXE for the C segment, so we\r
90803342
LE
171 // don't need to (and can't) allocate from there. Also, guest operating\r
172 // systems will see a hole in the UEFI memory map there.\r
173 //\r
174 SegmentCPages = 4;\r
175\r
176 ASSERT (sizeof mVbeShim <= EFI_PAGES_TO_SIZE (SegmentCPages));\r
177 CopyMem ((VOID *)(UINTN)SegmentC, mVbeShim, sizeof mVbeShim);\r
178\r
179 //\r
180 // Fill in the VBE INFO structure.\r
181 //\r
182 VbeInfoFull = (VBE_INFO *)(UINTN)SegmentC;\r
183 VbeInfo = &VbeInfoFull->Base;\r
184 Ptr = VbeInfoFull->Buffer;\r
185\r
186 CopyMem (VbeInfo->Signature, "VESA", 4);\r
187 VbeInfo->VesaVersion = 0x0300;\r
188\r
75f8e3aa 189 VbeInfo->OemNameAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
190 CopyMem (Ptr, "QEMU", 5);\r
191 Ptr += 5;\r
192\r
193 VbeInfo->Capabilities = BIT0; // DAC can be switched into 8-bit mode\r
194\r
75f8e3aa 195 VbeInfo->ModeListAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
196 *(UINT16*)Ptr = 0x00f1; // mode number\r
197 Ptr += 2;\r
198 *(UINT16*)Ptr = 0xFFFF; // mode list terminator\r
199 Ptr += 2;\r
200\r
201 VbeInfo->VideoMem64K = (UINT16)((1024 * 768 * 4 + 65535) / 65536);\r
202 VbeInfo->OemSoftwareVersion = 0x0000;\r
203\r
75f8e3aa 204 VbeInfo->VendorNameAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
205 CopyMem (Ptr, "OVMF", 5);\r
206 Ptr += 5;\r
207\r
75f8e3aa 208 VbeInfo->ProductNameAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
209 Printed = AsciiSPrint ((CHAR8 *)Ptr,\r
210 sizeof VbeInfoFull->Buffer - (Ptr - VbeInfoFull->Buffer), "%s",\r
211 CardName);\r
212 Ptr += Printed + 1;\r
213\r
75f8e3aa 214 VbeInfo->ProductRevAddress = (UINT32)SegmentC << 12 | (UINT16)(UINTN)Ptr;\r
90803342
LE
215 CopyMem (Ptr, mProductRevision, sizeof mProductRevision);\r
216 Ptr += sizeof mProductRevision;\r
217\r
218 ASSERT (sizeof VbeInfoFull->Buffer >= Ptr - VbeInfoFull->Buffer);\r
219 ZeroMem (Ptr, sizeof VbeInfoFull->Buffer - (Ptr - VbeInfoFull->Buffer));\r
220\r
221 //\r
222 // Fil in the VBE MODE INFO structure.\r
223 //\r
224 VbeModeInfo = (VBE_MODE_INFO *)(VbeInfoFull + 1);\r
225\r
226 //\r
227 // bit0: mode supported by present hardware configuration\r
228 // bit1: optional information available (must be =1 for VBE v1.2+)\r
229 // bit3: set if color, clear if monochrome\r
230 // bit4: set if graphics mode, clear if text mode\r
231 // bit5: mode is not VGA-compatible\r
232 // bit7: linear framebuffer mode supported\r
233 //\r
234 VbeModeInfo->ModeAttr = BIT7 | BIT5 | BIT4 | BIT3 | BIT1 | BIT0;\r
235\r
236 //\r
237 // bit0: exists\r
238 // bit1: bit1: readable\r
239 // bit2: writeable\r
240 //\r
241 VbeModeInfo->WindowAAttr = BIT2 | BIT1 | BIT0;\r
242\r
243 VbeModeInfo->WindowBAttr = 0x00;\r
244 VbeModeInfo->WindowGranularityKB = 0x0040;\r
245 VbeModeInfo->WindowSizeKB = 0x0040;\r
246 VbeModeInfo->WindowAStartSegment = 0xA000;\r
247 VbeModeInfo->WindowBStartSegment = 0x0000;\r
248 VbeModeInfo->WindowPositioningAddress = 0x0000;\r
249 VbeModeInfo->BytesPerScanLine = 1024 * 4;\r
250\r
251 VbeModeInfo->Width = 1024;\r
252 VbeModeInfo->Height = 768;\r
253 VbeModeInfo->CharCellWidth = 8;\r
254 VbeModeInfo->CharCellHeight = 16;\r
255 VbeModeInfo->NumPlanes = 1;\r
256 VbeModeInfo->BitsPerPixel = 32;\r
257 VbeModeInfo->NumBanks = 1;\r
258 VbeModeInfo->MemoryModel = 6; // direct color\r
259 VbeModeInfo->BankSizeKB = 0;\r
260 VbeModeInfo->NumImagePagesLessOne = 0;\r
261 VbeModeInfo->Vbe3 = 0x01;\r
262\r
263 VbeModeInfo->RedMaskSize = 8;\r
264 VbeModeInfo->RedMaskPos = 16;\r
265 VbeModeInfo->GreenMaskSize = 8;\r
266 VbeModeInfo->GreenMaskPos = 8;\r
267 VbeModeInfo->BlueMaskSize = 8;\r
268 VbeModeInfo->BlueMaskPos = 0;\r
269 VbeModeInfo->ReservedMaskSize = 8;\r
270 VbeModeInfo->ReservedMaskPos = 24;\r
271\r
272 //\r
273 // bit1: Bytes in reserved field may be used by application\r
274 //\r
275 VbeModeInfo->DirectColorModeInfo = BIT1;\r
276\r
277 VbeModeInfo->LfbAddress = (UINT32)FrameBufferBase;\r
278 VbeModeInfo->OffScreenAddress = 0;\r
279 VbeModeInfo->OffScreenSizeKB = 0;\r
280\r
281 VbeModeInfo->BytesPerScanLineLinear = 1024 * 4;\r
282 VbeModeInfo->NumImagesLessOneBanked = 0;\r
283 VbeModeInfo->NumImagesLessOneLinear = 0;\r
284 VbeModeInfo->RedMaskSizeLinear = 8;\r
285 VbeModeInfo->RedMaskPosLinear = 16;\r
286 VbeModeInfo->GreenMaskSizeLinear = 8;\r
287 VbeModeInfo->GreenMaskPosLinear = 8;\r
288 VbeModeInfo->BlueMaskSizeLinear = 8;\r
289 VbeModeInfo->BlueMaskPosLinear = 0;\r
290 VbeModeInfo->ReservedMaskSizeLinear = 8;\r
291 VbeModeInfo->ReservedMaskPosLinear = 24;\r
292 VbeModeInfo->MaxPixelClockHz = 0;\r
293\r
294 ZeroMem (VbeModeInfo->Reserved, sizeof VbeModeInfo->Reserved);\r
295\r
296 //\r
297 // Clear Write Enable (bit1), keep Read Enable (bit0) set\r
298 //\r
299 PciWrite8 (Pam1Address, (Pam1 & ~BIT1) | BIT0);\r
300\r
301 //\r
302 // Second, point the Int10h vector at the shim.\r
303 //\r
75f8e3aa 304 Int0x10->Segment = (UINT16) ((UINT32)SegmentC >> 4);\r
ea5396f3 305 Int0x10->Offset = (UINT16) ((UINTN) (VbeModeInfo + 1) - SegmentC);\r
90803342
LE
306\r
307 DEBUG ((EFI_D_INFO, "%a: VBE shim installed\n", __FUNCTION__));\r
308}\r