1 /*
2 * Copyright © 2023 Intel Corporation
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 (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "xe/intel_device_info.h"
25
26 #include "common/intel_gem.h"
27 #include "dev/intel_device_info.h"
28 #include "dev/intel_hwconfig.h"
29
30 #include "util/log.h"
31
32 #include "drm-uapi/xe_drm.h"
33
34 static inline bool
has_gmd_ip_version(const struct intel_device_info * devinfo)35 has_gmd_ip_version(const struct intel_device_info *devinfo)
36 {
37 return devinfo->verx10 >= 200;
38 }
39
40 static void *
xe_query_alloc_fetch(int fd,uint32_t query_id,int32_t * len)41 xe_query_alloc_fetch(int fd, uint32_t query_id, int32_t *len)
42 {
43 struct drm_xe_device_query query = {
44 .query = query_id,
45 };
46 if (intel_ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query))
47 return NULL;
48
49 void *data = calloc(1, query.size);
50 if (!data)
51 return NULL;
52
53 query.data = (uintptr_t)data;
54 if (intel_ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query))
55 goto data_query_failed;
56
57 if (len)
58 *len = query.size;
59 return data;
60
61 data_query_failed:
62 free(data);
63 return NULL;
64 }
65
66 static bool
xe_query_config(int fd,struct intel_device_info * devinfo)67 xe_query_config(int fd, struct intel_device_info *devinfo)
68 {
69 struct drm_xe_query_config *config;
70 config = xe_query_alloc_fetch(fd, DRM_XE_DEVICE_QUERY_CONFIG, NULL);
71 if (!config)
72 return false;
73
74 if (config->info[DRM_XE_QUERY_CONFIG_FLAGS] & DRM_XE_QUERY_CONFIG_FLAG_HAS_VRAM)
75 devinfo->has_local_mem = true;
76
77 if (!has_gmd_ip_version(devinfo))
78 devinfo->revision = (config->info[DRM_XE_QUERY_CONFIG_REV_AND_DEVICE_ID] >> 16) & 0xFFFF;
79 devinfo->gtt_size = 1ull << config->info[DRM_XE_QUERY_CONFIG_VA_BITS];
80 devinfo->mem_alignment = config->info[DRM_XE_QUERY_CONFIG_MIN_ALIGNMENT];
81
82 free(config);
83 return true;
84 }
85
86 bool
intel_device_info_xe_query_regions(int fd,struct intel_device_info * devinfo,bool update)87 intel_device_info_xe_query_regions(int fd, struct intel_device_info *devinfo,
88 bool update)
89 {
90 struct drm_xe_query_mem_regions *regions;
91 regions = xe_query_alloc_fetch(fd, DRM_XE_DEVICE_QUERY_MEM_REGIONS, NULL);
92 if (!regions)
93 return false;
94
95 for (int i = 0; i < regions->num_mem_regions; i++) {
96 struct drm_xe_mem_region *region = ®ions->mem_regions[i];
97
98 switch (region->mem_class) {
99 case DRM_XE_MEM_REGION_CLASS_SYSMEM: {
100 if (!update) {
101 devinfo->mem.sram.mem.klass = region->mem_class;
102 devinfo->mem.sram.mem.instance = region->instance;
103 devinfo->mem.sram.mappable.size = region->total_size;
104 } else {
105 assert(devinfo->mem.sram.mem.klass == region->mem_class);
106 assert(devinfo->mem.sram.mem.instance == region->instance);
107 assert(devinfo->mem.sram.mappable.size == region->total_size);
108 }
109 /* if running without elevated privileges Xe reports used == 0 */
110 devinfo->mem.sram.mappable.free = region->total_size - region->used;
111 break;
112 }
113 case DRM_XE_MEM_REGION_CLASS_VRAM: {
114 if (!update) {
115 devinfo->mem.vram.mem.klass = region->mem_class;
116 devinfo->mem.vram.mem.instance = region->instance;
117 devinfo->mem.vram.mappable.size = region->cpu_visible_size;
118 devinfo->mem.vram.unmappable.size = region->total_size - region->cpu_visible_size;
119 } else {
120 assert(devinfo->mem.vram.mem.klass == region->mem_class);
121 assert(devinfo->mem.vram.mem.instance == region->instance);
122 assert(devinfo->mem.vram.mappable.size == region->cpu_visible_size);
123 assert(devinfo->mem.vram.unmappable.size == (region->total_size - region->cpu_visible_size));
124 }
125 devinfo->mem.vram.mappable.free = devinfo->mem.vram.mappable.size - region->cpu_visible_used;
126 devinfo->mem.vram.unmappable.free = devinfo->mem.vram.unmappable.size - (region->used - region->cpu_visible_used);
127 break;
128 }
129 default:
130 mesa_loge("Unhandled Xe memory class");
131 break;
132 }
133 }
134
135 devinfo->mem.use_class_instance = true;
136 free(regions);
137 return true;
138 }
139
140 static bool
xe_query_gts(int fd,struct intel_device_info * devinfo)141 xe_query_gts(int fd, struct intel_device_info *devinfo)
142 {
143 struct drm_xe_query_gt_list *gt_list;
144 gt_list = xe_query_alloc_fetch(fd, DRM_XE_DEVICE_QUERY_GT_LIST, NULL);
145 if (!gt_list)
146 return false;
147
148 for (uint32_t i = 0; i < gt_list->num_gt; i++) {
149 if (gt_list->gt_list[i].type == DRM_XE_QUERY_GT_TYPE_MAIN) {
150 devinfo->timestamp_frequency = gt_list->gt_list[i].reference_clock;
151
152 if (has_gmd_ip_version(devinfo)) {
153 devinfo->gfx_ip_ver = GFX_IP_VER(gt_list->gt_list[i].ip_ver_major,
154 gt_list->gt_list[i].ip_ver_minor);
155 devinfo->revision = gt_list->gt_list[i].ip_ver_rev;
156 }
157 break;
158 }
159 }
160
161 free(gt_list);
162 return true;
163 }
164
165 void *
intel_device_info_xe_query_hwconfig(int fd,int32_t * len)166 intel_device_info_xe_query_hwconfig(int fd, int32_t *len)
167 {
168 return xe_query_alloc_fetch(fd, DRM_XE_DEVICE_QUERY_HWCONFIG, len);
169 }
170
171 static bool
xe_query_process_hwconfig(int fd,struct intel_device_info * devinfo)172 xe_query_process_hwconfig(int fd, struct intel_device_info *devinfo)
173 {
174 int32_t len;
175 void *data = intel_device_info_xe_query_hwconfig(fd, &len);
176
177 if (!data)
178 return false;
179
180 bool ret = intel_hwconfig_process_table(devinfo, data, len);
181 free(data);
182 return ret;
183 }
184
185 static void
xe_compute_topology(struct intel_device_info * devinfo,const uint8_t * geo_dss_mask,const uint32_t geo_dss_num_bytes,const uint32_t * eu_per_dss_mask)186 xe_compute_topology(struct intel_device_info * devinfo,
187 const uint8_t *geo_dss_mask,
188 const uint32_t geo_dss_num_bytes,
189 const uint32_t *eu_per_dss_mask)
190 {
191 intel_device_info_topology_reset_masks(devinfo);
192 /* TGL/DG1/ADL-P: 1 slice x 6 dual sub slices
193 * RKL/ADL-S: 1 slice x 2 dual sub slices
194 * DG2: 8 slices x 4 dual sub slices
195 */
196 if (devinfo->verx10 >= 125) {
197 devinfo->max_slices = 8;
198 devinfo->max_subslices_per_slice = 4;
199 } else {
200 devinfo->max_slices = 1;
201 devinfo->max_subslices_per_slice = 6;
202 }
203 devinfo->max_eus_per_subslice = 16;
204 devinfo->subslice_slice_stride = DIV_ROUND_UP(devinfo->max_slices, 8);
205 devinfo->eu_slice_stride = DIV_ROUND_UP(devinfo->max_eus_per_subslice * devinfo->max_subslices_per_slice, 8);
206 devinfo->eu_subslice_stride = DIV_ROUND_UP(devinfo->max_eus_per_subslice, 8);
207
208 assert((sizeof(uint32_t) * 8) >= devinfo->max_subslices_per_slice);
209 assert((sizeof(uint32_t) * 8) >= devinfo->max_eus_per_subslice);
210
211 const uint32_t dss_mask_in_slice = (1u << devinfo->max_subslices_per_slice) - 1;
212 struct slice {
213 uint32_t dss_mask;
214 struct {
215 bool enabled;
216 uint32_t eu_mask;
217 } dual_subslice[INTEL_DEVICE_MAX_SUBSLICES];
218 } slices[INTEL_DEVICE_MAX_SLICES] = {};
219
220 /* Compute and fill slices */
221 for (unsigned s = 0; s < devinfo->max_slices; s++) {
222 const unsigned first_bit = s * devinfo->max_subslices_per_slice;
223 const unsigned dss_index = first_bit / 8;
224 const unsigned shift = first_bit % 8;
225
226 assert(geo_dss_num_bytes > dss_index);
227
228 const uint32_t *dss_mask_ptr = (const uint32_t *)&geo_dss_mask[dss_index];
229 uint32_t dss_mask = *dss_mask_ptr;
230 dss_mask >>= shift;
231 dss_mask &= dss_mask_in_slice;
232
233 if (dss_mask) {
234 slices[s].dss_mask = dss_mask;
235 for (uint32_t dss = 0; dss < devinfo->max_subslices_per_slice; dss++) {
236 if ((1u << dss) & slices[s].dss_mask) {
237 slices[s].dual_subslice[dss].enabled = true;
238 slices[s].dual_subslice[dss].eu_mask = *eu_per_dss_mask;
239 }
240 }
241 }
242 }
243
244 /* Set devinfo masks */
245 for (unsigned s = 0; s < devinfo->max_slices; s++) {
246 if (!slices[s].dss_mask)
247 continue;
248
249 devinfo->slice_masks |= (1u << s);
250
251 for (unsigned ss = 0; ss < devinfo->max_subslices_per_slice; ss++) {
252 if (!slices[s].dual_subslice[ss].eu_mask)
253 continue;
254
255 devinfo->subslice_masks[s * devinfo->subslice_slice_stride +
256 ss / 8] |= (1u << (ss % 8));
257
258 for (unsigned eu = 0; eu < devinfo->max_eus_per_subslice; eu++) {
259 if (!(slices[s].dual_subslice[ss].eu_mask & (1u << eu)))
260 continue;
261
262 devinfo->eu_masks[s * devinfo->eu_slice_stride +
263 ss * devinfo->eu_subslice_stride +
264 eu / 8] |= (1u << (eu % 8));
265 }
266 }
267
268 }
269
270 intel_device_info_topology_update_counts(devinfo);
271 intel_device_info_update_pixel_pipes(devinfo, devinfo->subslice_masks);
272 intel_device_info_update_l3_banks(devinfo);
273 }
274
275 static bool
xe_query_topology(int fd,struct intel_device_info * devinfo)276 xe_query_topology(int fd, struct intel_device_info *devinfo)
277 {
278 struct drm_xe_query_topology_mask *topology;
279 int32_t len;
280 topology = xe_query_alloc_fetch(fd, DRM_XE_DEVICE_QUERY_GT_TOPOLOGY, &len);
281 if (!topology)
282 return false;
283
284 uint32_t geo_dss_num_bytes = 0, *eu_per_dss_mask = NULL;
285 uint8_t *geo_dss_mask = NULL, *tmp;
286 const struct drm_xe_query_topology_mask *head = topology;
287
288 tmp = (uint8_t *)topology + len;
289 const struct drm_xe_query_topology_mask *end = (struct drm_xe_query_topology_mask *)tmp;
290
291 while (topology < end) {
292 if (topology->gt_id == 0) {
293 switch (topology->type) {
294 case DRM_XE_TOPO_DSS_GEOMETRY:
295 geo_dss_mask = topology->mask;
296 geo_dss_num_bytes = topology->num_bytes;
297 break;
298 case DRM_XE_TOPO_EU_PER_DSS:
299 case DRM_XE_TOPO_SIMD16_EU_PER_DSS:
300 eu_per_dss_mask = (uint32_t *)topology->mask;
301 break;
302 }
303 }
304
305 topology = (struct drm_xe_query_topology_mask *)&topology->mask[topology->num_bytes];
306 }
307
308 bool ret = true;
309 if (!geo_dss_num_bytes || !geo_dss_mask || !eu_per_dss_mask) {
310 ret = false;
311 goto parse_failed;
312 }
313
314 xe_compute_topology(devinfo, geo_dss_mask, geo_dss_num_bytes, eu_per_dss_mask);
315
316 parse_failed:
317 free((void *)head);
318 return ret;
319 }
320
321 bool
intel_device_info_xe_get_info_from_fd(int fd,struct intel_device_info * devinfo)322 intel_device_info_xe_get_info_from_fd(int fd, struct intel_device_info *devinfo)
323 {
324 if (!intel_device_info_xe_query_regions(fd, devinfo, false))
325 return false;
326
327 if (!xe_query_config(fd, devinfo))
328 return false;
329
330 if (!xe_query_gts(fd, devinfo))
331 return false;
332
333 if (xe_query_process_hwconfig(fd, devinfo))
334 intel_device_info_update_after_hwconfig(devinfo);
335
336 if (!xe_query_topology(fd, devinfo))
337 return false;
338
339 devinfo->has_context_isolation = true;
340 devinfo->has_mmap_offset = true;
341 devinfo->has_caching_uapi = false;
342 devinfo->has_set_pat_uapi = true;
343
344 return true;
345 }
346