1 /*
2  * Copyright 2023 Google
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "VirtioGpuAddressSpaceStream.h"
7 
8 #include <errno.h>
9 
10 #include "util/log.h"
11 
GetRingParamsFromCapset(enum VirtGpuCapset capset,const VirtGpuCaps & caps,uint32_t & ringSize,uint32_t & bufferSize,uint32_t & blobAlignment)12 static bool GetRingParamsFromCapset(enum VirtGpuCapset capset, const VirtGpuCaps& caps,
13                                     uint32_t& ringSize, uint32_t& bufferSize,
14                                     uint32_t& blobAlignment) {
15     switch (capset) {
16         case kCapsetGfxStreamVulkan:
17             ringSize = caps.vulkanCapset.ringSize;
18             bufferSize = caps.vulkanCapset.bufferSize;
19             blobAlignment = caps.vulkanCapset.blobAlignment;
20             break;
21         case kCapsetGfxStreamMagma:
22             ringSize = caps.magmaCapset.ringSize;
23             bufferSize = caps.magmaCapset.bufferSize;
24             blobAlignment = caps.magmaCapset.blobAlignment;
25             break;
26         case kCapsetGfxStreamGles:
27             ringSize = caps.glesCapset.ringSize;
28             bufferSize = caps.glesCapset.bufferSize;
29             blobAlignment = caps.glesCapset.blobAlignment;
30             break;
31         case kCapsetGfxStreamComposer:
32             ringSize = caps.composerCapset.ringSize;
33             bufferSize = caps.composerCapset.bufferSize;
34             blobAlignment = caps.composerCapset.blobAlignment;
35             break;
36         default:
37             return false;
38     }
39 
40     return true;
41 }
42 
virtgpu_address_space_open()43 address_space_handle_t virtgpu_address_space_open() {
44     return (address_space_handle_t)(-EINVAL);
45 }
46 
virtgpu_address_space_close(address_space_handle_t)47 void virtgpu_address_space_close(address_space_handle_t) {
48     // Handle opened by VirtioGpuDevice wrapper
49 }
50 
virtgpu_address_space_ping(address_space_handle_t,struct address_space_ping * info)51 bool virtgpu_address_space_ping(address_space_handle_t, struct address_space_ping* info) {
52     int ret;
53     struct VirtGpuExecBuffer exec = {};
54     VirtGpuDevice* instance = VirtGpuDevice::getInstance();
55     struct gfxstreamContextPing ping = {};
56 
57     ping.hdr.opCode = GFXSTREAM_CONTEXT_PING;
58     ping.resourceId = info->resourceId;
59 
60     exec.command = static_cast<void*>(&ping);
61     exec.command_size = sizeof(ping);
62 
63     ret = instance->execBuffer(exec, nullptr);
64     if (ret)
65         return false;
66 
67     return true;
68 }
69 
createVirtioGpuAddressSpaceStream(enum VirtGpuCapset capset)70 AddressSpaceStream* createVirtioGpuAddressSpaceStream(enum VirtGpuCapset capset) {
71     VirtGpuResourcePtr pipe, blob;
72     VirtGpuResourceMappingPtr pipeMapping, blobMapping;
73     struct VirtGpuExecBuffer exec = {};
74     struct VirtGpuCreateBlob blobCreate = {};
75     struct gfxstreamContextCreate contextCreate = {};
76 
77     uint32_t ringSize = 0;
78     uint32_t bufferSize = 0;
79     uint32_t blobAlignment = 0;
80 
81     char* blobAddr, *bufferPtr;
82     int ret;
83 
84     VirtGpuDevice* instance = VirtGpuDevice::getInstance();
85     auto caps = instance->getCaps();
86 
87     if (!GetRingParamsFromCapset(capset, caps, ringSize, bufferSize, blobAlignment)) {
88         mesa_loge("Failed to get ring parameters");
89         return nullptr;
90     }
91 
92     blobCreate.blobId = 0;
93     blobCreate.blobMem = kBlobMemHost3d;
94     blobCreate.flags = kBlobFlagMappable;
95     blobCreate.size = ALIGN_POT(ringSize + bufferSize, blobAlignment);
96     blob = instance->createBlob(blobCreate);
97     if (!blob)
98         return nullptr;
99 
100     // Context creation command
101     contextCreate.hdr.opCode = GFXSTREAM_CONTEXT_CREATE;
102     contextCreate.resourceId = blob->getResourceHandle();
103 
104     exec.command = static_cast<void*>(&contextCreate);
105     exec.command_size = sizeof(contextCreate);
106 
107     ret = instance->execBuffer(exec, blob.get());
108     if (ret)
109         return nullptr;
110 
111     // Wait occurs on global timeline -- should we use context specific one?
112     ret = blob->wait();
113     if (ret)
114         return nullptr;
115 
116     blobMapping = blob->createMapping();
117     if (!blobMapping)
118         return nullptr;
119 
120     blobAddr = reinterpret_cast<char*>(blobMapping->asRawPtr());
121 
122     bufferPtr = blobAddr + sizeof(struct asg_ring_storage);
123     struct asg_context context = asg_context_create(blobAddr, bufferPtr, bufferSize);
124 
125     context.ring_config->transfer_mode = 1;
126     context.ring_config->host_consumed_pos = 0;
127     context.ring_config->guest_write_pos = 0;
128 
129     struct address_space_ops ops = {
130         .open = virtgpu_address_space_open,
131         .close = virtgpu_address_space_close,
132         .ping = virtgpu_address_space_ping,
133     };
134 
135     AddressSpaceStream* res =
136         new AddressSpaceStream((address_space_handle_t)(-1), 1, context, 0, 0, ops);
137 
138     res->setMapping(blobMapping);
139     res->setResourceId(contextCreate.resourceId);
140     return res;
141 }
142