1 /*
2 * Copyright 2014 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 */
23
24 /**
25 * \file amdgpu_device.c
26 *
27 * Implementation of functions for AMD GPU device
28 *
29 */
30
31 #include <sys/stat.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38
39 #include "xf86drm.h"
40 #include "amdgpu_drm.h"
41 #include "amdgpu_internal.h"
42 #include "util_math.h"
43
44 #define PTR_TO_UINT(x) ((unsigned)((intptr_t)(x)))
45
46 static pthread_mutex_t dev_mutex = PTHREAD_MUTEX_INITIALIZER;
47 static amdgpu_device_handle dev_list;
48
fd_compare(int fd1,int fd2)49 static int fd_compare(int fd1, int fd2)
50 {
51 char *name1 = drmGetPrimaryDeviceNameFromFd(fd1);
52 char *name2 = drmGetPrimaryDeviceNameFromFd(fd2);
53 int result;
54
55 if (name1 == NULL || name2 == NULL) {
56 free(name1);
57 free(name2);
58 return 0;
59 }
60
61 result = strcmp(name1, name2);
62 free(name1);
63 free(name2);
64
65 return result;
66 }
67
68 /**
69 * Get the authenticated form fd,
70 *
71 * \param fd - \c [in] File descriptor for AMD GPU device
72 * \param auth - \c [out] Pointer to output the fd is authenticated or not
73 * A render node fd, output auth = 0
74 * A legacy fd, get the authenticated for compatibility root
75 *
76 * \return 0 on success\n
77 * >0 - AMD specific error code\n
78 * <0 - Negative POSIX Error code
79 */
amdgpu_get_auth(int fd,int * auth)80 static int amdgpu_get_auth(int fd, int *auth)
81 {
82 int r = 0;
83 drm_client_t client = {};
84
85 if (drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER)
86 *auth = 0;
87 else {
88 client.idx = 0;
89 r = drmIoctl(fd, DRM_IOCTL_GET_CLIENT, &client);
90 if (!r)
91 *auth = client.auth;
92 }
93 return r;
94 }
95
amdgpu_device_free_internal(amdgpu_device_handle dev)96 static void amdgpu_device_free_internal(amdgpu_device_handle dev)
97 {
98 /* Remove dev from dev_list, if it was added there. */
99 if (dev == dev_list) {
100 dev_list = dev->next;
101 } else {
102 for (amdgpu_device_handle node = dev_list; node; node = node->next) {
103 if (node->next == dev) {
104 node->next = dev->next;
105 break;
106 }
107 }
108 }
109
110 close(dev->fd);
111 if ((dev->flink_fd >= 0) && (dev->fd != dev->flink_fd))
112 close(dev->flink_fd);
113
114 amdgpu_vamgr_deinit(&dev->va_mgr.vamgr_32);
115 amdgpu_vamgr_deinit(&dev->va_mgr.vamgr_low);
116 amdgpu_vamgr_deinit(&dev->va_mgr.vamgr_high_32);
117 amdgpu_vamgr_deinit(&dev->va_mgr.vamgr_high);
118 handle_table_fini(&dev->bo_handles);
119 handle_table_fini(&dev->bo_flink_names);
120 pthread_mutex_destroy(&dev->bo_table_mutex);
121 free(dev->marketing_name);
122 free(dev);
123 }
124
125 /**
126 * Assignment between two amdgpu_device pointers with reference counting.
127 *
128 * Usage:
129 * struct amdgpu_device *dst = ... , *src = ...;
130 *
131 * dst = src;
132 * // No reference counting. Only use this when you need to move
133 * // a reference from one pointer to another.
134 *
135 * amdgpu_device_reference(&dst, src);
136 * // Reference counters are updated. dst is decremented and src is
137 * // incremented. dst is freed if its reference counter is 0.
138 */
amdgpu_device_reference(struct amdgpu_device ** dst,struct amdgpu_device * src)139 static void amdgpu_device_reference(struct amdgpu_device **dst,
140 struct amdgpu_device *src)
141 {
142 if (update_references(&(*dst)->refcount, &src->refcount))
143 amdgpu_device_free_internal(*dst);
144 *dst = src;
145 }
146
_amdgpu_device_initialize(int fd,uint32_t * major_version,uint32_t * minor_version,amdgpu_device_handle * device_handle,bool deduplicate_device)147 static int _amdgpu_device_initialize(int fd,
148 uint32_t *major_version,
149 uint32_t *minor_version,
150 amdgpu_device_handle *device_handle,
151 bool deduplicate_device)
152 {
153 struct amdgpu_device *dev = NULL;
154 drmVersionPtr version;
155 int r;
156 int flag_auth = 0;
157 int flag_authexist=0;
158 uint32_t accel_working = 0;
159
160 *device_handle = NULL;
161
162 pthread_mutex_lock(&dev_mutex);
163
164 r = amdgpu_get_auth(fd, &flag_auth);
165 if (r) {
166 fprintf(stderr, "%s: amdgpu_get_auth (1) failed (%i)\n",
167 __func__, r);
168 pthread_mutex_unlock(&dev_mutex);
169 return r;
170 }
171
172 if (deduplicate_device)
173 for (dev = dev_list; dev; dev = dev->next)
174 if (fd_compare(dev->fd, fd) == 0)
175 break;
176
177 if (dev) {
178 r = amdgpu_get_auth(dev->fd, &flag_authexist);
179 if (r) {
180 fprintf(stderr, "%s: amdgpu_get_auth (2) failed (%i)\n",
181 __func__, r);
182 pthread_mutex_unlock(&dev_mutex);
183 return r;
184 }
185 if ((flag_auth) && (!flag_authexist)) {
186 dev->flink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
187 }
188 *major_version = dev->major_version;
189 *minor_version = dev->minor_version;
190 amdgpu_device_reference(device_handle, dev);
191 pthread_mutex_unlock(&dev_mutex);
192 return 0;
193 }
194
195 dev = calloc(1, sizeof(struct amdgpu_device));
196 if (!dev) {
197 fprintf(stderr, "%s: calloc failed\n", __func__);
198 pthread_mutex_unlock(&dev_mutex);
199 return -ENOMEM;
200 }
201
202 dev->fd = -1;
203 dev->flink_fd = -1;
204
205 atomic_set(&dev->refcount, 1);
206
207 version = drmGetVersion(fd);
208 if (version->version_major != 3) {
209 fprintf(stderr, "%s: DRM version is %d.%d.%d but this driver is "
210 "only compatible with 3.x.x.\n",
211 __func__,
212 version->version_major,
213 version->version_minor,
214 version->version_patchlevel);
215 drmFreeVersion(version);
216 r = -EBADF;
217 goto cleanup;
218 }
219
220 dev->fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
221 dev->flink_fd = dev->fd;
222 dev->major_version = version->version_major;
223 dev->minor_version = version->version_minor;
224 drmFreeVersion(version);
225
226 pthread_mutex_init(&dev->bo_table_mutex, NULL);
227
228 /* Check if acceleration is working. */
229 r = amdgpu_query_info(dev, AMDGPU_INFO_ACCEL_WORKING, 4, &accel_working);
230 if (r) {
231 fprintf(stderr, "%s: amdgpu_query_info(ACCEL_WORKING) failed (%i)\n",
232 __func__, r);
233 goto cleanup;
234 }
235 if (!accel_working) {
236 fprintf(stderr, "%s: AMDGPU_INFO_ACCEL_WORKING = 0\n", __func__);
237 r = -EBADF;
238 goto cleanup;
239 }
240
241 r = amdgpu_query_gpu_info_init(dev);
242 if (r) {
243 fprintf(stderr, "%s: amdgpu_query_gpu_info_init failed\n", __func__);
244 goto cleanup;
245 }
246
247 amdgpu_va_manager_init(&dev->va_mgr,
248 dev->dev_info.virtual_address_offset,
249 dev->dev_info.virtual_address_max,
250 dev->dev_info.high_va_offset,
251 dev->dev_info.high_va_max,
252 dev->dev_info.virtual_address_alignment);
253
254 amdgpu_parse_asic_ids(dev);
255
256 *major_version = dev->major_version;
257 *minor_version = dev->minor_version;
258 *device_handle = dev;
259 if (deduplicate_device) {
260 dev->next = dev_list;
261 dev_list = dev;
262 }
263 pthread_mutex_unlock(&dev_mutex);
264
265 return 0;
266
267 cleanup:
268 if (dev->fd >= 0)
269 close(dev->fd);
270 free(dev);
271 pthread_mutex_unlock(&dev_mutex);
272 return r;
273 }
274
amdgpu_device_initialize(int fd,uint32_t * major_version,uint32_t * minor_version,amdgpu_device_handle * device_handle)275 drm_public int amdgpu_device_initialize(int fd,
276 uint32_t *major_version,
277 uint32_t *minor_version,
278 amdgpu_device_handle *device_handle)
279 {
280 return _amdgpu_device_initialize(fd, major_version, minor_version, device_handle, true);
281 }
282
amdgpu_device_initialize2(int fd,bool deduplicate_device,uint32_t * major_version,uint32_t * minor_version,amdgpu_device_handle * device_handle)283 drm_public int amdgpu_device_initialize2(int fd, bool deduplicate_device,
284 uint32_t *major_version,
285 uint32_t *minor_version,
286 amdgpu_device_handle *device_handle)
287 {
288 return _amdgpu_device_initialize(fd, major_version, minor_version, device_handle, deduplicate_device);
289 }
290
amdgpu_device_deinitialize(amdgpu_device_handle dev)291 drm_public int amdgpu_device_deinitialize(amdgpu_device_handle dev)
292 {
293 pthread_mutex_lock(&dev_mutex);
294 amdgpu_device_reference(&dev, NULL);
295 pthread_mutex_unlock(&dev_mutex);
296 return 0;
297 }
298
amdgpu_device_get_fd(amdgpu_device_handle device_handle)299 drm_public int amdgpu_device_get_fd(amdgpu_device_handle device_handle)
300 {
301 return device_handle->fd;
302 }
303
amdgpu_get_marketing_name(amdgpu_device_handle dev)304 drm_public const char *amdgpu_get_marketing_name(amdgpu_device_handle dev)
305 {
306 if (dev->marketing_name)
307 return dev->marketing_name;
308 else
309 return "AMD Radeon Graphics";
310 }
311
amdgpu_query_sw_info(amdgpu_device_handle dev,enum amdgpu_sw_info info,void * value)312 drm_public int amdgpu_query_sw_info(amdgpu_device_handle dev,
313 enum amdgpu_sw_info info,
314 void *value)
315 {
316 uint32_t *val32 = (uint32_t*)value;
317
318 switch (info) {
319 case amdgpu_sw_info_address32_hi:
320 if (dev->va_mgr.vamgr_high_32.va_max)
321 *val32 = (dev->va_mgr.vamgr_high_32.va_max - 1) >> 32;
322 else
323 *val32 = (dev->va_mgr.vamgr_32.va_max - 1) >> 32;
324 return 0;
325 }
326 return -EINVAL;
327 }
328