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