Gop.c 24 KB

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