1 /*
2  * Copyright 2023 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 <lib/zx/vmo.h>
19 #include <msd-virtio-gpu/magma-virtio-gpu-defs.h>
20 #include <os_dirent.h>
21 #include <services/service_connector.h>
22 
23 #include <climits>
24 #include <cstdio>
25 #include <cstdlib>
26 
27 #include "FuchsiaVirtGpu.h"
28 
FuchsiaVirtGpuDevice(enum VirtGpuCapset capset,magma_device_t device)29 FuchsiaVirtGpuDevice::FuchsiaVirtGpuDevice(enum VirtGpuCapset capset, magma_device_t device)
30     : VirtGpuDevice(capset), device_(device) {
31     memset(&mCaps, 0, sizeof(struct VirtGpuCaps));
32 
33     // Hard-coded values that may be assumed on Fuchsia.
34     mCaps.params[kParam3D] = 1;
35     mCaps.params[kParamCapsetFix] = 1;
36     mCaps.params[kParamResourceBlob] = 1;
37     mCaps.params[kParamHostVisible] = 1;
38     mCaps.params[kParamCrossDevice] = 0;
39     mCaps.params[kParamContextInit] = 1;
40     mCaps.params[kParamSupportedCapsetIds] = 0;
41     mCaps.params[kParamExplicitDebugName] = 0;
42     mCaps.params[kParamCreateGuestHandle] = 0;
43 
44     if (capset == kCapsetGfxStreamVulkan) {
45         uint64_t query_id = kMagmaVirtioGpuQueryCapset;
46         query_id |= static_cast<uint64_t>(kCapsetGfxStreamVulkan) << 32;
47         constexpr uint16_t kVersion = 0;
48         query_id |= static_cast<uint64_t>(kVersion) << 16;
49 
50         magma_handle_t buffer;
51         magma_status_t status = magma_device_query(device_, query_id, &buffer, nullptr);
52         if (status == MAGMA_STATUS_OK) {
53             zx::vmo capset_info(buffer);
54             zx_status_t status =
55                 capset_info.read(&mCaps.vulkanCapset, /*offset=*/0, sizeof(struct vulkanCapset));
56             ALOGD("Got capset result, read status %d", status);
57         } else {
58             ALOGE("Query(%lu) failed: status %d, expected buffer result", query_id, status);
59         }
60 
61         // We always need an ASG blob in some cases, so always define blobAlignment
62         if (!mCaps.vulkanCapset.blobAlignment) {
63             mCaps.vulkanCapset.blobAlignment = 4096;
64         }
65     }
66 }
67 
~FuchsiaVirtGpuDevice()68 FuchsiaVirtGpuDevice::~FuchsiaVirtGpuDevice() { magma_device_release(device_); }
69 
getDeviceHandle(void)70 int64_t FuchsiaVirtGpuDevice::getDeviceHandle(void) { return device_; }
71 
createBlob(const struct VirtGpuCreateBlob & blobCreate)72 VirtGpuResourcePtr FuchsiaVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
73     ALOGE("%s: unimplemented", __func__);
74     return nullptr;
75 }
76 
createResource(uint32_t width,uint32_t height,uint32_t stride,uint32_t virglFormat,uint32_t target,uint32_t bind)77 VirtGpuResourcePtr FuchsiaVirtGpuDevice::createResource(uint32_t width, uint32_t height,
78                                                         uint32_t stride, uint32_t virglFormat,
79                                                         uint32_t target, uint32_t bind) {
80     ALOGE("%s: unimplemented", __func__);
81     return nullptr;
82 }
83 
importBlob(const struct VirtGpuExternalHandle & handle)84 VirtGpuResourcePtr FuchsiaVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle& handle) {
85     ALOGE("%s: unimplemented", __func__);
86     return nullptr;
87 }
88 
execBuffer(struct VirtGpuExecBuffer & execbuffer,const VirtGpuResource * blob)89 int FuchsiaVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
90                                      const VirtGpuResource* blob) {
91     ALOGE("%s: unimplemented", __func__);
92     return 0;
93 }
94 
getCaps(void)95 struct VirtGpuCaps FuchsiaVirtGpuDevice::getCaps(void) { return {}; }
96 
createPlatformVirtGpuDevice(enum VirtGpuCapset capset,int fd)97 VirtGpuDevice* createPlatformVirtGpuDevice(enum VirtGpuCapset capset, int fd) {
98     // We don't handle the VirtioGpuPipeStream case.
99     if (fd >= 0) {
100         ALOGE("Fuchsia: fd not handled");
101         abort();
102         return nullptr;
103     }
104 
105     const char kDevGpu[] = "/loader-gpu-devices/class/gpu";
106 
107     struct os_dirent* de;
108     os_dir_t* dir = os_opendir(kDevGpu);
109     if (!dir) {
110         ALOGE("Error opening %s", kDevGpu);
111         return nullptr;
112     }
113 
114     ALOGD("Opened dir %s", kDevGpu);
115 
116     VirtGpuDevice* gpu_device = nullptr;
117 
118     while ((de = os_readdir(dir)) != NULL) {
119         ALOGD("Got name %s", de->d_name);
120 
121         if (strcmp(de->d_name, ".") == 0) {
122             continue;
123         }
124         // extra +1 ensures space for null termination
125         char name[sizeof(kDevGpu) + sizeof('/') + sizeof(de->d_name) + 1];
126         snprintf(name, sizeof(name), "%s/%s", kDevGpu, de->d_name);
127 
128         zx_handle_t device_channel = GetConnectToServiceFunction()(name);
129         if (device_channel == ZX_HANDLE_INVALID) {
130             ALOGE("Failed to open device: %s", name);
131             continue;
132         }
133 
134         magma_device_t magma_device;
135         magma_status_t status = magma_device_import(device_channel, &magma_device);
136         if (status != MAGMA_STATUS_OK) {
137             ALOGE("magma_device_import failed: %d", status);
138             continue;
139         }
140 
141         gpu_device = new FuchsiaVirtGpuDevice(capset, magma_device);
142         break;
143     }
144     os_closedir(dir);
145 
146     return gpu_device;
147 }
148