]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - OvmfPkg/VirtioGpuDxe/Gop.c
MdePkg: Add Tls configuration related define
[mirror_edk2.git] / OvmfPkg / VirtioGpuDxe / Gop.c
... / ...
CommitLineData
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
7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
8\r
9**/\r
10\r
11#include <Library/MemoryAllocationLib.h>\r
12#include <Library/PcdLib.h>\r
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
41 IN OUT VGPU_GOP *VgpuGop,\r
42 IN BOOLEAN DisableHead\r
43 )\r
44{\r
45 EFI_STATUS Status;\r
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
62 0,\r
63 0,\r
64 0,\r
65 0, // X, Y, Width, Height\r
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
115 // Unmap and release backing pages.\r
116 //\r
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
123 VgpuGop->BackingStore = NULL;\r
124 VgpuGop->NumberOfPages = 0;\r
125 VgpuGop->BackingStoreMap = NULL;\r
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
138\r
139 VgpuGop->ResourceId = 0;\r
140}\r
141\r
142//\r
143// The resolutions supported by this driver.\r
144//\r
145typedef struct {\r
146 UINT32 Width;\r
147 UINT32 Height;\r
148} GOP_RESOLUTION;\r
149\r
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
165 { 1280, 1024 },\r
166 { 1360, 768 },\r
167 { 1366, 768 },\r
168 { 1400, 1050 },\r
169 { 1440, 900 },\r
170 { 1600, 900 },\r
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
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
237STATIC\r
238VOID\r
239EFIAPI\r
240GopInitialize (\r
241 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This\r
242 )\r
243{\r
244 VGPU_GOP *VgpuGop;\r
245 EFI_STATUS Status;\r
246 UINT32 XRes = 0, YRes = 0, Index;\r
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
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
294}\r
295\r
296//\r
297// EFI_GRAPHICS_OUTPUT_PROTOCOL member functions.\r
298//\r
299STATIC\r
300EFI_STATUS\r
301EFIAPI\r
302GopQueryMode (\r
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
307 )\r
308{\r
309 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *GopModeInfo;\r
310\r
311 if ((Info == NULL) ||\r
312 (SizeOfInfo == NULL) ||\r
313 (ModeNumber >= This->Mode->MaxMode))\r
314 {\r
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
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
334\r
335 *SizeOfInfo = sizeof *GopModeInfo;\r
336 *Info = GopModeInfo;\r
337 return EFI_SUCCESS;\r
338}\r
339\r
340STATIC\r
341EFI_STATUS\r
342EFIAPI\r
343GopSetMode (\r
344 IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,\r
345 IN UINT32 ModeNumber\r
346 )\r
347{\r
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
357\r
358 EFI_STATUS Status;\r
359 EFI_STATUS Status2;\r
360\r
361 if (!This->Mode) {\r
362 // SetMode() call in InitVgpuGop() triggers this.\r
363 GopInitialize (This);\r
364 }\r
365\r
366 Status = GopQueryMode (This, ModeNumber, &SizeOfInfo, &GopModeInfo);\r
367 if (Status != EFI_SUCCESS) {\r
368 return Status;\r
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
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
400 GopModeInfo->HorizontalResolution, // Width\r
401 GopModeInfo->VerticalResolution // Height\r
402 );\r
403 if (EFI_ERROR (Status)) {\r
404 return Status;\r
405 }\r
406\r
407 //\r
408 // Allocate, zero and map guest backing store, for bus master common buffer\r
409 // operation.\r
410 //\r
411 NewNumberOfBytes = GopModeInfo->HorizontalResolution *\r
412 GopModeInfo->VerticalResolution * sizeof (UINT32);\r
413 NewNumberOfPages = EFI_SIZE_TO_PAGES (NewNumberOfBytes);\r
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
421 if (EFI_ERROR (Status)) {\r
422 goto DestroyHostResource;\r
423 }\r
424\r
425 //\r
426 // Attach backing store to the host resource.\r
427 //\r
428 Status = VirtioGpuResourceAttachBacking (\r
429 VgpuGop->ParentBus, // VgpuDev\r
430 NewResourceId, // ResourceId\r
431 NewBackingStoreDeviceAddress, // BackingStoreDeviceAddress\r
432 NewNumberOfPages // NumberOfPages\r
433 );\r
434 if (EFI_ERROR (Status)) {\r
435 goto UnmapAndFreeBackingStore;\r
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
445 GopModeInfo->HorizontalResolution, // Width\r
446 GopModeInfo->VerticalResolution, // Height\r
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
464 GopModeInfo->HorizontalResolution, // Width\r
465 GopModeInfo->VerticalResolution, // Height\r
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
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
482 );\r
483 ASSERT_EFI_ERROR (Status2);\r
484 if (EFI_ERROR (Status2)) {\r
485 CpuDeadLoop ();\r
486 }\r
487\r
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
506 VgpuGop->ResourceId = NewResourceId;\r
507 VgpuGop->BackingStore = NewBackingStore;\r
508 VgpuGop->NumberOfPages = NewNumberOfPages;\r
509 VgpuGop->BackingStoreMap = NewBackingStoreMap;\r
510\r
511 //\r
512 // Populate Mode and ModeInfo (mutable fields only).\r
513 //\r
514 VgpuGop->GopMode.Mode = ModeNumber;\r
515 CopyMem (&VgpuGop->GopModeInfo, GopModeInfo, sizeof VgpuGop->GopModeInfo);\r
516 FreePool (GopModeInfo);\r
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
526UnmapAndFreeBackingStore:\r
527 VirtioGpuUnmapAndFreeBackingStore (\r
528 VgpuGop->ParentBus, // VgpuDev\r
529 NewNumberOfPages, // NumberOfPages\r
530 NewBackingStore, // HostAddress\r
531 NewBackingStoreMap // Mapping\r
532 );\r
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
541 FreePool (GopModeInfo);\r
542 return Status;\r
543}\r
544\r
545STATIC\r
546EFI_STATUS\r
547EFIAPI\r
548GopBlt (\r
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
559 )\r
560{\r
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
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
588 if ((BltOperation == EfiBltVideoToBltBuffer) ||\r
589 (BltOperation == EfiBltBufferToVideo))\r
590 {\r
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
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
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
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
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
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
644 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
645 SegmentSize,\r
646 *(UINT32 *)BltBuffer\r
647 );\r
648 }\r
649\r
650 break;\r
651\r
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
661 CopyMem (\r
662 (UINT8 *)BltBuffer +\r
663 (DestinationY + Y) * Delta + DestinationX * sizeof *BltBuffer,\r
664 VgpuGop->BackingStore +\r
665 (SourceY + Y) * CurrentHorizontal + SourceX,\r
666 SegmentSize\r
667 );\r
668 }\r
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
680 for (Y = 0; Y < Height; ++Y) {\r
681 CopyMem (\r
682 VgpuGop->BackingStore +\r
683 (DestinationY + Y) * CurrentHorizontal + DestinationX,\r
684 (UINT8 *)BltBuffer +\r
685 (SourceY + Y) * Delta + SourceX * sizeof *BltBuffer,\r
686 SegmentSize\r
687 );\r
688 }\r
689\r
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
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
769CONST EFI_GRAPHICS_OUTPUT_PROTOCOL mGopTemplate = {\r
770 GopQueryMode,\r
771 GopSetMode,\r
772 GopBlt,\r
773 NULL // Mode, to be overwritten in the actual protocol instance\r
774};\r