1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <cutils/log.h>
18 #include <fcntl.h>
19 #include <sys/mman.h>
20 #include <unistd.h>
21 #include <xf86drm.h>
22 
23 #include <cerrno>
24 #include <cstring>
25 
26 #include "LinuxVirtGpu.h"
27 #include "virtgpu_drm.h"
28 
LinuxVirtGpuResource(int64_t deviceHandle,uint32_t blobHandle,uint32_t resourceHandle,uint64_t size)29 LinuxVirtGpuResource::LinuxVirtGpuResource(int64_t deviceHandle, uint32_t blobHandle,
30                                            uint32_t resourceHandle, uint64_t size)
31     : mDeviceHandle(deviceHandle),
32       mBlobHandle(blobHandle),
33       mResourceHandle(resourceHandle),
34       mSize(size) {}
35 
~LinuxVirtGpuResource()36 LinuxVirtGpuResource::~LinuxVirtGpuResource() {
37     struct drm_gem_close gem_close {
38         .handle = mBlobHandle, .pad = 0,
39     };
40 
41     int ret = drmIoctl(mDeviceHandle, DRM_IOCTL_GEM_CLOSE, &gem_close);
42     if (ret) {
43         ALOGE("DRM_IOCTL_GEM_CLOSE failed with : [%s, blobHandle %u, resourceHandle: %u]",
44               strerror(errno), mBlobHandle, mResourceHandle);
45     }
46 }
47 
getBlobHandle() const48 uint32_t LinuxVirtGpuResource::getBlobHandle() const { return mBlobHandle; }
49 
getResourceHandle() const50 uint32_t LinuxVirtGpuResource::getResourceHandle() const { return mResourceHandle; }
51 
createMapping()52 VirtGpuResourceMappingPtr LinuxVirtGpuResource::createMapping() {
53     int ret;
54     struct drm_virtgpu_map map {
55         .handle = mBlobHandle, .pad = 0,
56     };
57 
58     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_MAP, &map);
59     if (ret) {
60         ALOGE("DRM_IOCTL_VIRTGPU_MAP failed with %s", strerror(errno));
61         return nullptr;
62     }
63 
64     uint8_t* ptr = static_cast<uint8_t*>(
65         mmap64(nullptr, mSize, PROT_WRITE | PROT_READ, MAP_SHARED, mDeviceHandle, map.offset));
66 
67     if (ptr == MAP_FAILED) {
68         ALOGE("mmap64 failed with (%s)", strerror(errno));
69         return nullptr;
70     }
71 
72     return std::make_shared<LinuxVirtGpuResourceMapping>(shared_from_this(), ptr, mSize);
73 }
74 
exportBlob(struct VirtGpuExternalHandle & handle)75 int LinuxVirtGpuResource::exportBlob(struct VirtGpuExternalHandle& handle) {
76     int ret, fd;
77 
78     uint32_t flags = DRM_CLOEXEC;
79     ret = drmPrimeHandleToFD(mDeviceHandle, mBlobHandle, flags, &fd);
80     if (ret) {
81         ALOGE("drmPrimeHandleToFD failed with %s", strerror(errno));
82         return ret;
83     }
84 
85     handle.osHandle = static_cast<int64_t>(fd);
86     handle.type = kMemHandleDmabuf;
87     return 0;
88 }
89 
wait()90 int LinuxVirtGpuResource::wait() {
91     int ret;
92     struct drm_virtgpu_3d_wait wait_3d = {0};
93 
94     int retry = 0;
95     do {
96         if (retry > 0 && (retry % 10 == 0)) {
97             ALOGE("DRM_IOCTL_VIRTGPU_WAIT failed with EBUSY for %d times.", retry);
98         }
99         wait_3d.handle = mBlobHandle;
100         ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_WAIT, &wait_3d);
101         ++retry;
102     } while (ret < 0 && errno == EBUSY);
103 
104     if (ret < 0) {
105         ALOGE("DRM_IOCTL_VIRTGPU_WAIT failed with %s", strerror(errno));
106         return ret;
107     }
108 
109     return 0;
110 }
111 
transferToHost(uint32_t x,uint32_t y,uint32_t w,uint32_t h)112 int LinuxVirtGpuResource::transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
113     int ret;
114     struct drm_virtgpu_3d_transfer_to_host xfer = {0};
115 
116     xfer.box.x = x;
117     xfer.box.y = y;
118     xfer.box.w = w;
119     xfer.box.h = h;
120     xfer.box.d = 1;
121     xfer.bo_handle = mBlobHandle;
122 
123     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST, &xfer);
124     if (ret < 0) {
125         ALOGE("DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST failed with %s", strerror(errno));
126         return ret;
127     }
128 
129     return 0;
130 }
131 
transferFromHost(uint32_t x,uint32_t y,uint32_t w,uint32_t h)132 int LinuxVirtGpuResource::transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
133     int ret;
134     struct drm_virtgpu_3d_transfer_from_host xfer = {0};
135 
136     xfer.box.x = x;
137     xfer.box.y = y;
138     xfer.box.w = w;
139     xfer.box.h = h;
140     xfer.box.d = 1;
141     xfer.bo_handle = mBlobHandle;
142 
143     ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST, &xfer);
144     if (ret < 0) {
145         ALOGE("DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST failed with %s", strerror(errno));
146         return ret;
147     }
148 
149     return 0;
150 }
151