]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioGpuDxe/Gop.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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
b94836b2
DP
311 if ((Info == NULL) ||\r
312 (SizeOfInfo == NULL) ||\r
313 (ModeNumber >= This->Mode->MaxMode))\r
314 {\r
8731debe
LE
315 return EFI_INVALID_PARAMETER;\r
316 }\r
317\r
318 GopModeInfo = AllocateZeroPool (sizeof *GopModeInfo);\r
319 if (GopModeInfo == NULL) {\r
320 return EFI_OUT_OF_RESOURCES;\r
321 }\r
322\r
916f90ba
GH
323 if (ModeNumber < ARRAY_SIZE (mGopResolutions)) {\r
324 GopModeInfo->HorizontalResolution = mGopResolutions[ModeNumber].Width;\r
325 GopModeInfo->VerticalResolution = mGopResolutions[ModeNumber].Height;\r
326 } else {\r
327 VGPU_GOP *VgpuGop = VGPU_GOP_FROM_GOP (This);\r
328 GopModeInfo->HorizontalResolution = VgpuGop->NativeXRes;\r
329 GopModeInfo->VerticalResolution = VgpuGop->NativeYRes;\r
330 }\r
331\r
332 GopModeInfo->PixelFormat = PixelBltOnly;\r
333 GopModeInfo->PixelsPerScanLine = GopModeInfo->HorizontalResolution;\r
8731debe
LE
334\r
335 *SizeOfInfo = sizeof *GopModeInfo;\r
ac0a286f 336 *Info = GopModeInfo;\r
8731debe
LE
337 return EFI_SUCCESS;\r
338}\r
339\r
340STATIC\r
341EFI_STATUS\r
342EFIAPI\r
343GopSetMode (\r
ac0a286f
MK
344 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
345 IN UINT32 ModeNumber\r
8731debe
LE
346 )\r
347{\r
5f6ecaa3
GH
348 VGPU_GOP *VgpuGop;\r
349 UINT32 NewResourceId;\r
350 UINTN NewNumberOfBytes;\r
351 UINTN NewNumberOfPages;\r
352 VOID *NewBackingStore;\r
353 EFI_PHYSICAL_ADDRESS NewBackingStoreDeviceAddress;\r
354 VOID *NewBackingStoreMap;\r
355 UINTN SizeOfInfo;\r
356 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;\r
f10ae923 357\r
ac0a286f
MK
358 EFI_STATUS Status;\r
359 EFI_STATUS Status2;\r
8731debe 360\r
86de090b
GH
361 if (!This->Mode) {\r
362 // SetMode() call in InitVgpuGop() triggers this.\r
363 GopInitialize (This);\r
364 }\r
365\r
5f6ecaa3
GH
366 Status = GopQueryMode (This, ModeNumber, &SizeOfInfo, &GopModeInfo);\r
367 if (Status != EFI_SUCCESS) {\r
368 return Status;\r
8731debe
LE
369 }\r
370\r
371 VgpuGop = VGPU_GOP_FROM_GOP (This);\r
372\r
373 //\r
374 // Distinguish the first (internal) call from the other (protocol consumer)\r
375 // calls.\r
376 //\r
377 if (VgpuGop->ResourceId == 0) {\r
8731debe
LE
378 //\r
379 // This is the first time we create a host side resource.\r
380 //\r
381 NewResourceId = 1;\r
382 } else {\r
383 //\r
384 // We already have an active host side resource. Create the new one without\r
385 // interfering with the current one, so that we can cleanly bail out on\r
386 // error, without disturbing the current graphics mode.\r
387 //\r
388 // The formula below will alternate between IDs 1 and 2.\r
389 //\r
390 NewResourceId = 3 - VgpuGop->ResourceId;\r
391 }\r
392\r
393 //\r
394 // Create the 2D host resource.\r
395 //\r
396 Status = VirtioGpuResourceCreate2d (\r
397 VgpuGop->ParentBus, // VgpuDev\r
398 NewResourceId, // ResourceId\r
399 VirtioGpuFormatB8G8R8X8Unorm, // Format\r
5f6ecaa3
GH
400 GopModeInfo->HorizontalResolution, // Width\r
401 GopModeInfo->VerticalResolution // Height\r
8731debe
LE
402 );\r
403 if (EFI_ERROR (Status)) {\r
404 return Status;\r
405 }\r
406\r
407 //\r
f10ae923
LE
408 // Allocate, zero and map guest backing store, for bus master common buffer\r
409 // operation.\r
8731debe 410 //\r
5f6ecaa3
GH
411 NewNumberOfBytes = GopModeInfo->HorizontalResolution *\r
412 GopModeInfo->VerticalResolution * sizeof (UINT32);\r
8731debe 413 NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);\r
ac0a286f
MK
414 Status = VirtioGpuAllocateZeroAndMapBackingStore (\r
415 VgpuGop->ParentBus, // VgpuDev\r
416 NewNumberOfPages, // NumberOfPages\r
417 &NewBackingStore, // HostAddress\r
418 &NewBackingStoreDeviceAddress, // DeviceAddress\r
419 &NewBackingStoreMap // Mapping\r
420 );\r
f10ae923 421 if (EFI_ERROR (Status)) {\r
8731debe
LE
422 goto DestroyHostResource;\r
423 }\r
8731debe
LE
424\r
425 //\r
426 // Attach backing store to the host resource.\r
427 //\r
428 Status = VirtioGpuResourceAttachBacking (\r
5409c6ab
LE
429 VgpuGop->ParentBus, // VgpuDev\r
430 NewResourceId, // ResourceId\r
f10ae923 431 NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress\r
5409c6ab 432 NewNumberOfPages // NumberOfPages\r
8731debe
LE
433 );\r
434 if (EFI_ERROR (Status)) {\r
f10ae923 435 goto UnmapAndFreeBackingStore;\r
8731debe
LE
436 }\r
437\r
438 //\r
439 // Point head (scanout) #0 to the host resource.\r
440 //\r
441 Status = VirtioGpuSetScanout (\r
442 VgpuGop->ParentBus, // VgpuDev\r
443 0, // X\r
444 0, // Y\r
5f6ecaa3
GH
445 GopModeInfo->HorizontalResolution, // Width\r
446 GopModeInfo->VerticalResolution, // Height\r
8731debe
LE
447 0, // ScanoutId\r
448 NewResourceId // ResourceId\r
449 );\r
450 if (EFI_ERROR (Status)) {\r
451 goto DetachBackingStore;\r
452 }\r
453\r
454 //\r
455 // If this is not the first (i.e., internal) call, then we have to (a) flush\r
456 // the new resource to head (scanout) #0, after having flipped the latter to\r
457 // the former above, plus (b) release the old resources.\r
458 //\r
459 if (VgpuGop->ResourceId != 0) {\r
460 Status = VirtioGpuResourceFlush (\r
461 VgpuGop->ParentBus, // VgpuDev\r
462 0, // X\r
463 0, // Y\r
5f6ecaa3
GH
464 GopModeInfo->HorizontalResolution, // Width\r
465 GopModeInfo->VerticalResolution, // Height\r
8731debe
LE
466 NewResourceId // ResourceId\r
467 );\r
468 if (EFI_ERROR (Status)) {\r
469 //\r
470 // Flip head (scanout) #0 back to the current resource. If this fails, we\r
471 // cannot continue, as this error occurs on the error path and is\r
472 // therefore non-recoverable.\r
473 //\r
474 Status2 = VirtioGpuSetScanout (\r
5f6ecaa3
GH
475 VgpuGop->ParentBus, // VgpuDev\r
476 0, // X\r
477 0, // Y\r
478 VgpuGop->GopModeInfo.HorizontalResolution, // Width\r
479 VgpuGop->GopModeInfo.VerticalResolution, // Height\r
480 0, // ScanoutId\r
481 VgpuGop->ResourceId // ResourceId\r
8731debe
LE
482 );\r
483 ASSERT_EFI_ERROR (Status2);\r
484 if (EFI_ERROR (Status2)) {\r
485 CpuDeadLoop ();\r
486 }\r
ac0a286f 487\r
8731debe
LE
488 goto DetachBackingStore;\r
489 }\r
490\r
491 //\r
492 // Flush successful; release the old resources (without disabling head\r
493 // (scanout) #0).\r
494 //\r
495 ReleaseGopResources (VgpuGop, FALSE /* DisableHead */);\r
496 }\r
497\r
498 //\r
499 // This is either the first (internal) call when we have no old resources\r
500 // yet, or we've changed the mode successfully and released the old\r
501 // resources.\r
502 //\r
503 ASSERT (VgpuGop->ResourceId == 0);\r
504 ASSERT (VgpuGop->BackingStore == NULL);\r
505\r
ac0a286f
MK
506 VgpuGop->ResourceId = NewResourceId;\r
507 VgpuGop->BackingStore = NewBackingStore;\r
508 VgpuGop->NumberOfPages = NewNumberOfPages;\r
f10ae923 509 VgpuGop->BackingStoreMap = NewBackingStoreMap;\r
8731debe
LE
510\r
511 //\r
512 // Populate Mode and ModeInfo (mutable fields only).\r
513 //\r
5f6ecaa3 514 VgpuGop->GopMode.Mode = ModeNumber;\r
3ca7326b 515 CopyMem (&VgpuGop->GopModeInfo, GopModeInfo, sizeof VgpuGop->GopModeInfo);\r
5f6ecaa3 516 FreePool (GopModeInfo);\r
8731debe
LE
517 return EFI_SUCCESS;\r
518\r
519DetachBackingStore:\r
520 Status2 = VirtioGpuResourceDetachBacking (VgpuGop->ParentBus, NewResourceId);\r
521 ASSERT_EFI_ERROR (Status2);\r
522 if (EFI_ERROR (Status2)) {\r
523 CpuDeadLoop ();\r
524 }\r
525\r
f10ae923
LE
526UnmapAndFreeBackingStore:\r
527 VirtioGpuUnmapAndFreeBackingStore (\r
528 VgpuGop->ParentBus, // VgpuDev\r
529 NewNumberOfPages, // NumberOfPages\r
530 NewBackingStore, // HostAddress\r
531 NewBackingStoreMap // Mapping\r
532 );\r
8731debe
LE
533\r
534DestroyHostResource:\r
535 Status2 = VirtioGpuResourceUnref (VgpuGop->ParentBus, NewResourceId);\r
536 ASSERT_EFI_ERROR (Status2);\r
537 if (EFI_ERROR (Status2)) {\r
538 CpuDeadLoop ();\r
539 }\r
540\r
5f6ecaa3 541 FreePool (GopModeInfo);\r
8731debe
LE
542 return Status;\r
543}\r
544\r
545STATIC\r
546EFI_STATUS\r
547EFIAPI\r
548GopBlt (\r
ac0a286f
MK
549 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
550 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer OPTIONAL,\r
551 IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,\r
552 IN UINTN SourceX,\r
553 IN UINTN SourceY,\r
554 IN UINTN DestinationX,\r
555 IN UINTN DestinationY,\r
556 IN UINTN Width,\r
557 IN UINTN Height,\r
558 IN UINTN Delta OPTIONAL\r
8731debe
LE
559 )\r
560{\r
ac0a286f
MK
561 VGPU_GOP *VgpuGop;\r
562 UINT32 CurrentHorizontal;\r
563 UINT32 CurrentVertical;\r
564 UINTN SegmentSize;\r
565 UINTN Y;\r
566 UINTN ResourceOffset;\r
567 EFI_STATUS Status;\r
568\r
569 VgpuGop = VGPU_GOP_FROM_GOP (This);\r
8731debe
LE
570 CurrentHorizontal = VgpuGop->GopModeInfo.HorizontalResolution;\r
571 CurrentVertical = VgpuGop->GopModeInfo.VerticalResolution;\r
572\r
573 //\r
574 // We can avoid pixel format conversion in the guest because the internal\r
575 // representation of EFI_GRAPHICS_OUTPUT_BLT_PIXEL and that of\r
576 // VirtioGpuFormatB8G8R8X8Unorm are identical.\r
577 //\r
578 SegmentSize = Width * sizeof (UINT32);\r
579\r
580 //\r
581 // Delta is relevant for operations that read a rectangle from, or write a\r
582 // rectangle to, BltBuffer.\r
583 //\r
584 // In these cases, Delta is the stride of BltBuffer, in bytes. If Delta is\r
585 // zero, then Width is the entire width of BltBuffer, and the stride is\r
586 // supposed to be calculated from Width.\r
587 //\r
ac0a286f
MK
588 if ((BltOperation == EfiBltVideoToBltBuffer) ||\r
589 (BltOperation == EfiBltBufferToVideo))\r
590 {\r
8731debe
LE
591 if (Delta == 0) {\r
592 Delta = SegmentSize;\r
593 }\r
594 }\r
595\r
596 //\r
597 // For operations that write to the display, check if the destination fits\r
598 // onto the display.\r
599 //\r
ac0a286f
MK
600 if ((BltOperation == EfiBltVideoFill) ||\r
601 (BltOperation == EfiBltBufferToVideo) ||\r
602 (BltOperation == EfiBltVideoToVideo))\r
603 {\r
604 if ((DestinationX > CurrentHorizontal) ||\r
605 (Width > CurrentHorizontal - DestinationX) ||\r
606 (DestinationY > CurrentVertical) ||\r
607 (Height > CurrentVertical - DestinationY))\r
608 {\r
8731debe
LE
609 return EFI_INVALID_PARAMETER;\r
610 }\r
611 }\r
612\r
613 //\r
614 // For operations that read from the display, check if the source fits onto\r
615 // the display.\r
616 //\r
ac0a286f
MK
617 if ((BltOperation == EfiBltVideoToBltBuffer) ||\r
618 (BltOperation == EfiBltVideoToVideo))\r
619 {\r
620 if ((SourceX > CurrentHorizontal) ||\r
621 (Width > CurrentHorizontal - SourceX) ||\r
622 (SourceY > CurrentVertical) ||\r
623 (Height > CurrentVertical - SourceY))\r
624 {\r
8731debe
LE
625 return EFI_INVALID_PARAMETER;\r
626 }\r
627 }\r
628\r
629 //\r
630 // Render the request. For requests that do not modify the display, there\r
631 // won't be further steps.\r
632 //\r
633 switch (BltOperation) {\r
ac0a286f
MK
634 case EfiBltVideoFill:\r
635 //\r
636 // Write data from the BltBuffer pixel (0, 0) directly to every pixel of\r
637 // the video display rectangle (DestinationX, DestinationY) (DestinationX +\r
638 // Width, DestinationY + Height). Only one pixel will be used from the\r
639 // BltBuffer. Delta is NOT used.\r
640 //\r
641 for (Y = 0; Y < Height; ++Y) {\r
642 SetMem32 (\r
643 VgpuGop->BackingStore +\r
8731debe 644 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
ac0a286f
MK
645 SegmentSize,\r
646 *(UINT32 *)BltBuffer\r
647 );\r
648 }\r
8731debe 649\r
ac0a286f 650 break;\r
8731debe 651\r
ac0a286f
MK
652 case EfiBltVideoToBltBuffer:\r
653 //\r
654 // Read data from the video display rectangle (SourceX, SourceY) (SourceX +\r
655 // Width, SourceY + Height) and place it in the BltBuffer rectangle\r
656 // (DestinationX, DestinationY ) (DestinationX + Width, DestinationY +\r
657 // Height). If DestinationX or DestinationY is not zero then Delta must be\r
658 // set to the length in bytes of a row in the BltBuffer.\r
659 //\r
660 for (Y = 0; Y < Height; ++Y) {\r
8731debe 661 CopyMem (\r
ac0a286f
MK
662 (UINT8 *)BltBuffer +\r
663 (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,\r
8731debe 664 VgpuGop->BackingStore +\r
ac0a286f 665 (SourceY + Y) * CurrentHorizontal + SourceX,\r
8731debe
LE
666 SegmentSize\r
667 );\r
668 }\r
ac0a286f
MK
669\r
670 return EFI_SUCCESS;\r
671\r
672 case EfiBltBufferToVideo:\r
673 //\r
674 // Write data from the BltBuffer rectangle (SourceX, SourceY) (SourceX +\r
675 // Width, SourceY + Height) directly to the video display rectangle\r
676 // (DestinationX, DestinationY) (DestinationX + Width, DestinationY +\r
677 // Height). If SourceX or SourceY is not zero then Delta must be set to the\r
678 // length in bytes of a row in the BltBuffer.\r
679 //\r
8731debe
LE
680 for (Y = 0; Y < Height; ++Y) {\r
681 CopyMem (\r
682 VgpuGop->BackingStore +\r
ac0a286f
MK
683 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
684 (UINT8 *)BltBuffer +\r
685 (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,\r
8731debe
LE
686 SegmentSize\r
687 );\r
688 }\r
8731debe 689\r
ac0a286f
MK
690 break;\r
691\r
692 case EfiBltVideoToVideo:\r
693 //\r
694 // Copy from the video display rectangle (SourceX, SourceY) (SourceX +\r
695 // Width, SourceY + Height) to the video display rectangle (DestinationX,\r
696 // DestinationY) (DestinationX + Width, DestinationY + Height). The\r
697 // BltBuffer and Delta are not used in this mode.\r
698 //\r
699 // A single invocation of CopyMem() handles overlap between source and\r
700 // destination (that is, within a single line), but for multiple\r
701 // invocations, we must handle overlaps.\r
702 //\r
703 if (SourceY < DestinationY) {\r
704 Y = Height;\r
705 while (Y > 0) {\r
706 --Y;\r
707 CopyMem (\r
708 VgpuGop->BackingStore +\r
709 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
710 VgpuGop->BackingStore +\r
711 (SourceY + Y) * CurrentHorizontal + SourceX,\r
712 SegmentSize\r
713 );\r
714 }\r
715 } else {\r
716 for (Y = 0; Y < Height; ++Y) {\r
717 CopyMem (\r
718 VgpuGop->BackingStore +\r
719 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
720 VgpuGop->BackingStore +\r
721 (SourceY + Y) * CurrentHorizontal + SourceX,\r
722 SegmentSize\r
723 );\r
724 }\r
725 }\r
726\r
727 break;\r
728\r
729 default:\r
730 return EFI_INVALID_PARAMETER;\r
8731debe
LE
731 }\r
732\r
733 //\r
734 // For operations that wrote to the display, submit the updated area to the\r
735 // host -- update the host resource from guest memory.\r
736 //\r
737 ResourceOffset = sizeof (UINT32) * (DestinationY * CurrentHorizontal +\r
738 DestinationX);\r
739 Status = VirtioGpuTransferToHost2d (\r
740 VgpuGop->ParentBus, // VgpuDev\r
741 (UINT32)DestinationX, // X\r
742 (UINT32)DestinationY, // Y\r
743 (UINT32)Width, // Width\r
744 (UINT32)Height, // Height\r
745 ResourceOffset, // Offset\r
746 VgpuGop->ResourceId // ResourceId\r
747 );\r
748 if (EFI_ERROR (Status)) {\r
749 return Status;\r
750 }\r
751\r
752 //\r
753 // Flush the updated resource to the display.\r
754 //\r
755 Status = VirtioGpuResourceFlush (\r
756 VgpuGop->ParentBus, // VgpuDev\r
757 (UINT32)DestinationX, // X\r
758 (UINT32)DestinationY, // Y\r
759 (UINT32)Width, // Width\r
760 (UINT32)Height, // Height\r
761 VgpuGop->ResourceId // ResourceId\r
762 );\r
763 return Status;\r
764}\r
765\r
766//\r
767// Template for initializing VGPU_GOP.Gop.\r
768//\r
ac0a286f 769CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {\r
8731debe
LE
770 GopQueryMode,\r
771 GopSetMode,\r
772 GopBlt,\r
773 NULL // Mode, to be overwritten in the actual protocol instance\r
774};\r