]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/VirtioGpuDxe/Commands.c
OvmfPkg/Virtio: take RingBaseShift in SetQueueAddress()
[mirror_edk2.git] / OvmfPkg / VirtioGpuDxe / Commands.c
CommitLineData
c5f235bb
LE
1/** @file\r
2\r
3 VirtIo GPU initialization, and commands (primitives) for the GPU device.\r
4\r
5 Copyright (C) 2016, Red Hat, Inc.\r
fc2c1543 6 Copyright (c) 2017, AMD Inc, All rights reserved.<BR>\r
c5f235bb
LE
7\r
8 This program and the accompanying materials are licensed and made available\r
9 under the terms and conditions of the BSD License which accompanies this\r
10 distribution. The full text of the license may be found at\r
11 http://opensource.org/licenses/bsd-license.php\r
12\r
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
14 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
c5f235bb
LE
18#include <Library/VirtioLib.h>\r
19\r
20#include "VirtioGpu.h"\r
21\r
22/**\r
23 Configure the VirtIo GPU device that underlies VgpuDev.\r
24\r
25 @param[in,out] VgpuDev The VGPU_DEV object to set up VirtIo messaging for.\r
26 On input, the caller is responsible for having\r
27 initialized VgpuDev->VirtIo. On output, VgpuDev->Ring\r
28 has been initialized, and synchronous VirtIo GPU\r
29 commands (primitives) can be submitted to the device.\r
30\r
31 @retval EFI_SUCCESS VirtIo GPU configuration successful.\r
32\r
33 @retval EFI_UNSUPPORTED The host-side configuration of the VirtIo GPU is not\r
34 supported by this driver.\r
35\r
36 @retval Error codes from underlying functions.\r
37**/\r
38EFI_STATUS\r
39VirtioGpuInit (\r
40 IN OUT VGPU_DEV *VgpuDev\r
41 )\r
42{\r
43 UINT8 NextDevStat;\r
44 EFI_STATUS Status;\r
45 UINT64 Features;\r
46 UINT16 QueueSize;\r
47\r
48 //\r
49 // Execute virtio-v1.0-cs04, 3.1.1 Driver Requirements: Device\r
50 // Initialization.\r
51 //\r
52 // 1. Reset the device.\r
53 //\r
54 NextDevStat = 0;\r
55 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
56 if (EFI_ERROR (Status)) {\r
57 goto Failed;\r
58 }\r
59\r
60 //\r
61 // 2. Set the ACKNOWLEDGE status bit [...]\r
62 //\r
63 NextDevStat |= VSTAT_ACK;\r
64 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
65 if (EFI_ERROR (Status)) {\r
66 goto Failed;\r
67 }\r
68\r
69 //\r
70 // 3. Set the DRIVER status bit [...]\r
71 //\r
72 NextDevStat |= VSTAT_DRIVER;\r
73 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
74 if (EFI_ERROR (Status)) {\r
75 goto Failed;\r
76 }\r
77\r
78 //\r
79 // 4. Read device feature bits...\r
80 //\r
81 Status = VgpuDev->VirtIo->GetDeviceFeatures (VgpuDev->VirtIo, &Features);\r
82 if (EFI_ERROR (Status)) {\r
83 goto Failed;\r
84 }\r
85 if ((Features & VIRTIO_F_VERSION_1) == 0) {\r
86 Status = EFI_UNSUPPORTED;\r
87 goto Failed;\r
88 }\r
89 //\r
90 // We only want the most basic 2D features.\r
91 //\r
92 Features &= VIRTIO_F_VERSION_1;\r
93\r
94 //\r
95 // ... and write the subset of feature bits understood by the [...] driver to\r
96 // the device. [...]\r
97 // 5. Set the FEATURES_OK status bit.\r
98 // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]\r
99 //\r
100 Status = Virtio10WriteFeatures (VgpuDev->VirtIo, Features, &NextDevStat);\r
101 if (EFI_ERROR (Status)) {\r
102 goto Failed;\r
103 }\r
104\r
105 //\r
106 // 7. Perform device-specific setup, including discovery of virtqueues for\r
107 // the device [...]\r
108 //\r
109 Status = VgpuDev->VirtIo->SetQueueSel (VgpuDev->VirtIo,\r
110 VIRTIO_GPU_CONTROL_QUEUE);\r
111 if (EFI_ERROR (Status)) {\r
112 goto Failed;\r
113 }\r
114 Status = VgpuDev->VirtIo->GetQueueNumMax (VgpuDev->VirtIo, &QueueSize);\r
115 if (EFI_ERROR (Status)) {\r
116 goto Failed;\r
117 }\r
118\r
119 //\r
120 // We implement each VirtIo GPU command that we use with two descriptors:\r
121 // request, response.\r
122 //\r
123 if (QueueSize < 2) {\r
124 Status = EFI_UNSUPPORTED;\r
125 goto Failed;\r
126 }\r
127\r
128 //\r
129 // [...] population of virtqueues [...]\r
130 //\r
fc2c1543 131 Status = VirtioRingInit (VgpuDev->VirtIo, QueueSize, &VgpuDev->Ring);\r
c5f235bb
LE
132 if (EFI_ERROR (Status)) {\r
133 goto Failed;\r
134 }\r
53a4c604
BS
135 Status = VgpuDev->VirtIo->SetQueueAddress (\r
136 VgpuDev->VirtIo,\r
137 &VgpuDev->Ring,\r
138 0\r
139 );\r
c5f235bb
LE
140 if (EFI_ERROR (Status)) {\r
141 goto ReleaseQueue;\r
142 }\r
143\r
144 //\r
145 // 8. Set the DRIVER_OK status bit.\r
146 //\r
147 NextDevStat |= VSTAT_DRIVER_OK;\r
148 Status = VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
149 if (EFI_ERROR (Status)) {\r
150 goto ReleaseQueue;\r
151 }\r
152\r
153 return EFI_SUCCESS;\r
154\r
155ReleaseQueue:\r
fc2c1543 156 VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);\r
c5f235bb
LE
157\r
158Failed:\r
159 //\r
160 // If any of these steps go irrecoverably wrong, the driver SHOULD set the\r
161 // FAILED status bit to indicate that it has given up on the device (it can\r
162 // reset the device later to restart if desired). [...]\r
163 //\r
164 // VirtIo access failure here should not mask the original error.\r
165 //\r
166 NextDevStat |= VSTAT_FAILED;\r
167 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, NextDevStat);\r
168\r
169 return Status;\r
170}\r
171\r
172/**\r
173 De-configure the VirtIo GPU device that underlies VgpuDev.\r
174\r
175 @param[in,out] VgpuDev The VGPU_DEV object to tear down VirtIo messaging\r
176 for. On input, the caller is responsible for having\r
177 called VirtioGpuInit(). On output, VgpuDev->Ring has\r
178 been uninitialized; VirtIo GPU commands (primitives)\r
179 can no longer be submitted to the device.\r
180**/\r
181VOID\r
182VirtioGpuUninit (\r
183 IN OUT VGPU_DEV *VgpuDev\r
184 )\r
185{\r
186 //\r
187 // Resetting the VirtIo device makes it release its resources and forget its\r
188 // configuration.\r
189 //\r
190 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);\r
fc2c1543 191 VirtioRingUninit (VgpuDev->VirtIo, &VgpuDev->Ring);\r
c5f235bb
LE
192}\r
193\r
194/**\r
195 EFI_EVENT_NOTIFY function for the VGPU_DEV.ExitBoot event. It resets the\r
196 VirtIo device, causing it to release its resources and to forget its\r
197 configuration.\r
198\r
199 This function may only be called (that is, VGPU_DEV.ExitBoot may only be\r
200 signaled) after VirtioGpuInit() returns and before VirtioGpuUninit() is\r
201 called.\r
202\r
203 @param[in] Event Event whose notification function is being invoked.\r
204\r
205 @param[in] Context Pointer to the associated VGPU_DEV object.\r
206**/\r
207VOID\r
208EFIAPI\r
209VirtioGpuExitBoot (\r
210 IN EFI_EVENT Event,\r
211 IN VOID *Context\r
212 )\r
213{\r
214 VGPU_DEV *VgpuDev;\r
215\r
216 VgpuDev = Context;\r
217 VgpuDev->VirtIo->SetDeviceStatus (VgpuDev->VirtIo, 0);\r
218}\r
a66ea3b5
LE
219\r
220/**\r
221 Internal utility function that sends a request to the VirtIo GPU device\r
222 model, awaits the answer from the host, and returns a status.\r
223\r
224 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU\r
225 device. The caller is responsible to have\r
226 successfully invoked VirtioGpuInit() on VgpuDev\r
227 previously, while VirtioGpuUninit() must not have\r
228 been called on VgpuDev.\r
229\r
230 @param[in] RequestType The type of the request. The caller is responsible\r
231 for providing a VirtioGpuCmd* RequestType which, on\r
232 success, elicits a VirtioGpuRespOkNodata response\r
233 from the host.\r
234\r
235 @param[in] Fence Whether to enable fencing for this request. Fencing\r
236 forces the host to complete the command before\r
237 producing a response. If Fence is TRUE, then\r
238 VgpuDev->FenceId is consumed, and incremented.\r
239\r
240 @param[in,out] Header Pointer to the caller-allocated request object. The\r
241 request must start with VIRTIO_GPU_CONTROL_HEADER.\r
242 This function overwrites all fields of Header before\r
243 submitting the request to the host:\r
244\r
245 - it sets Type from RequestType,\r
246\r
247 - it sets Flags and FenceId based on Fence,\r
248\r
249 - it zeroes CtxId and Padding.\r
250\r
251 @param[in] RequestSize Size of the entire caller-allocated request object,\r
252 including the leading VIRTIO_GPU_CONTROL_HEADER.\r
253\r
254 @retval EFI_SUCCESS Operation successful.\r
255\r
256 @retval EFI_DEVICE_ERROR The host rejected the request. The host error\r
257 code has been logged on the EFI_D_ERROR level.\r
258\r
259 @return Codes for unexpected errors in VirtIo\r
260 messaging.\r
261**/\r
262STATIC\r
263EFI_STATUS\r
264VirtioGpuSendCommand (\r
265 IN OUT VGPU_DEV *VgpuDev,\r
266 IN VIRTIO_GPU_CONTROL_TYPE RequestType,\r
267 IN BOOLEAN Fence,\r
268 IN OUT volatile VIRTIO_GPU_CONTROL_HEADER *Header,\r
269 IN UINTN RequestSize\r
270 )\r
271{\r
272 DESC_INDICES Indices;\r
273 volatile VIRTIO_GPU_CONTROL_HEADER Response;\r
274 EFI_STATUS Status;\r
275 UINT32 ResponseSize;\r
276\r
277 //\r
278 // Initialize Header.\r
279 //\r
280 Header->Type = RequestType;\r
281 if (Fence) {\r
282 Header->Flags = VIRTIO_GPU_FLAG_FENCE;\r
283 Header->FenceId = VgpuDev->FenceId++;\r
284 } else {\r
285 Header->Flags = 0;\r
286 Header->FenceId = 0;\r
287 }\r
288 Header->CtxId = 0;\r
289 Header->Padding = 0;\r
290\r
291 ASSERT (RequestSize >= sizeof *Header);\r
7f1bf51b 292 ASSERT (RequestSize <= MAX_UINT32);\r
a66ea3b5
LE
293\r
294 //\r
295 // Compose the descriptor chain.\r
296 //\r
297 VirtioPrepare (&VgpuDev->Ring, &Indices);\r
7f1bf51b 298 VirtioAppendDesc (&VgpuDev->Ring, (UINTN)Header, (UINT32)RequestSize,\r
a66ea3b5
LE
299 VRING_DESC_F_NEXT, &Indices);\r
300 VirtioAppendDesc (&VgpuDev->Ring, (UINTN)&Response, sizeof Response,\r
301 VRING_DESC_F_WRITE, &Indices);\r
302\r
303 //\r
304 // Send the command.\r
305 //\r
306 Status = VirtioFlush (VgpuDev->VirtIo, VIRTIO_GPU_CONTROL_QUEUE,\r
307 &VgpuDev->Ring, &Indices, &ResponseSize);\r
308 if (EFI_ERROR (Status)) {\r
309 return Status;\r
310 }\r
311\r
312 //\r
313 // Parse the response.\r
314 //\r
315 if (ResponseSize != sizeof Response) {\r
316 DEBUG ((EFI_D_ERROR, "%a: malformed response to Request=0x%x\n",\r
317 __FUNCTION__, (UINT32)RequestType));\r
318 return EFI_PROTOCOL_ERROR;\r
319 }\r
320\r
321 if (Response.Type == VirtioGpuRespOkNodata) {\r
322 return EFI_SUCCESS;\r
323 }\r
324\r
325 DEBUG ((EFI_D_ERROR, "%a: Request=0x%x Response=0x%x\n", __FUNCTION__,\r
326 (UINT32)RequestType, Response.Type));\r
327 return EFI_DEVICE_ERROR;\r
328}\r
329\r
330/**\r
331 The following functions send requests to the VirtIo GPU device model, await\r
332 the answer from the host, and return a status. They share the following\r
333 interface details:\r
334\r
335 @param[in,out] VgpuDev The VGPU_DEV object that represents the VirtIo GPU\r
336 device. The caller is responsible to have\r
337 successfully invoked VirtioGpuInit() on VgpuDev\r
338 previously, while VirtioGpuUninit() must not have\r
339 been called on VgpuDev.\r
340\r
341 @retval EFI_INVALID_PARAMETER Invalid command-specific parameters were\r
342 detected by this driver.\r
343\r
344 @retval EFI_SUCCESS Operation successful.\r
345\r
346 @retval EFI_DEVICE_ERROR The host rejected the request. The host error\r
347 code has been logged on the EFI_D_ERROR level.\r
348\r
349 @return Codes for unexpected errors in VirtIo\r
350 messaging.\r
351\r
352 For the command-specific parameters, please consult the GPU Device section of\r
353 the VirtIo 1.0 specification (see references in\r
354 "OvmfPkg/Include/IndustryStandard/VirtioGpu.h").\r
355**/\r
356EFI_STATUS\r
357VirtioGpuResourceCreate2d (\r
358 IN OUT VGPU_DEV *VgpuDev,\r
359 IN UINT32 ResourceId,\r
360 IN VIRTIO_GPU_FORMATS Format,\r
361 IN UINT32 Width,\r
362 IN UINT32 Height\r
363 )\r
364{\r
365 volatile VIRTIO_GPU_RESOURCE_CREATE_2D Request;\r
366\r
367 if (ResourceId == 0) {\r
368 return EFI_INVALID_PARAMETER;\r
369 }\r
370\r
371 Request.ResourceId = ResourceId;\r
372 Request.Format = (UINT32)Format;\r
373 Request.Width = Width;\r
374 Request.Height = Height;\r
375\r
376 return VirtioGpuSendCommand (\r
377 VgpuDev,\r
378 VirtioGpuCmdResourceCreate2d,\r
379 FALSE, // Fence\r
380 &Request.Header,\r
381 sizeof Request\r
382 );\r
383}\r
384\r
385EFI_STATUS\r
386VirtioGpuResourceUnref (\r
387 IN OUT VGPU_DEV *VgpuDev,\r
388 IN UINT32 ResourceId\r
389 )\r
390{\r
391 volatile VIRTIO_GPU_RESOURCE_UNREF Request;\r
392\r
393 if (ResourceId == 0) {\r
394 return EFI_INVALID_PARAMETER;\r
395 }\r
396\r
397 Request.ResourceId = ResourceId;\r
398 Request.Padding = 0;\r
399\r
400 return VirtioGpuSendCommand (\r
401 VgpuDev,\r
402 VirtioGpuCmdResourceUnref,\r
403 FALSE, // Fence\r
404 &Request.Header,\r
405 sizeof Request\r
406 );\r
407}\r
408\r
409EFI_STATUS\r
410VirtioGpuResourceAttachBacking (\r
411 IN OUT VGPU_DEV *VgpuDev,\r
412 IN UINT32 ResourceId,\r
413 IN VOID *FirstBackingPage,\r
414 IN UINTN NumberOfPages\r
415 )\r
416{\r
417 volatile VIRTIO_GPU_RESOURCE_ATTACH_BACKING Request;\r
418\r
419 if (ResourceId == 0) {\r
420 return EFI_INVALID_PARAMETER;\r
421 }\r
422\r
423 Request.ResourceId = ResourceId;\r
424 Request.NrEntries = 1;\r
425 Request.Entry.Addr = (UINTN)FirstBackingPage;\r
426 Request.Entry.Length = (UINT32)EFI_PAGES_TO_SIZE (NumberOfPages);\r
427 Request.Entry.Padding = 0;\r
428\r
429 return VirtioGpuSendCommand (\r
430 VgpuDev,\r
431 VirtioGpuCmdResourceAttachBacking,\r
432 FALSE, // Fence\r
433 &Request.Header,\r
434 sizeof Request\r
435 );\r
436}\r
437\r
438EFI_STATUS\r
439VirtioGpuResourceDetachBacking (\r
440 IN OUT VGPU_DEV *VgpuDev,\r
441 IN UINT32 ResourceId\r
442 )\r
443{\r
444 volatile VIRTIO_GPU_RESOURCE_DETACH_BACKING Request;\r
445\r
446 if (ResourceId == 0) {\r
447 return EFI_INVALID_PARAMETER;\r
448 }\r
449\r
450 Request.ResourceId = ResourceId;\r
451 Request.Padding = 0;\r
452\r
453 //\r
454 // In this case, we set Fence to TRUE, because after this function returns,\r
455 // the caller might reasonably want to repurpose the backing pages\r
456 // immediately. Thus we should ensure that the host releases all references\r
457 // to the backing pages before we return.\r
458 //\r
459 return VirtioGpuSendCommand (\r
460 VgpuDev,\r
461 VirtioGpuCmdResourceDetachBacking,\r
462 TRUE, // Fence\r
463 &Request.Header,\r
464 sizeof Request\r
465 );\r
466}\r
467\r
468EFI_STATUS\r
469VirtioGpuSetScanout (\r
470 IN OUT VGPU_DEV *VgpuDev,\r
471 IN UINT32 X,\r
472 IN UINT32 Y,\r
473 IN UINT32 Width,\r
474 IN UINT32 Height,\r
475 IN UINT32 ScanoutId,\r
476 IN UINT32 ResourceId\r
477 )\r
478{\r
479 volatile VIRTIO_GPU_SET_SCANOUT Request;\r
480\r
481 //\r
482 // Unlike for most other commands, ResourceId=0 is valid; it\r
483 // is used to disable a scanout.\r
484 //\r
485 Request.Rectangle.X = X;\r
486 Request.Rectangle.Y = Y;\r
487 Request.Rectangle.Width = Width;\r
488 Request.Rectangle.Height = Height;\r
489 Request.ScanoutId = ScanoutId;\r
490 Request.ResourceId = ResourceId;\r
491\r
492 return VirtioGpuSendCommand (\r
493 VgpuDev,\r
494 VirtioGpuCmdSetScanout,\r
495 FALSE, // Fence\r
496 &Request.Header,\r
497 sizeof Request\r
498 );\r
499}\r
500\r
501EFI_STATUS\r
502VirtioGpuTransferToHost2d (\r
503 IN OUT VGPU_DEV *VgpuDev,\r
504 IN UINT32 X,\r
505 IN UINT32 Y,\r
506 IN UINT32 Width,\r
507 IN UINT32 Height,\r
508 IN UINT64 Offset,\r
509 IN UINT32 ResourceId\r
510 )\r
511{\r
512 volatile VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D Request;\r
513\r
514 if (ResourceId == 0) {\r
515 return EFI_INVALID_PARAMETER;\r
516 }\r
517\r
518 Request.Rectangle.X = X;\r
519 Request.Rectangle.Y = Y;\r
520 Request.Rectangle.Width = Width;\r
521 Request.Rectangle.Height = Height;\r
522 Request.Offset = Offset;\r
523 Request.ResourceId = ResourceId;\r
524 Request.Padding = 0;\r
525\r
526 return VirtioGpuSendCommand (\r
527 VgpuDev,\r
528 VirtioGpuCmdTransferToHost2d,\r
529 FALSE, // Fence\r
530 &Request.Header,\r
531 sizeof Request\r
532 );\r
533}\r
534\r
535EFI_STATUS\r
536VirtioGpuResourceFlush (\r
537 IN OUT VGPU_DEV *VgpuDev,\r
538 IN UINT32 X,\r
539 IN UINT32 Y,\r
540 IN UINT32 Width,\r
541 IN UINT32 Height,\r
542 IN UINT32 ResourceId\r
543 )\r
544{\r
545 volatile VIRTIO_GPU_RESOURCE_FLUSH Request;\r
546\r
547 if (ResourceId == 0) {\r
548 return EFI_INVALID_PARAMETER;\r
549 }\r
550\r
551 Request.Rectangle.X = X;\r
552 Request.Rectangle.Y = Y;\r
553 Request.Rectangle.Width = Width;\r
554 Request.Rectangle.Height = Height;\r
555 Request.ResourceId = ResourceId;\r
556 Request.Padding = 0;\r
557\r
558 return VirtioGpuSendCommand (\r
559 VgpuDev,\r
560 VirtioGpuCmdResourceFlush,\r
561 FALSE, // Fence\r
562 &Request.Header,\r
563 sizeof Request\r
564 );\r
565}\r