]>
Commit | Line | Data |
---|---|---|
656419f9 RC |
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) 2020, Rebecca Cran <rebecca@bsdio.com>\r | |
13 | Copyright (C) 2015, Nahanni Systems, Inc.\r | |
14 | Copyright (C) 2014, Red Hat, Inc.\r | |
15 | Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>\r | |
16 | \r | |
17 | SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
18 | \r | |
19 | **/\r | |
20 | \r | |
21 | #include <IndustryStandard/LegacyVgaBios.h>\r | |
22 | #include <Library/DebugLib.h>\r | |
23 | #include <Library/PciLib.h>\r | |
24 | #include <Library/PrintLib.h>\r | |
25 | \r | |
26 | #include "Gop.h"\r | |
27 | #include "VbeShim.h"\r | |
28 | \r | |
29 | #pragma pack (1)\r | |
30 | typedef struct {\r | |
ac0a286f MK |
31 | UINT16 Offset;\r |
32 | UINT16 Segment;\r | |
656419f9 RC |
33 | } IVT_ENTRY;\r |
34 | #pragma pack ()\r | |
35 | \r | |
36 | //\r | |
37 | // This string is displayed by Windows 2008 R2 SP1 in the Screen Resolution,\r | |
38 | // Advanced Settings dialog. It should be short.\r | |
39 | //\r | |
ac0a286f | 40 | STATIC CONST CHAR8 mProductRevision[] = "2.0";\r |
656419f9 | 41 | \r |
ac0a286f MK |
42 | #define NUM_VBE_MODES 3\r |
43 | STATIC CONST UINT16 vbeModeIds[] = {\r | |
656419f9 RC |
44 | 0x13f, // 640x480x32\r |
45 | 0x140, // 800x600x32\r | |
46 | 0x141 // 1024x768x32\r | |
47 | };\r | |
48 | \r | |
49 | // Modes can be toggled with bit-0\r | |
ac0a286f MK |
50 | #define VBE_MODE_ENABLED 0x00BB\r |
51 | #define VBE_MODE_DISABLED 0x00BA\r | |
656419f9 | 52 | \r |
ac0a286f | 53 | STATIC VBE2_MODE_INFO vbeModes[] = {\r |
656419f9 | 54 | { // 0x13f 640x480x32\r |
656419f9 | 55 | // ModeAttr - BytesPerScanLine\r |
ac0a286f | 56 | VBE_MODE_DISABLED, 0x07, 0x00, 0x40, 0x40, 0xA000, 0x00, 0x0000, 640*4,\r |
656419f9 | 57 | // Width, Height..., Vbe3\r |
ac0a286f | 58 | 640, 480, 16, 8, 1, 32, 1, 0x06, 0, 0, 1,\r |
656419f9 | 59 | // Masks\r |
ac0a286f | 60 | 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x08, 0x18, 0x00,\r |
656419f9 | 61 | // Framebuffer\r |
ac0a286f | 62 | 0xdeadbeef, 0x0000, 0x0000\r |
656419f9 RC |
63 | },\r |
64 | { // 0x140 800x600x32\r | |
656419f9 | 65 | // ModeAttr - BytesPerScanLine\r |
ac0a286f | 66 | VBE_MODE_DISABLED, 0x07, 0x00, 0x40, 0x40, 0xA000, 0x00, 0x0000, 800*4,\r |
656419f9 | 67 | // Width, Height..., Vbe3\r |
ac0a286f | 68 | 800, 600, 16, 8, 1, 32, 1, 0x06, 0, 0, 1,\r |
656419f9 | 69 | // Masks\r |
ac0a286f | 70 | 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x08, 0x18, 0x00,\r |
656419f9 | 71 | // Framebuffer\r |
ac0a286f | 72 | 0xdeadbeef, 0x0000, 0x0000\r |
656419f9 RC |
73 | },\r |
74 | { // 0x141 1024x768x32\r | |
656419f9 | 75 | // ModeAttr - BytesPerScanLine\r |
ac0a286f | 76 | VBE_MODE_ENABLED, 0x07, 0x00, 0x40, 0x40, 0xA000, 0x00, 0x0000, 1024*4,\r |
656419f9 | 77 | // Width, Height..., Vbe3\r |
ac0a286f | 78 | 1024, 768, 16, 8, 1, 32, 1, 0x06, 0, 0, 1,\r |
656419f9 | 79 | // Masks\r |
ac0a286f | 80 | 0x08, 0x10, 0x08, 0x08, 0x08, 0x00, 0x08, 0x18, 0x00,\r |
656419f9 | 81 | // Framebuffer\r |
ac0a286f | 82 | 0xdeadbeef, 0x0000, 0x0000\r |
656419f9 RC |
83 | }\r |
84 | };\r | |
85 | \r | |
86 | /**\r | |
87 | Install the VBE Info and VBE Mode Info structures, and the VBE service\r | |
88 | handler routine in the C segment. Point the real-mode Int10h interrupt vector\r | |
89 | to the handler. The only advertised mode is 1024x768x32.\r | |
90 | \r | |
91 | @param[in] CardName Name of the video card to be exposed in the\r | |
92 | Product Name field of the VBE Info structure.\r | |
93 | @param[in] FrameBufferBase Guest-physical base address of the video card's\r | |
94 | frame buffer.\r | |
95 | **/\r | |
96 | VOID\r | |
97 | InstallVbeShim (\r | |
ac0a286f MK |
98 | IN CONST CHAR16 *CardName,\r |
99 | IN EFI_PHYSICAL_ADDRESS FrameBufferBase\r | |
656419f9 RC |
100 | )\r |
101 | {\r | |
ac0a286f MK |
102 | EFI_PHYSICAL_ADDRESS Segment0, SegmentC, SegmentF;\r |
103 | UINTN Segment0Pages;\r | |
104 | IVT_ENTRY *Int0x10;\r | |
105 | EFI_STATUS Status;\r | |
106 | UINTN Pam1Address;\r | |
107 | UINT8 Pam1;\r | |
108 | UINTN SegmentCPages;\r | |
109 | VBE_INFO *VbeInfoFull;\r | |
110 | VBE_INFO_BASE *VbeInfo;\r | |
111 | UINT8 *Ptr;\r | |
112 | UINTN Printed;\r | |
113 | VBE_MODE_INFO *VbeModeInfo;\r | |
114 | UINTN i;\r | |
656419f9 RC |
115 | \r |
116 | Segment0 = 0x00000;\r | |
117 | SegmentC = 0xC0000;\r | |
118 | SegmentF = 0xF0000;\r | |
119 | \r | |
120 | //\r | |
121 | // Attempt to cover the real mode IVT with an allocation. This is a UEFI\r | |
122 | // driver, hence the arch protocols have been installed previously. Among\r | |
123 | // those, the CPU arch protocol has configured the IDT, so we can overwrite\r | |
124 | // the IVT used in real mode.\r | |
125 | //\r | |
126 | // The allocation request may fail, eg. if LegacyBiosDxe has already run.\r | |
127 | //\r | |
128 | Segment0Pages = 1;\r | |
129 | Int0x10 = (IVT_ENTRY *)(UINTN)Segment0 + 0x10;\r | |
ac0a286f MK |
130 | Status = gBS->AllocatePages (\r |
131 | AllocateAddress,\r | |
132 | EfiBootServicesCode,\r | |
133 | Segment0Pages,\r | |
134 | &Segment0\r | |
135 | );\r | |
656419f9 RC |
136 | \r |
137 | if (EFI_ERROR (Status)) {\r | |
ac0a286f | 138 | EFI_PHYSICAL_ADDRESS Handler;\r |
656419f9 RC |
139 | \r |
140 | //\r | |
141 | // Check if a video BIOS handler has been installed previously -- we\r | |
142 | // shouldn't override a real video BIOS with our shim, nor our own shim if\r | |
143 | // it's already present.\r | |
144 | //\r | |
145 | Handler = (Int0x10->Segment << 4) + Int0x10->Offset;\r | |
ac0a286f MK |
146 | if ((Handler >= SegmentC) && (Handler < SegmentF)) {\r |
147 | DEBUG ((\r | |
148 | DEBUG_VERBOSE,\r | |
149 | "%a: Video BIOS handler found at %04x:%04x\n",\r | |
150 | __FUNCTION__,\r | |
151 | Int0x10->Segment,\r | |
152 | Int0x10->Offset\r | |
153 | ));\r | |
656419f9 RC |
154 | return;\r |
155 | }\r | |
156 | \r | |
157 | //\r | |
158 | // Otherwise we'll overwrite the Int10h vector, even though we may not own\r | |
159 | // the page at zero.\r | |
160 | //\r | |
ac0a286f MK |
161 | DEBUG ((\r |
162 | DEBUG_VERBOSE,\r | |
163 | "%a: failed to allocate page at zero: %r\n",\r | |
164 | __FUNCTION__,\r | |
165 | Status\r | |
166 | ));\r | |
656419f9 RC |
167 | } else {\r |
168 | //\r | |
169 | // We managed to allocate the page at zero. SVN r14218 guarantees that it\r | |
170 | // is NUL-filled.\r | |
171 | //\r | |
172 | ASSERT (Int0x10->Segment == 0x0000);\r | |
173 | ASSERT (Int0x10->Offset == 0x0000);\r | |
174 | }\r | |
175 | \r | |
176 | //\r | |
177 | // Put the shim in place first.\r | |
178 | //\r | |
179 | Pam1Address = PCI_LIB_ADDRESS (0, 0, 0, 0x5A);\r | |
180 | //\r | |
181 | // low nibble covers 0xC0000 to 0xC3FFF\r | |
182 | // high nibble covers 0xC4000 to 0xC7FFF\r | |
183 | // bit1 in each nibble is Write Enable\r | |
184 | // bit0 in each nibble is Read Enable\r | |
185 | //\r | |
186 | Pam1 = PciRead8 (Pam1Address);\r | |
187 | PciWrite8 (Pam1Address, Pam1 | (BIT1 | BIT0));\r | |
188 | \r | |
189 | //\r | |
190 | // We never added memory space durig PEI or DXE for the C segment, so we\r | |
191 | // don't need to (and can't) allocate from there. Also, guest operating\r | |
192 | // systems will see a hole in the UEFI memory map there.\r | |
193 | //\r | |
194 | SegmentCPages = 4;\r | |
195 | \r | |
196 | ASSERT (sizeof mVbeShim <= EFI_PAGES_TO_SIZE (SegmentCPages));\r | |
197 | CopyMem ((VOID *)(UINTN)SegmentC, mVbeShim, sizeof mVbeShim);\r | |
198 | \r | |
199 | //\r | |
200 | // Fill in the VBE INFO structure.\r | |
201 | //\r | |
202 | VbeInfoFull = (VBE_INFO *)(UINTN)SegmentC;\r | |
203 | VbeInfo = &VbeInfoFull->Base;\r | |
204 | Ptr = VbeInfoFull->Buffer;\r | |
205 | \r | |
206 | CopyMem (VbeInfo->Signature, "VESA", 4);\r | |
207 | VbeInfo->VesaVersion = 0x0200;\r | |
208 | \r | |
209 | VbeInfo->OemNameAddress = (UINT32)SegmentC << 12 | (UINT16)((UINTN)Ptr-SegmentC);\r | |
210 | CopyMem (Ptr, "FBSD", 5);\r | |
211 | Ptr += 5;\r | |
212 | \r | |
213 | VbeInfo->Capabilities = BIT1 | BIT0; // DAC can be switched into 8-bit mode\r | |
214 | \r | |
215 | VbeInfo->ModeListAddress = (UINT32)SegmentC << 12 | (UINT16)((UINTN)Ptr-SegmentC);\r | |
ac0a286f MK |
216 | for (i = 0; i < NUM_VBE_MODES; i++) {\r |
217 | *(UINT16 *)Ptr = vbeModeIds[i]; // mode number\r | |
218 | Ptr += 2;\r | |
656419f9 | 219 | }\r |
656419f9 | 220 | \r |
ac0a286f MK |
221 | *(UINT16 *)Ptr = 0xFFFF; // mode list terminator\r |
222 | Ptr += 2;\r | |
223 | \r | |
224 | VbeInfo->VideoMem64K = (UINT16)((1024 * 768 * 4 + 65535) / 65536);\r | |
656419f9 RC |
225 | VbeInfo->OemSoftwareVersion = 0x0200;\r |
226 | \r | |
227 | VbeInfo->VendorNameAddress = (UINT32)SegmentC << 12 | (UINT16)((UINTN)Ptr-SegmentC);\r | |
228 | CopyMem (Ptr, "FBSD", 5);\r | |
229 | Ptr += 5;\r | |
230 | \r | |
231 | VbeInfo->ProductNameAddress = (UINT32)SegmentC << 12 | (UINT16)((UINTN)Ptr-SegmentC);\r | |
ac0a286f MK |
232 | Printed = AsciiSPrint (\r |
233 | (CHAR8 *)Ptr,\r | |
234 | sizeof VbeInfoFull->Buffer - (Ptr - VbeInfoFull->Buffer),\r | |
235 | "%s",\r | |
236 | CardName\r | |
237 | );\r | |
656419f9 RC |
238 | Ptr += Printed + 1;\r |
239 | \r | |
240 | VbeInfo->ProductRevAddress = (UINT32)SegmentC << 12 | (UINT16)((UINTN)Ptr-SegmentC);\r | |
241 | CopyMem (Ptr, mProductRevision, sizeof mProductRevision);\r | |
242 | Ptr += sizeof mProductRevision;\r | |
243 | \r | |
244 | ASSERT (sizeof VbeInfoFull->Buffer >= Ptr - VbeInfoFull->Buffer);\r | |
245 | ZeroMem (Ptr, sizeof VbeInfoFull->Buffer - (Ptr - VbeInfoFull->Buffer));\r | |
246 | \r | |
247 | //\r | |
248 | // Fill in the VBE MODE INFO structure list\r | |
249 | //\r | |
250 | VbeModeInfo = (VBE_MODE_INFO *)(VbeInfoFull + 1);\r | |
ac0a286f | 251 | Ptr = (UINT8 *)VbeModeInfo;\r |
656419f9 RC |
252 | for (i = 0; i < NUM_VBE_MODES; i++) {\r |
253 | vbeModes[i].LfbAddress = (UINT32)FrameBufferBase;\r | |
254 | CopyMem (Ptr, &vbeModes[i], 0x32);\r | |
255 | Ptr += 0x32;\r | |
256 | }\r | |
257 | \r | |
258 | ZeroMem (Ptr, 56); // Clear remaining bytes\r | |
259 | \r | |
260 | //\r | |
261 | // Clear Write Enable (bit1), keep Read Enable (bit0) set\r | |
262 | //\r | |
263 | PciWrite8 (Pam1Address, (Pam1 & ~BIT1) | BIT0);\r | |
264 | \r | |
265 | //\r | |
266 | // Second, point the Int10h vector at the shim.\r | |
267 | //\r | |
ac0a286f MK |
268 | Int0x10->Segment = (UINT16)((UINT32)SegmentC >> 4);\r |
269 | Int0x10->Offset = (UINT16)((UINTN)(VbeModeInfo + 1) - SegmentC);\r | |
270 | \r | |
271 | DEBUG ((\r | |
272 | DEBUG_INFO,\r | |
273 | "%a: VBE shim installed to %x:%x\n",\r | |
274 | __FUNCTION__,\r | |
275 | Int0x10->Segment,\r | |
276 | Int0x10->Offset\r | |
277 | ));\r | |
656419f9 | 278 | }\r |