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