xref: /aosp_15_r20/external/mesa3d/src/gfxstream/guest/platform/linux/LinuxVirtGpuDevice.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2022 Google
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include <fcntl.h>
7 #include <pthread.h>
8 #include <stdlib.h>
9 #include <sys/mman.h>
10 #include <unistd.h>
11 #include <xf86drm.h>
12 
13 #include <cerrno>
14 #include <cstring>
15 #include <fstream>
16 #include <string>
17 
18 #include "LinuxVirtGpu.h"
19 #include "drm-uapi/virtgpu_drm.h"
20 #include "util/log.h"
21 #include "virtgpu_gfxstream_protocol.h"
22 
23 #define VIRTGPU_PARAM_CREATE_FENCE_PASSING 9  /* Fence passing */
24 #define VIRTGPU_PARAM_CREATE_GUEST_HANDLE 10  /* Host OS handle can be created from guest memory. */
25 
26 #define PARAM(x) \
27     (struct VirtGpuParam) { x, #x, 0 }
28 
align_up(uint32_t n,uint32_t a)29 static inline uint32_t align_up(uint32_t n, uint32_t a) { return ((n + a - 1) / a) * a; }
30 
LinuxVirtGpuDevice(enum VirtGpuCapset capset,int32_t descriptor)31 LinuxVirtGpuDevice::LinuxVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor)
32     : VirtGpuDevice(capset) {
33     struct VirtGpuParam params[] = {
34         PARAM(VIRTGPU_PARAM_3D_FEATURES),          PARAM(VIRTGPU_PARAM_CAPSET_QUERY_FIX),
35         PARAM(VIRTGPU_PARAM_RESOURCE_BLOB),        PARAM(VIRTGPU_PARAM_HOST_VISIBLE),
36         PARAM(VIRTGPU_PARAM_CROSS_DEVICE),         PARAM(VIRTGPU_PARAM_CONTEXT_INIT),
37         PARAM(VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs), PARAM(VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME),
38         PARAM(VIRTGPU_PARAM_CREATE_FENCE_PASSING),
39         PARAM(VIRTGPU_PARAM_CREATE_GUEST_HANDLE),
40     };
41 
42     int ret;
43     struct drm_virtgpu_get_caps get_caps = {0};
44     struct drm_virtgpu_context_init init = {0};
45     struct drm_virtgpu_context_set_param ctx_set_params[3] = {{0}};
46     const char* processName = nullptr;
47 
48     memset(&mCaps, 0, sizeof(struct VirtGpuCaps));
49 
50 #ifdef __ANDROID__
51     processName = getprogname();
52 #endif
53 
54     if (descriptor < 0) {
55         mDeviceHandle = static_cast<int64_t>(drmOpenRender(128));
56         if (mDeviceHandle < 0) {
57             mesa_loge("Failed to open rendernode: %s", strerror(errno));
58             return;
59         }
60     } else {
61         mDeviceHandle = dup(descriptor);
62         if (mDeviceHandle < 0) {
63             mesa_loge("Failed to dup rendernode: %s", strerror(errno));
64             return;
65         }
66     }
67 
68     for (uint32_t i = 0; i < kParamMax; i++) {
69         struct drm_virtgpu_getparam get_param = {0};
70         get_param.param = params[i].param;
71         get_param.value = (uint64_t)(uintptr_t)&params[i].value;
72 
73         ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_GETPARAM, &get_param);
74         if (ret) {
75             mesa_logi("virtgpu backend not enabling %s", params[i].name);
76             continue;
77         }
78 
79         mCaps.params[i] = params[i].value;
80     }
81 
82     get_caps.cap_set_id = static_cast<uint32_t>(capset);
83     switch (capset) {
84         case kCapsetGfxStreamVulkan:
85             get_caps.size = sizeof(struct vulkanCapset);
86             get_caps.addr = (unsigned long long)&mCaps.vulkanCapset;
87             break;
88         case kCapsetGfxStreamMagma:
89             get_caps.size = sizeof(struct magmaCapset);
90             get_caps.addr = (unsigned long long)&mCaps.magmaCapset;
91             break;
92         case kCapsetGfxStreamGles:
93             get_caps.size = sizeof(struct vulkanCapset);
94             get_caps.addr = (unsigned long long)&mCaps.glesCapset;
95             break;
96         case kCapsetGfxStreamComposer:
97             get_caps.size = sizeof(struct vulkanCapset);
98             get_caps.addr = (unsigned long long)&mCaps.composerCapset;
99             break;
100         default:
101             get_caps.size = 0;
102     }
103 
104     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_GET_CAPS, &get_caps);
105     if (ret) {
106         // Don't fail get capabilities just yet, AEMU doesn't use this API
107         // yet (b/272121235);
108         mesa_loge("DRM_IOCTL_VIRTGPU_GET_CAPS failed with %s", strerror(errno));
109     }
110 
111     // We always need an ASG blob in some cases, so always define blobAlignment
112     if (!mCaps.vulkanCapset.blobAlignment) {
113         mCaps.vulkanCapset.blobAlignment = 4096;
114     }
115 
116     ctx_set_params[0].param = VIRTGPU_CONTEXT_PARAM_NUM_RINGS;
117     ctx_set_params[0].value = 2;
118     init.num_params = 1;
119 
120     if (capset != kCapsetNone) {
121         ctx_set_params[init.num_params].param = VIRTGPU_CONTEXT_PARAM_CAPSET_ID;
122         ctx_set_params[init.num_params].value = static_cast<uint32_t>(capset);
123         init.num_params++;
124     }
125 
126     if (mCaps.params[kParamExplicitDebugName] && processName) {
127         ctx_set_params[init.num_params].param = VIRTGPU_CONTEXT_PARAM_DEBUG_NAME;
128         ctx_set_params[init.num_params].value = reinterpret_cast<uint64_t>(processName);
129         init.num_params++;
130     }
131 
132     init.ctx_set_params = (unsigned long long)&ctx_set_params[0];
133     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_CONTEXT_INIT, &init);
134     if (ret) {
135         mesa_loge("DRM_IOCTL_VIRTGPU_CONTEXT_INIT failed with %s, continuing without context...",
136                   strerror(errno));
137     }
138 }
139 
~LinuxVirtGpuDevice()140 LinuxVirtGpuDevice::~LinuxVirtGpuDevice() { close(mDeviceHandle); }
141 
getCaps(void)142 struct VirtGpuCaps LinuxVirtGpuDevice::getCaps(void) { return mCaps; }
143 
getDeviceHandle(void)144 int64_t LinuxVirtGpuDevice::getDeviceHandle(void) { return mDeviceHandle; }
145 
createResource(uint32_t width,uint32_t height,uint32_t stride,uint32_t size,uint32_t virglFormat,uint32_t target,uint32_t bind)146 VirtGpuResourcePtr LinuxVirtGpuDevice::createResource(uint32_t width, uint32_t height,
147                                                       uint32_t stride, uint32_t size,
148                                                       uint32_t virglFormat, uint32_t target,
149                                                       uint32_t bind) {
150     drm_virtgpu_resource_create create = {
151         .target = target,
152         .format = virglFormat,
153         .bind = bind,
154         .width = width,
155         .height = height,
156         .depth = 1U,
157         .array_size = 1U,
158         .last_level = 0,
159         .nr_samples = 0,
160         .size = size,
161         .stride = stride,
162     };
163 
164     int ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE, &create);
165     if (ret) {
166         mesa_loge("DRM_IOCTL_VIRTGPU_RESOURCE_CREATE failed with %s", strerror(errno));
167         return nullptr;
168     }
169 
170     return std::make_shared<LinuxVirtGpuResource>(
171         mDeviceHandle, create.bo_handle, create.res_handle, static_cast<uint64_t>(create.size));
172 }
173 
createBlob(const struct VirtGpuCreateBlob & blobCreate)174 VirtGpuResourcePtr LinuxVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
175     int ret;
176     struct drm_virtgpu_resource_create_blob create = {0};
177 
178     create.size = blobCreate.size;
179     create.blob_mem = blobCreate.blobMem;
180     create.blob_flags = blobCreate.flags;
181     create.blob_id = blobCreate.blobId;
182     create.cmd = (uint64_t)(uintptr_t)blobCreate.blobCmd;
183     create.cmd_size = blobCreate.blobCmdSize;
184 
185     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB, &create);
186     if (ret < 0) {
187         mesa_loge("DRM_VIRTGPU_RESOURCE_CREATE_BLOB failed with %s", strerror(errno));
188         return nullptr;
189     }
190 
191     return std::make_shared<LinuxVirtGpuResource>(mDeviceHandle, create.bo_handle,
192                                                   create.res_handle, blobCreate.size);
193 }
194 
importBlob(const struct VirtGpuExternalHandle & handle)195 VirtGpuResourcePtr LinuxVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle& handle) {
196     struct drm_virtgpu_resource_info info = {0};
197     uint32_t blobHandle;
198     int ret;
199 
200     ret = drmPrimeFDToHandle(mDeviceHandle, handle.osHandle, &blobHandle);
201     close(handle.osHandle);
202     if (ret) {
203         mesa_loge("DRM_IOCTL_PRIME_FD_TO_HANDLE failed: %s", strerror(errno));
204         return nullptr;
205     }
206 
207     info.bo_handle = blobHandle;
208     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, &info);
209     if (ret) {
210         mesa_loge("DRM_IOCTL_VIRTGPU_RESOURCE_INFO failed: %s", strerror(errno));
211         return nullptr;
212     }
213 
214     return std::make_shared<LinuxVirtGpuResource>(mDeviceHandle, blobHandle, info.res_handle,
215                                                   static_cast<uint64_t>(info.size));
216 }
217 
execBuffer(struct VirtGpuExecBuffer & execbuffer,const VirtGpuResource * blob)218 int LinuxVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
219                                    const VirtGpuResource* blob) {
220     int ret;
221     struct drm_virtgpu_execbuffer exec = {0};
222     uint32_t blobHandle;
223 
224     exec.flags = execbuffer.flags;
225     exec.size = execbuffer.command_size;
226     exec.ring_idx = execbuffer.ring_idx;
227     exec.command = (uint64_t)(uintptr_t)(execbuffer.command);
228     exec.fence_fd = -1;
229 
230     if (blob) {
231         blobHandle = blob->getBlobHandle();
232         exec.bo_handles = (uint64_t)(uintptr_t)(&blobHandle);
233         exec.num_bo_handles = 1;
234     }
235 
236     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_EXECBUFFER, &exec);
237     if (ret) {
238         mesa_loge("DRM_IOCTL_VIRTGPU_EXECBUFFER failed: %s", strerror(errno));
239         return ret;
240     }
241 
242     if (execbuffer.flags & kFenceOut) {
243         execbuffer.handle.osHandle = exec.fence_fd;
244         execbuffer.handle.type = kFenceHandleSyncFd;
245     }
246 
247     return 0;
248 }
249 
osCreateVirtGpuDevice(enum VirtGpuCapset capset,int32_t descriptor)250 VirtGpuDevice* osCreateVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor) {
251     return new LinuxVirtGpuDevice(capset, descriptor);
252 }
253