]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Bhyve/BhyveRfbDxe/VbeShim.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Bhyve / BhyveRfbDxe / VbeShim.c
CommitLineData
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
30typedef 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 40STATIC CONST CHAR8 mProductRevision[] = "2.0";\r
656419f9 41\r
ac0a286f
MK
42#define NUM_VBE_MODES 3\r
43STATIC 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 53STATIC 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
96VOID\r
97InstallVbeShim (\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