]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/QemuRamfbDxe/QemuRamfb.c
0d49d8bbab0384267ebce83c4d66feb29a8e0727
[mirror_edk2.git] / OvmfPkg / QemuRamfbDxe / QemuRamfb.c
1 /** @file
2 This driver is a implementation of the Graphics Output Protocol
3 for the QEMU ramfb device.
4
5 Copyright (c) 2018, Red Hat Inc.
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include <Protocol/GraphicsOutput.h>
12
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/DebugLib.h>
16 #include <Library/DevicePathLib.h>
17 #include <Library/FrameBufferBltLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/QemuFwCfgLib.h>
21
22 #include <Guid/QemuRamfb.h>
23
24 #define RAMFB_FORMAT 0x34325258 /* DRM_FORMAT_XRGB8888 */
25 #define RAMFB_BPP 4
26
27 #pragma pack (1)
28 typedef struct RAMFB_CONFIG {
29 UINT64 Address;
30 UINT32 FourCC;
31 UINT32 Flags;
32 UINT32 Width;
33 UINT32 Height;
34 UINT32 Stride;
35 } RAMFB_CONFIG;
36 #pragma pack ()
37
38 STATIC EFI_HANDLE mRamfbHandle;
39 STATIC EFI_HANDLE mGopHandle;
40 STATIC FRAME_BUFFER_CONFIGURE *mQemuRamfbFrameBufferBltConfigure;
41 STATIC UINTN mQemuRamfbFrameBufferBltConfigureSize;
42 STATIC FIRMWARE_CONFIG_ITEM mRamfbFwCfgItem;
43
44 STATIC EFI_GRAPHICS_OUTPUT_MODE_INFORMATION mQemuRamfbModeInfo[] = {
45 {
46 0, // Version
47 640, // HorizontalResolution
48 480, // VerticalResolution
49 },{
50 0, // Version
51 800, // HorizontalResolution
52 600, // VerticalResolution
53 },{
54 0, // Version
55 1024, // HorizontalResolution
56 768, // VerticalResolution
57 }
58 };
59
60 STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE mQemuRamfbMode = {
61 ARRAY_SIZE (mQemuRamfbModeInfo), // MaxMode
62 0, // Mode
63 mQemuRamfbModeInfo, // Info
64 sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION), // SizeOfInfo
65 };
66
67 STATIC
68 EFI_STATUS
69 EFIAPI
70 QemuRamfbGraphicsOutputQueryMode (
71 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
72 IN UINT32 ModeNumber,
73 OUT UINTN *SizeOfInfo,
74 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
75 )
76 {
77 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo;
78
79 if (Info == NULL || SizeOfInfo == NULL ||
80 ModeNumber >= mQemuRamfbMode.MaxMode) {
81 return EFI_INVALID_PARAMETER;
82 }
83 ModeInfo = &mQemuRamfbModeInfo[ModeNumber];
84
85 *Info = AllocateCopyPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
86 ModeInfo);
87 if (*Info == NULL) {
88 return EFI_OUT_OF_RESOURCES;
89 }
90 *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
91
92 return EFI_SUCCESS;
93 }
94
95 STATIC
96 EFI_STATUS
97 EFIAPI
98 QemuRamfbGraphicsOutputSetMode (
99 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
100 IN UINT32 ModeNumber
101 )
102 {
103 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *ModeInfo;
104 RAMFB_CONFIG Config;
105 EFI_GRAPHICS_OUTPUT_BLT_PIXEL Black;
106 RETURN_STATUS Status;
107
108 if (ModeNumber >= mQemuRamfbMode.MaxMode) {
109 return EFI_UNSUPPORTED;
110 }
111 ModeInfo = &mQemuRamfbModeInfo[ModeNumber];
112
113 DEBUG ((DEBUG_INFO, "Ramfb: SetMode %u (%ux%u)\n", ModeNumber,
114 ModeInfo->HorizontalResolution, ModeInfo->VerticalResolution));
115
116 Config.Address = SwapBytes64 (mQemuRamfbMode.FrameBufferBase);
117 Config.FourCC = SwapBytes32 (RAMFB_FORMAT);
118 Config.Flags = SwapBytes32 (0);
119 Config.Width = SwapBytes32 (ModeInfo->HorizontalResolution);
120 Config.Height = SwapBytes32 (ModeInfo->VerticalResolution);
121 Config.Stride = SwapBytes32 (ModeInfo->HorizontalResolution * RAMFB_BPP);
122
123 Status = FrameBufferBltConfigure (
124 (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase,
125 ModeInfo,
126 mQemuRamfbFrameBufferBltConfigure,
127 &mQemuRamfbFrameBufferBltConfigureSize
128 );
129
130 if (Status == RETURN_BUFFER_TOO_SMALL) {
131 if (mQemuRamfbFrameBufferBltConfigure != NULL) {
132 FreePool (mQemuRamfbFrameBufferBltConfigure);
133 }
134 mQemuRamfbFrameBufferBltConfigure =
135 AllocatePool (mQemuRamfbFrameBufferBltConfigureSize);
136 if (mQemuRamfbFrameBufferBltConfigure == NULL) {
137 mQemuRamfbFrameBufferBltConfigureSize = 0;
138 return EFI_OUT_OF_RESOURCES;
139 }
140
141 Status = FrameBufferBltConfigure (
142 (VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase,
143 ModeInfo,
144 mQemuRamfbFrameBufferBltConfigure,
145 &mQemuRamfbFrameBufferBltConfigureSize
146 );
147 }
148 if (RETURN_ERROR (Status)) {
149 ASSERT (Status == RETURN_UNSUPPORTED);
150 return Status;
151 }
152
153 mQemuRamfbMode.Mode = ModeNumber;
154 mQemuRamfbMode.Info = ModeInfo;
155
156 QemuFwCfgSelectItem (mRamfbFwCfgItem);
157 QemuFwCfgWriteBytes (sizeof (Config), &Config);
158
159 //
160 // clear screen
161 //
162 ZeroMem (&Black, sizeof (Black));
163 Status = FrameBufferBlt (
164 mQemuRamfbFrameBufferBltConfigure,
165 &Black,
166 EfiBltVideoFill,
167 0, // SourceX -- ignored
168 0, // SourceY -- ignored
169 0, // DestinationX
170 0, // DestinationY
171 ModeInfo->HorizontalResolution, // Width
172 ModeInfo->VerticalResolution, // Height
173 0 // Delta -- ignored
174 );
175 if (RETURN_ERROR (Status)) {
176 DEBUG ((DEBUG_WARN, "%a: clearing the screen failed: %r\n",
177 __FUNCTION__, Status));
178 }
179
180 return EFI_SUCCESS;
181 }
182
183 STATIC
184 EFI_STATUS
185 EFIAPI
186 QemuRamfbGraphicsOutputBlt (
187 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
188 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
189 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
190 IN UINTN SourceX,
191 IN UINTN SourceY,
192 IN UINTN DestinationX,
193 IN UINTN DestinationY,
194 IN UINTN Width,
195 IN UINTN Height,
196 IN UINTN Delta
197 )
198 {
199 return FrameBufferBlt (
200 mQemuRamfbFrameBufferBltConfigure,
201 BltBuffer,
202 BltOperation,
203 SourceX,
204 SourceY,
205 DestinationX,
206 DestinationY,
207 Width,
208 Height,
209 Delta
210 );
211 }
212
213 STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL mQemuRamfbGraphicsOutput = {
214 QemuRamfbGraphicsOutputQueryMode,
215 QemuRamfbGraphicsOutputSetMode,
216 QemuRamfbGraphicsOutputBlt,
217 &mQemuRamfbMode,
218 };
219
220 EFI_STATUS
221 EFIAPI
222 InitializeQemuRamfb (
223 IN EFI_HANDLE ImageHandle,
224 IN EFI_SYSTEM_TABLE *SystemTable
225 )
226 {
227 EFI_DEVICE_PATH_PROTOCOL *RamfbDevicePath;
228 EFI_DEVICE_PATH_PROTOCOL *GopDevicePath;
229 VOID *DevicePath;
230 VENDOR_DEVICE_PATH VendorDeviceNode;
231 ACPI_ADR_DEVICE_PATH AcpiDeviceNode;
232 EFI_STATUS Status;
233 EFI_PHYSICAL_ADDRESS FbBase;
234 UINTN FbSize, MaxFbSize, Pages;
235 UINTN FwCfgSize;
236 UINTN Index;
237
238 if (!QemuFwCfgIsAvailable ()) {
239 DEBUG ((DEBUG_INFO, "Ramfb: no FwCfg\n"));
240 return EFI_NOT_FOUND;
241 }
242
243 Status = QemuFwCfgFindFile ("etc/ramfb", &mRamfbFwCfgItem, &FwCfgSize);
244 if (EFI_ERROR (Status)) {
245 return EFI_NOT_FOUND;
246 }
247 if (FwCfgSize != sizeof (RAMFB_CONFIG)) {
248 DEBUG ((DEBUG_ERROR, "Ramfb: FwCfg size mismatch (expected %lu, got %lu)\n",
249 (UINT64)sizeof (RAMFB_CONFIG), (UINT64)FwCfgSize));
250 return EFI_PROTOCOL_ERROR;
251 }
252
253 MaxFbSize = 0;
254 for (Index = 0; Index < ARRAY_SIZE (mQemuRamfbModeInfo); Index++) {
255 mQemuRamfbModeInfo[Index].PixelsPerScanLine =
256 mQemuRamfbModeInfo[Index].HorizontalResolution;
257 mQemuRamfbModeInfo[Index].PixelFormat =
258 PixelBlueGreenRedReserved8BitPerColor;
259 FbSize = RAMFB_BPP *
260 mQemuRamfbModeInfo[Index].HorizontalResolution *
261 mQemuRamfbModeInfo[Index].VerticalResolution;
262 if (MaxFbSize < FbSize) {
263 MaxFbSize = FbSize;
264 }
265 DEBUG ((DEBUG_INFO, "Ramfb: Mode %lu: %ux%u, %lu kB\n", (UINT64)Index,
266 mQemuRamfbModeInfo[Index].HorizontalResolution,
267 mQemuRamfbModeInfo[Index].VerticalResolution,
268 (UINT64)(FbSize / 1024)));
269 }
270
271 Pages = EFI_SIZE_TO_PAGES (MaxFbSize);
272 MaxFbSize = EFI_PAGES_TO_SIZE (Pages);
273 FbBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedPages (Pages);
274 if (FbBase == 0) {
275 DEBUG ((DEBUG_ERROR, "Ramfb: memory allocation failed\n"));
276 return EFI_OUT_OF_RESOURCES;
277 }
278 DEBUG ((DEBUG_INFO, "Ramfb: Framebuffer at 0x%lx, %lu kB, %lu pages\n",
279 (UINT64)FbBase, (UINT64)(MaxFbSize / 1024), (UINT64)Pages));
280 mQemuRamfbMode.FrameBufferSize = MaxFbSize;
281 mQemuRamfbMode.FrameBufferBase = FbBase;
282
283 //
284 // 800 x 600
285 //
286 QemuRamfbGraphicsOutputSetMode (&mQemuRamfbGraphicsOutput, 1);
287
288 //
289 // ramfb vendor devpath
290 //
291 VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH;
292 VendorDeviceNode.Header.SubType = HW_VENDOR_DP;
293 CopyGuid (&VendorDeviceNode.Guid, &gQemuRamfbGuid);
294 SetDevicePathNodeLength (&VendorDeviceNode.Header,
295 sizeof (VENDOR_DEVICE_PATH));
296
297 RamfbDevicePath = AppendDevicePathNode (NULL,
298 (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode);
299 if (RamfbDevicePath == NULL) {
300 Status = EFI_OUT_OF_RESOURCES;
301 goto FreeFramebuffer;
302 }
303
304 Status = gBS->InstallMultipleProtocolInterfaces (
305 &mRamfbHandle,
306 &gEfiDevicePathProtocolGuid,
307 RamfbDevicePath,
308 NULL
309 );
310 if (EFI_ERROR (Status)) {
311 DEBUG ((DEBUG_ERROR, "Ramfb: install Ramfb Vendor DevicePath failed: %r\n",
312 Status));
313 goto FreeRamfbDevicePath;
314 }
315
316 //
317 // gop devpath + protocol
318 //
319 AcpiDeviceNode.Header.Type = ACPI_DEVICE_PATH;
320 AcpiDeviceNode.Header.SubType = ACPI_ADR_DP;
321 AcpiDeviceNode.ADR = ACPI_DISPLAY_ADR (
322 1, // DeviceIdScheme
323 0, // HeadId
324 0, // NonVgaOutput
325 1, // BiosCanDetect
326 0, // VendorInfo
327 ACPI_ADR_DISPLAY_TYPE_EXTERNAL_DIGITAL, // Type
328 0, // Port
329 0 // Index
330 );
331 SetDevicePathNodeLength (&AcpiDeviceNode.Header,
332 sizeof (ACPI_ADR_DEVICE_PATH));
333
334 GopDevicePath = AppendDevicePathNode (RamfbDevicePath,
335 (EFI_DEVICE_PATH_PROTOCOL *) &AcpiDeviceNode);
336 if (GopDevicePath == NULL) {
337 Status = EFI_OUT_OF_RESOURCES;
338 goto FreeRamfbHandle;
339 }
340
341 Status = gBS->InstallMultipleProtocolInterfaces (
342 &mGopHandle,
343 &gEfiDevicePathProtocolGuid,
344 GopDevicePath,
345 &gEfiGraphicsOutputProtocolGuid,
346 &mQemuRamfbGraphicsOutput,
347 NULL
348 );
349 if (EFI_ERROR (Status)) {
350 DEBUG ((DEBUG_ERROR, "Ramfb: install GOP DevicePath failed: %r\n",
351 Status));
352 goto FreeGopDevicePath;
353 }
354
355 Status = gBS->OpenProtocol (
356 mRamfbHandle,
357 &gEfiDevicePathProtocolGuid,
358 &DevicePath,
359 gImageHandle,
360 mGopHandle,
361 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
362 );
363 if (EFI_ERROR (Status)) {
364 DEBUG ((DEBUG_ERROR, "Ramfb: OpenProtocol failed: %r\n", Status));
365 goto FreeGopHandle;
366 }
367
368 return EFI_SUCCESS;
369
370 FreeGopHandle:
371 gBS->UninstallMultipleProtocolInterfaces (
372 mGopHandle,
373 &gEfiDevicePathProtocolGuid,
374 GopDevicePath,
375 &gEfiGraphicsOutputProtocolGuid,
376 &mQemuRamfbGraphicsOutput,
377 NULL
378 );
379 FreeGopDevicePath:
380 FreePool (GopDevicePath);
381 FreeRamfbHandle:
382 gBS->UninstallMultipleProtocolInterfaces (
383 mRamfbHandle,
384 &gEfiDevicePathProtocolGuid,
385 RamfbDevicePath,
386 NULL
387 );
388 FreeRamfbDevicePath:
389 FreePool (RamfbDevicePath);
390 FreeFramebuffer:
391 FreePages ((VOID*)(UINTN)mQemuRamfbMode.FrameBufferBase, Pages);
392 return Status;
393 }