]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioGpuDxe/Gop.c
OvmfPkg/VirtioGpuDxe: replace struct copy with CopyMem call
[mirror_edk2.git] / OvmfPkg / VirtioGpuDxe / Gop.c
CommitLineData
8731debe
LE
1/** @file\r
2\r
3 EFI_GRAPHICS_OUTPUT_PROTOCOL member functions for the VirtIo GPU driver.\r
4\r
5 Copyright (C) 2016, Red Hat, Inc.\r
6\r
b26f0cf9 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
8731debe
LE
8\r
9**/\r
10\r
8731debe 11#include <Library/MemoryAllocationLib.h>\r
916f90ba 12#include <Library/PcdLib.h>\r
8731debe
LE
13\r
14#include "VirtioGpu.h"\r
15\r
16/**\r
17 Release guest-side and host-side resources that are related to an initialized\r
18 VGPU_GOP.Gop.\r
19\r
20 param[in,out] VgpuGop The VGPU_GOP object to release resources for.\r
21\r
22 On input, the caller is responsible for having called\r
23 VgpuGop->Gop.SetMode() at least once successfully.\r
24 (This is equivalent to the requirement that\r
25 VgpuGop->BackingStore be non-NULL. It is also\r
26 equivalent to the requirement that VgpuGop->ResourceId\r
27 be nonzero.)\r
28\r
29 On output, resources will be released, and\r
30 VgpuGop->BackingStore and VgpuGop->ResourceId will be\r
31 nulled.\r
32\r
33 param[in] DisableHead Whether this head (scanout) currently references the\r
34 resource identified by VgpuGop->ResourceId. Only pass\r
35 FALSE when VgpuGop->Gop.SetMode() calls this function\r
36 while switching between modes, and set it to TRUE\r
37 every other time.\r
38**/\r
39VOID\r
40ReleaseGopResources (\r
ac0a286f
MK
41 IN OUT VGPU_GOP *VgpuGop,\r
42 IN BOOLEAN DisableHead\r
8731debe
LE
43 )\r
44{\r
ac0a286f 45 EFI_STATUS Status;\r
8731debe
LE
46\r
47 ASSERT (VgpuGop->ResourceId != 0);\r
48 ASSERT (VgpuGop->BackingStore != NULL);\r
49\r
50 //\r
51 // If any of the following host-side destruction steps fail, we can't get out\r
52 // of an inconsistent state, so we'll hang. In general errors in object\r
53 // destruction can hardly be recovered from.\r
54 //\r
55 if (DisableHead) {\r
56 //\r
57 // Dissociate head (scanout) #0 from the currently used 2D host resource,\r
58 // by setting ResourceId=0 for it.\r
59 //\r
60 Status = VirtioGpuSetScanout (\r
61 VgpuGop->ParentBus, // VgpuDev\r
ac0a286f
MK
62 0,\r
63 0,\r
64 0,\r
65 0, // X, Y, Width, Height\r
8731debe
LE
66 0, // ScanoutId\r
67 0 // ResourceId\r
68 );\r
69 //\r
70 // HACK BEGINS HERE\r
71 //\r
72 // According to the GPU Device section of the VirtIo specification, the\r
73 // above operation is valid:\r
74 //\r
75 // "The driver can use resource_id = 0 to disable a scanout."\r
76 //\r
77 // However, in practice QEMU does not allow us to disable head (scanout) #0\r
78 // -- it rejects the command with response code 0x1202\r
79 // (VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID). Looking at the QEMU source\r
80 // code, function virtio_gpu_set_scanout() in "hw/display/virtio-gpu.c",\r
81 // this appears fully intentional, despite not being documented in the\r
82 // spec.\r
83 //\r
84 // Surprisingly, ignoring the error here, and proceeding to release\r
85 // host-side resources that presumably underlie head (scanout) #0, work\r
86 // without any problems -- the driver survives repeated "disconnect" /\r
87 // "connect -r" commands in the UEFI shell.\r
88 //\r
89 // So, for now, let's just suppress the error.\r
90 //\r
91 Status = EFI_SUCCESS;\r
92 //\r
93 // HACK ENDS HERE\r
94 //\r
95\r
96 ASSERT_EFI_ERROR (Status);\r
97 if (EFI_ERROR (Status)) {\r
98 CpuDeadLoop ();\r
99 }\r
100 }\r
101\r
102 //\r
103 // Detach backing pages from the currently used 2D host resource.\r
104 //\r
105 Status = VirtioGpuResourceDetachBacking (\r
106 VgpuGop->ParentBus, // VgpuDev\r
107 VgpuGop->ResourceId // ResourceId\r
108 );\r
109 ASSERT_EFI_ERROR (Status);\r
110 if (EFI_ERROR (Status)) {\r
111 CpuDeadLoop ();\r
112 }\r
113\r
114 //\r
f10ae923 115 // Unmap and release backing pages.\r
8731debe 116 //\r
f10ae923
LE
117 VirtioGpuUnmapAndFreeBackingStore (\r
118 VgpuGop->ParentBus, // VgpuDev\r
119 VgpuGop->NumberOfPages, // NumberOfPages\r
120 VgpuGop->BackingStore, // HostAddress\r
121 VgpuGop->BackingStoreMap // Mapping\r
122 );\r
ac0a286f
MK
123 VgpuGop->BackingStore = NULL;\r
124 VgpuGop->NumberOfPages = 0;\r
f10ae923 125 VgpuGop->BackingStoreMap = NULL;\r
8731debe
LE
126\r
127 //\r
128 // Destroy the currently used 2D host resource.\r
129 //\r
130 Status = VirtioGpuResourceUnref (\r
131 VgpuGop->ParentBus, // VgpuDev\r
132 VgpuGop->ResourceId // ResourceId\r
133 );\r
134 ASSERT_EFI_ERROR (Status);\r
135 if (EFI_ERROR (Status)) {\r
136 CpuDeadLoop ();\r
137 }\r
ac0a286f 138\r
8731debe
LE
139 VgpuGop->ResourceId = 0;\r
140}\r
141\r
142//\r
143// The resolutions supported by this driver.\r
144//\r
145typedef struct {\r
ac0a286f
MK
146 UINT32 Width;\r
147 UINT32 Height;\r
8731debe
LE
148} GOP_RESOLUTION;\r
149\r
ac0a286f
MK
150STATIC CONST GOP_RESOLUTION mGopResolutions[] = {\r
151 { 640, 480 },\r
152 { 800, 480 },\r
153 { 800, 600 },\r
154 { 832, 624 },\r
155 { 960, 640 },\r
156 { 1024, 600 },\r
157 { 1024, 768 },\r
158 { 1152, 864 },\r
159 { 1152, 870 },\r
160 { 1280, 720 },\r
161 { 1280, 760 },\r
162 { 1280, 768 },\r
163 { 1280, 800 },\r
164 { 1280, 960 },\r
8731debe 165 { 1280, 1024 },\r
ac0a286f
MK
166 { 1360, 768 },\r
167 { 1366, 768 },\r
8731debe 168 { 1400, 1050 },\r
ac0a286f
MK
169 { 1440, 900 },\r
170 { 1600, 900 },\r
8731debe
LE
171 { 1600, 1200 },\r
172 { 1680, 1050 },\r
173 { 1920, 1080 },\r
174 { 1920, 1200 },\r
175 { 1920, 1440 },\r
176 { 2000, 2000 },\r
177 { 2048, 1536 },\r
178 { 2048, 2048 },\r
179 { 2560, 1440 },\r
180 { 2560, 1600 },\r
181 { 2560, 2048 },\r
182 { 2800, 2100 },\r
183 { 3200, 2400 },\r
184 { 3840, 2160 },\r
185 { 4096, 2160 },\r
186 { 7680, 4320 },\r
187 { 8192, 4320 },\r
188};\r
189\r
190//\r
191// Macro for casting VGPU_GOP.Gop to VGPU_GOP.\r
192//\r
193#define VGPU_GOP_FROM_GOP(GopPointer) \\r
194 CR (GopPointer, VGPU_GOP, Gop, VGPU_GOP_SIG)\r
195\r
916f90ba
GH
196STATIC\r
197VOID\r
198EFIAPI\r
199GopNativeResolution (\r
200 IN VGPU_GOP *VgpuGop,\r
201 OUT UINT32 *XRes,\r
202 OUT UINT32 *YRes\r
203 )\r
204{\r
205 volatile VIRTIO_GPU_RESP_DISPLAY_INFO DisplayInfo;\r
206 EFI_STATUS Status;\r
207 UINTN Index;\r
208\r
209 Status = VirtioGpuGetDisplayInfo (VgpuGop->ParentBus, &DisplayInfo);\r
210 if (Status != EFI_SUCCESS) {\r
211 return;\r
212 }\r
213\r
214 for (Index = 0; Index < VIRTIO_GPU_MAX_SCANOUTS; Index++) {\r
215 if (!DisplayInfo.Pmodes[Index].Enabled ||\r
216 !DisplayInfo.Pmodes[Index].Rectangle.Width ||\r
217 !DisplayInfo.Pmodes[Index].Rectangle.Height)\r
218 {\r
219 continue;\r
220 }\r
221\r
222 DEBUG ((\r
223 DEBUG_INFO,\r
224 "%a: #%d: %dx%d\n",\r
225 __FUNCTION__,\r
226 Index,\r
227 DisplayInfo.Pmodes[Index].Rectangle.Width,\r
228 DisplayInfo.Pmodes[Index].Rectangle.Height\r
229 ));\r
230 if ((*XRes == 0) || (*YRes == 0)) {\r
231 *XRes = DisplayInfo.Pmodes[Index].Rectangle.Width;\r
232 *YRes = DisplayInfo.Pmodes[Index].Rectangle.Height;\r
233 }\r
234 }\r
235}\r
236\r
86de090b
GH
237STATIC\r
238VOID\r
239EFIAPI\r
240GopInitialize (\r
241 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This\r
242 )\r
243{\r
916f90ba
GH
244 VGPU_GOP *VgpuGop;\r
245 EFI_STATUS Status;\r
246 UINT32 XRes = 0, YRes = 0, Index;\r
86de090b
GH
247\r
248 VgpuGop = VGPU_GOP_FROM_GOP (This);\r
249\r
250 //\r
251 // Set up the Gop -> GopMode -> GopModeInfo pointer chain, and the other\r
252 // (nonzero) constant fields.\r
253 //\r
254 // No direct framebuffer access is supported, only Blt() is.\r
255 //\r
256 VgpuGop->Gop.Mode = &VgpuGop->GopMode;\r
257\r
258 VgpuGop->GopMode.MaxMode = (UINT32)(ARRAY_SIZE (mGopResolutions));\r
259 VgpuGop->GopMode.Info = &VgpuGop->GopModeInfo;\r
260 VgpuGop->GopMode.SizeOfInfo = sizeof VgpuGop->GopModeInfo;\r
261\r
262 VgpuGop->GopModeInfo.PixelFormat = PixelBltOnly;\r
916f90ba
GH
263\r
264 //\r
265 // query host for display resolution\r
266 //\r
267 GopNativeResolution (VgpuGop, &XRes, &YRes);\r
268 if ((XRes == 0) || (YRes == 0)) {\r
269 return;\r
270 }\r
271\r
272 if (PcdGet8 (PcdVideoResolutionSource) == 0) {\r
273 Status = PcdSet32S (PcdVideoHorizontalResolution, XRes);\r
274 ASSERT_RETURN_ERROR (Status);\r
275 Status = PcdSet32S (PcdVideoVerticalResolution, YRes);\r
276 ASSERT_RETURN_ERROR (Status);\r
277 Status = PcdSet8S (PcdVideoResolutionSource, 2);\r
278 ASSERT_RETURN_ERROR (Status);\r
279 }\r
280\r
281 VgpuGop->NativeXRes = XRes;\r
282 VgpuGop->NativeYRes = YRes;\r
283 for (Index = 0; Index < ARRAY_SIZE (mGopResolutions); Index++) {\r
284 if ((mGopResolutions[Index].Width == XRes) &&\r
285 (mGopResolutions[Index].Height == YRes))\r
286 {\r
287 // native resolution already is in mode list\r
288 return;\r
289 }\r
290 }\r
291\r
292 // add to mode list\r
293 VgpuGop->GopMode.MaxMode++;\r
86de090b
GH
294}\r
295\r
8731debe
LE
296//\r
297// EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.\r
298//\r
299STATIC\r
300EFI_STATUS\r
301EFIAPI\r
302GopQueryMode (\r
ac0a286f
MK
303 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
304 IN UINT32 ModeNumber,\r
305 OUT UINTN *SizeOfInfo,\r
306 OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info\r
8731debe
LE
307 )\r
308{\r
ac0a286f 309 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;\r
8731debe 310\r
86de090b 311 if (ModeNumber >= This->Mode->MaxMode) {\r
8731debe
LE
312 return EFI_INVALID_PARAMETER;\r
313 }\r
314\r
315 GopModeInfo = AllocateZeroPool (sizeof *GopModeInfo);\r
316 if (GopModeInfo == NULL) {\r
317 return EFI_OUT_OF_RESOURCES;\r
318 }\r
319\r
916f90ba
GH
320 if (ModeNumber < ARRAY_SIZE (mGopResolutions)) {\r
321 GopModeInfo->HorizontalResolution = mGopResolutions[ModeNumber].Width;\r
322 GopModeInfo->VerticalResolution = mGopResolutions[ModeNumber].Height;\r
323 } else {\r
324 VGPU_GOP *VgpuGop = VGPU_GOP_FROM_GOP (This);\r
325 GopModeInfo->HorizontalResolution = VgpuGop->NativeXRes;\r
326 GopModeInfo->VerticalResolution = VgpuGop->NativeYRes;\r
327 }\r
328\r
329 GopModeInfo->PixelFormat = PixelBltOnly;\r
330 GopModeInfo->PixelsPerScanLine = GopModeInfo->HorizontalResolution;\r
8731debe
LE
331\r
332 *SizeOfInfo = sizeof *GopModeInfo;\r
ac0a286f 333 *Info = GopModeInfo;\r
8731debe
LE
334 return EFI_SUCCESS;\r
335}\r
336\r
337STATIC\r
338EFI_STATUS\r
339EFIAPI\r
340GopSetMode (\r
ac0a286f
MK
341 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
342 IN UINT32 ModeNumber\r
8731debe
LE
343 )\r
344{\r
5f6ecaa3
GH
345 VGPU_GOP *VgpuGop;\r
346 UINT32 NewResourceId;\r
347 UINTN NewNumberOfBytes;\r
348 UINTN NewNumberOfPages;\r
349 VOID *NewBackingStore;\r
350 EFI_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress;\r
351 VOID *NewBackingStoreMap;\r
352 UINTN SizeOfInfo;\r
353 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;\r
f10ae923 354\r
ac0a286f
MK
355 EFI_STATUS Status;\r
356 EFI_STATUS Status2;\r
8731debe 357\r
86de090b
GH
358 if (!This->Mode) {\r
359 // SetMode() call in InitVgpuGop() triggers this.\r
360 GopInitialize (This);\r
361 }\r
362\r
5f6ecaa3
GH
363 Status = GopQueryMode (This, ModeNumber, &SizeOfInfo, &GopModeInfo);\r
364 if (Status != EFI_SUCCESS) {\r
365 return Status;\r
8731debe
LE
366 }\r
367\r
368 VgpuGop = VGPU_GOP_FROM_GOP (This);\r
369\r
370 //\r
371 // Distinguish the first (internal) call from the other (protocol consumer)\r
372 // calls.\r
373 //\r
374 if (VgpuGop->ResourceId == 0) {\r
8731debe
LE
375 //\r
376 // This is the first time we create a host side resource.\r
377 //\r
378 NewResourceId = 1;\r
379 } else {\r
380 //\r
381 // We already have an active host side resource. Create the new one without\r
382 // interfering with the current one, so that we can cleanly bail out on\r
383 // error, without disturbing the current graphics mode.\r
384 //\r
385 // The formula below will alternate between IDs 1 and 2.\r
386 //\r
387 NewResourceId = 3 - VgpuGop->ResourceId;\r
388 }\r
389\r
390 //\r
391 // Create the 2D host resource.\r
392 //\r
393 Status = VirtioGpuResourceCreate2d (\r
394 VgpuGop->ParentBus, // VgpuDev\r
395 NewResourceId, // ResourceId\r
396 VirtioGpuFormatB8G8R8X8Unorm, // Format\r
5f6ecaa3
GH
397 GopModeInfo->HorizontalResolution, // Width\r
398 GopModeInfo->VerticalResolution // Height\r
8731debe
LE
399 );\r
400 if (EFI_ERROR (Status)) {\r
401 return Status;\r
402 }\r
403\r
404 //\r
f10ae923
LE
405 // Allocate, zero and map guest backing store, for bus master common buffer\r
406 // operation.\r
8731debe 407 //\r
5f6ecaa3
GH
408 NewNumberOfBytes = GopModeInfo->HorizontalResolution *\r
409 GopModeInfo->VerticalResolution * sizeof (UINT32);\r
8731debe 410 NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);\r
ac0a286f
MK
411 Status = VirtioGpuAllocateZeroAndMapBackingStore (\r
412 VgpuGop->ParentBus, // VgpuDev\r
413 NewNumberOfPages, // NumberOfPages\r
414 &NewBackingStore, // HostAddress\r
415 &NewBackingStoreDeviceAddress, // DeviceAddress\r
416 &NewBackingStoreMap // Mapping\r
417 );\r
f10ae923 418 if (EFI_ERROR (Status)) {\r
8731debe
LE
419 goto DestroyHostResource;\r
420 }\r
8731debe
LE
421\r
422 //\r
423 // Attach backing store to the host resource.\r
424 //\r
425 Status = VirtioGpuResourceAttachBacking (\r
5409c6ab
LE
426 VgpuGop->ParentBus, // VgpuDev\r
427 NewResourceId, // ResourceId\r
f10ae923 428 NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress\r
5409c6ab 429 NewNumberOfPages // NumberOfPages\r
8731debe
LE
430 );\r
431 if (EFI_ERROR (Status)) {\r
f10ae923 432 goto UnmapAndFreeBackingStore;\r
8731debe
LE
433 }\r
434\r
435 //\r
436 // Point head (scanout) #0 to the host resource.\r
437 //\r
438 Status = VirtioGpuSetScanout (\r
439 VgpuGop->ParentBus, // VgpuDev\r
440 0, // X\r
441 0, // Y\r
5f6ecaa3
GH
442 GopModeInfo->HorizontalResolution, // Width\r
443 GopModeInfo->VerticalResolution, // Height\r
8731debe
LE
444 0, // ScanoutId\r
445 NewResourceId // ResourceId\r
446 );\r
447 if (EFI_ERROR (Status)) {\r
448 goto DetachBackingStore;\r
449 }\r
450\r
451 //\r
452 // If this is not the first (i.e., internal) call, then we have to (a) flush\r
453 // the new resource to head (scanout) #0, after having flipped the latter to\r
454 // the former above, plus (b) release the old resources.\r
455 //\r
456 if (VgpuGop->ResourceId != 0) {\r
457 Status = VirtioGpuResourceFlush (\r
458 VgpuGop->ParentBus, // VgpuDev\r
459 0, // X\r
460 0, // Y\r
5f6ecaa3
GH
461 GopModeInfo->HorizontalResolution, // Width\r
462 GopModeInfo->VerticalResolution, // Height\r
8731debe
LE
463 NewResourceId // ResourceId\r
464 );\r
465 if (EFI_ERROR (Status)) {\r
466 //\r
467 // Flip head (scanout) #0 back to the current resource. If this fails, we\r
468 // cannot continue, as this error occurs on the error path and is\r
469 // therefore non-recoverable.\r
470 //\r
471 Status2 = VirtioGpuSetScanout (\r
5f6ecaa3
GH
472 VgpuGop->ParentBus, // VgpuDev\r
473 0, // X\r
474 0, // Y\r
475 VgpuGop->GopModeInfo.HorizontalResolution, // Width\r
476 VgpuGop->GopModeInfo.VerticalResolution, // Height\r
477 0, // ScanoutId\r
478 VgpuGop->ResourceId // ResourceId\r
8731debe
LE
479 );\r
480 ASSERT_EFI_ERROR (Status2);\r
481 if (EFI_ERROR (Status2)) {\r
482 CpuDeadLoop ();\r
483 }\r
ac0a286f 484\r
8731debe
LE
485 goto DetachBackingStore;\r
486 }\r
487\r
488 //\r
489 // Flush successful; release the old resources (without disabling head\r
490 // (scanout) #0).\r
491 //\r
492 ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);\r
493 }\r
494\r
495 //\r
496 // This is either the first (internal) call when we have no old resources\r
497 // yet, or we've changed the mode successfully and released the old\r
498 // resources.\r
499 //\r
500 ASSERT (VgpuGop->ResourceId == 0);\r
501 ASSERT (VgpuGop->BackingStore == NULL);\r
502\r
ac0a286f
MK
503 VgpuGop->ResourceId = NewResourceId;\r
504 VgpuGop->BackingStore = NewBackingStore;\r
505 VgpuGop->NumberOfPages = NewNumberOfPages;\r
f10ae923 506 VgpuGop->BackingStoreMap = NewBackingStoreMap;\r
8731debe
LE
507\r
508 //\r
509 // Populate Mode and ModeInfo (mutable fields only).\r
510 //\r
5f6ecaa3 511 VgpuGop->GopMode.Mode = ModeNumber;\r
3ca7326b 512 CopyMem (&VgpuGop->GopModeInfo, GopModeInfo, sizeof VgpuGop->GopModeInfo);\r
5f6ecaa3 513 FreePool (GopModeInfo);\r
8731debe
LE
514 return EFI_SUCCESS;\r
515\r
516DetachBackingStore:\r
517 Status2 = VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResourceId);\r
518 ASSERT_EFI_ERROR (Status2);\r
519 if (EFI_ERROR (Status2)) {\r
520 CpuDeadLoop ();\r
521 }\r
522\r
f10ae923
LE
523UnmapAndFreeBackingStore:\r
524 VirtioGpuUnmapAndFreeBackingStore (\r
525 VgpuGop->ParentBus, // VgpuDev\r
526 NewNumberOfPages, // NumberOfPages\r
527 NewBackingStore, // HostAddress\r
528 NewBackingStoreMap // Mapping\r
529 );\r
8731debe
LE
530\r
531DestroyHostResource:\r
532 Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);\r
533 ASSERT_EFI_ERROR (Status2);\r
534 if (EFI_ERROR (Status2)) {\r
535 CpuDeadLoop ();\r
536 }\r
537\r
5f6ecaa3 538 FreePool (GopModeInfo);\r
8731debe
LE
539 return Status;\r
540}\r
541\r
542STATIC\r
543EFI_STATUS\r
544EFIAPI\r
545GopBlt (\r
ac0a286f
MK
546 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
547 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL,\r
548 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,\r
549 IN UINTN SourceX,\r
550 IN UINTN SourceY,\r
551 IN UINTN DestinationX,\r
552 IN UINTN DestinationY,\r
553 IN UINTN Width,\r
554 IN UINTN Height,\r
555 IN UINTN Delta OPTIONAL\r
8731debe
LE
556 )\r
557{\r
ac0a286f
MK
558 VGPU_GOP *VgpuGop;\r
559 UINT32 CurrentHorizontal;\r
560 UINT32 CurrentVertical;\r
561 UINTN SegmentSize;\r
562 UINTN Y;\r
563 UINTN ResourceOffset;\r
564 EFI_STATUS Status;\r
565\r
566 VgpuGop = VGPU_GOP_FROM_GOP (This);\r
8731debe
LE
567 CurrentHorizontal = VgpuGop->GopModeInfo.HorizontalResolution;\r
568 CurrentVertical = VgpuGop->GopModeInfo.VerticalResolution;\r
569\r
570 //\r
571 // We can avoid pixel format conversion in the guest because the internal\r
572 // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of\r
573 // VirtioGpuFormatB8G8R8X8Unorm are identical.\r
574 //\r
575 SegmentSize = Width * sizeof (UINT32);\r
576\r
577 //\r
578 // Delta is relevant for operations that read a rectangle from, or write a\r
579 // rectangle to, BltBuffer.\r
580 //\r
581 // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is\r
582 // zero, then Width is the entire width of BltBuffer, and the stride is\r
583 // supposed to be calculated from Width.\r
584 //\r
ac0a286f
MK
585 if ((BltOperation == EfiBltVideoToBltBuffer) ||\r
586 (BltOperation == EfiBltBufferToVideo))\r
587 {\r
8731debe
LE
588 if (Delta == 0) {\r
589 Delta = SegmentSize;\r
590 }\r
591 }\r
592\r
593 //\r
594 // For operations that write to the display, check if the destination fits\r
595 // onto the display.\r
596 //\r
ac0a286f
MK
597 if ((BltOperation == EfiBltVideoFill) ||\r
598 (BltOperation == EfiBltBufferToVideo) ||\r
599 (BltOperation == EfiBltVideoToVideo))\r
600 {\r
601 if ((DestinationX > CurrentHorizontal) ||\r
602 (Width > CurrentHorizontal - DestinationX) ||\r
603 (DestinationY > CurrentVertical) ||\r
604 (Height > CurrentVertical - DestinationY))\r
605 {\r
8731debe
LE
606 return EFI_INVALID_PARAMETER;\r
607 }\r
608 }\r
609\r
610 //\r
611 // For operations that read from the display, check if the source fits onto\r
612 // the display.\r
613 //\r
ac0a286f
MK
614 if ((BltOperation == EfiBltVideoToBltBuffer) ||\r
615 (BltOperation == EfiBltVideoToVideo))\r
616 {\r
617 if ((SourceX > CurrentHorizontal) ||\r
618 (Width > CurrentHorizontal - SourceX) ||\r
619 (SourceY > CurrentVertical) ||\r
620 (Height > CurrentVertical - SourceY))\r
621 {\r
8731debe
LE
622 return EFI_INVALID_PARAMETER;\r
623 }\r
624 }\r
625\r
626 //\r
627 // Render the request. For requests that do not modify the display, there\r
628 // won't be further steps.\r
629 //\r
630 switch (BltOperation) {\r
ac0a286f
MK
631 case EfiBltVideoFill:\r
632 //\r
633 // Write data from the BltBuffer pixel (0, 0) directly to every pixel of\r
634 // the video display rectangle (DestinationX, DestinationY) (DestinationX +\r
635 // Width, DestinationY + Height). Only one pixel will be used from the\r
636 // BltBuffer. Delta is NOT used.\r
637 //\r
638 for (Y = 0; Y < Height; ++Y) {\r
639 SetMem32 (\r
640 VgpuGop->BackingStore +\r
8731debe 641 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
ac0a286f
MK
642 SegmentSize,\r
643 *(UINT32 *)BltBuffer\r
644 );\r
645 }\r
8731debe 646\r
ac0a286f 647 break;\r
8731debe 648\r
ac0a286f
MK
649 case EfiBltVideoToBltBuffer:\r
650 //\r
651 // Read data from the video display rectangle (SourceX, SourceY) (SourceX +\r
652 // Width, SourceY + Height) and place it in the BltBuffer rectangle\r
653 // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +\r
654 // Height). If DestinationX or DestinationY is not zero then Delta must be\r
655 // set to the length in bytes of a row in the BltBuffer.\r
656 //\r
657 for (Y = 0; Y < Height; ++Y) {\r
8731debe 658 CopyMem (\r
ac0a286f
MK
659 (UINT8 *)BltBuffer +\r
660 (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,\r
8731debe 661 VgpuGop->BackingStore +\r
ac0a286f 662 (SourceY + Y) * CurrentHorizontal + SourceX,\r
8731debe
LE
663 SegmentSize\r
664 );\r
665 }\r
ac0a286f
MK
666\r
667 return EFI_SUCCESS;\r
668\r
669 case EfiBltBufferToVideo:\r
670 //\r
671 // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +\r
672 // Width, SourceY + Height) directly to the video display rectangle\r
673 // (DestinationX, DestinationY) (DestinationX + Width, DestinationY +\r
674 // Height). If SourceX or SourceY is not zero then Delta must be set to the\r
675 // length in bytes of a row in the BltBuffer.\r
676 //\r
8731debe
LE
677 for (Y = 0; Y < Height; ++Y) {\r
678 CopyMem (\r
679 VgpuGop->BackingStore +\r
ac0a286f
MK
680 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
681 (UINT8 *)BltBuffer +\r
682 (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,\r
8731debe
LE
683 SegmentSize\r
684 );\r
685 }\r
8731debe 686\r
ac0a286f
MK
687 break;\r
688\r
689 case EfiBltVideoToVideo:\r
690 //\r
691 // Copy from the video display rectangle (SourceX, SourceY) (SourceX +\r
692 // Width, SourceY + Height) to the video display rectangle (DestinationX,\r
693 // DestinationY) (DestinationX + Width, DestinationY + Height). The\r
694 // BltBuffer and Delta are not used in this mode.\r
695 //\r
696 // A single invocation of CopyMem() handles overlap between source and\r
697 // destination (that is, within a single line), but for multiple\r
698 // invocations, we must handle overlaps.\r
699 //\r
700 if (SourceY < DestinationY) {\r
701 Y = Height;\r
702 while (Y > 0) {\r
703 --Y;\r
704 CopyMem (\r
705 VgpuGop->BackingStore +\r
706 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
707 VgpuGop->BackingStore +\r
708 (SourceY + Y) * CurrentHorizontal + SourceX,\r
709 SegmentSize\r
710 );\r
711 }\r
712 } else {\r
713 for (Y = 0; Y < Height; ++Y) {\r
714 CopyMem (\r
715 VgpuGop->BackingStore +\r
716 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
717 VgpuGop->BackingStore +\r
718 (SourceY + Y) * CurrentHorizontal + SourceX,\r
719 SegmentSize\r
720 );\r
721 }\r
722 }\r
723\r
724 break;\r
725\r
726 default:\r
727 return EFI_INVALID_PARAMETER;\r
8731debe
LE
728 }\r
729\r
730 //\r
731 // For operations that wrote to the display, submit the updated area to the\r
732 // host -- update the host resource from guest memory.\r
733 //\r
734 ResourceOffset = sizeof (UINT32) * (DestinationY * CurrentHorizontal +\r
735 DestinationX);\r
736 Status = VirtioGpuTransferToHost2d (\r
737 VgpuGop->ParentBus, // VgpuDev\r
738 (UINT32)DestinationX, // X\r
739 (UINT32)DestinationY, // Y\r
740 (UINT32)Width, // Width\r
741 (UINT32)Height, // Height\r
742 ResourceOffset, // Offset\r
743 VgpuGop->ResourceId // ResourceId\r
744 );\r
745 if (EFI_ERROR (Status)) {\r
746 return Status;\r
747 }\r
748\r
749 //\r
750 // Flush the updated resource to the display.\r
751 //\r
752 Status = VirtioGpuResourceFlush (\r
753 VgpuGop->ParentBus, // VgpuDev\r
754 (UINT32)DestinationX, // X\r
755 (UINT32)DestinationY, // Y\r
756 (UINT32)Width, // Width\r
757 (UINT32)Height, // Height\r
758 VgpuGop->ResourceId // ResourceId\r
759 );\r
760 return Status;\r
761}\r
762\r
763//\r
764// Template for initializing VGPU_GOP.Gop.\r
765//\r
ac0a286f 766CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {\r
8731debe
LE
767 GopQueryMode,\r
768 GopSetMode,\r
769 GopBlt,\r
770 NULL // Mode, to be overwritten in the actual protocol instance\r
771};\r