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