1 /*
2 * Copyright (C) 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io>
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #ifndef __AGX_IO_H
25 #define __AGX_IO_H
26
27 #include <stdbool.h>
28 #include "agx_bo.h"
29
30 #if __APPLE__
31 #include <mach/mach.h>
32 #include <IOKit/IODataQueueClient.h>
33 #endif
34
35 /*
36 * This file contains necessary defines for the macOS (IOKit) interface to the
37 * AGX accelerator, required to build a userspace graphics driver on macOS.
38 *
39 * They are not used under Linux.
40 *
41 * Information is this file was originally determined independently. More
42 * recently, names have been augmented via the oob_timestamp code sample from
43 * Project Zero [1]
44 *
45 * [1] https://bugs.chromium.org/p/project-zero/issues/detail?id=1986
46 */
47
48 #define AGX_SERVICE_TYPE 0x100005
49
50 enum agx_selector {
51 AGX_SELECTOR_GET_GLOBAL_IDS = 0x6,
52 AGX_SELECTOR_SET_API = 0x7,
53 AGX_SELECTOR_CREATE_COMMAND_QUEUE = 0x8,
54 AGX_SELECTOR_FREE_COMMAND_QUEUE = 0x9,
55 AGX_SELECTOR_ALLOCATE_MEM = 0xA,
56 AGX_SELECTOR_FREE_MEM = 0xB,
57 AGX_SELECTOR_CREATE_SHMEM = 0xF,
58 AGX_SELECTOR_FREE_SHMEM = 0x10,
59 AGX_SELECTOR_CREATE_NOTIFICATION_QUEUE = 0x11,
60 AGX_SELECTOR_FREE_NOTIFICATION_QUEUE = 0x12,
61 AGX_SELECTOR_SUBMIT_COMMAND_BUFFERS = 0x1E,
62 AGX_SELECTOR_GET_VERSION = 0x23,
63 AGX_NUM_SELECTORS = 0x32
64 };
65
66 static const char *selector_table[AGX_NUM_SELECTORS] = {
67 "unk0",
68 "unk1",
69 "unk2",
70 "unk3",
71 "unk4",
72 "unk5",
73 "GET_GLOBAL_IDS",
74 "SET_API",
75 "CREATE_COMMAND_QUEUE",
76 "FREE_COMMAND_QUEUE",
77 "ALLOCATE_MEM",
78 "FREE_MEM",
79 "unkC",
80 "unkD",
81 "unkE",
82 "CREATE_SHMEM",
83 "FREE_SHMEM",
84 "CREATE_NOTIFICATION_QUEUE",
85 "FREE_NOTIFICATION_QUEUE",
86 "unk13",
87 "unk14",
88 "unk15",
89 "unk16",
90 "unk17",
91 "unk18",
92 "unk19",
93 "unk1A",
94 "unk1B",
95 "unk1C",
96 "unk1D",
97 "SUBMIT_COMMAND_BUFFERS",
98 "unk1F",
99 "unk20",
100 "unk21",
101 "unk22",
102 "GET_VERSION",
103 "unk24",
104 "unk25",
105 "unk26",
106 "unk27",
107 "unk28",
108 "unk29",
109 "unk2A",
110 "unk2B",
111 "unk2C",
112 "unk2D",
113 "unk2E",
114 "unk2F",
115 "unk30",
116 "unk31"
117 };
118
119 static inline const char *
wrap_selector_name(uint32_t selector)120 wrap_selector_name(uint32_t selector)
121 {
122 return (selector < AGX_NUM_SELECTORS) ? selector_table[selector] : "unk??";
123 }
124
125 struct agx_create_command_queue_resp {
126 uint64_t id;
127 uint32_t unk2; // 90 0A 08 27
128 uint32_t unk3; // 0
129 } __attribute__((packed));
130
131 struct agx_create_shmem_resp {
132 /* IOAccelDeviceShmemData */
133 void *map;
134 uint32_t size;
135 uint32_t id;
136 } __attribute__((packed));
137
138 struct agx_create_notification_queue_resp {
139 #ifdef __APPLE__
140 IODataQueueMemory *queue;
141 #else
142 void *queue;
143 #endif
144 uint32_t unk2; // 1
145 uint32_t unk3; // 0
146 } __attribute__((packed));
147
148 struct agx_submit_cmdbuf_req {
149 /* IOAccelCommandQueueSubmitArgs_Header */
150 uint32_t unk0;
151 uint32_t count;
152
153 /* IOAccelCommandQueueSubmitArgs_Command */
154 uint32_t command_buffer_shmem_id;
155 uint32_t segment_list_shmem_id;
156 uint64_t unk1B; // 0, new in 12.x
157 uint64_t notify_1;
158 uint64_t notify_2;
159 uint32_t unk2;
160 uint32_t unk3;
161 } __attribute__((packed));
162
163 /* Memory allocation isn't really understood yet. By comparing SHADER/CMDBUF_32
164 * vs everything else, it appears the 0x40000000 bit indicates the GPU VA must
165 * be be in the first 4GiB */
166
167 enum agx_memory_type {
168 AGX_MEMORY_TYPE_NORMAL = 0x00000000, /* used for user allocations */
169 AGX_MEMORY_TYPE_UNK = 0x08000000, /* unknown */
170 AGX_MEMORY_TYPE_CMDBUF_64 = 0x18000000, /* used for command buffer storage */
171 AGX_MEMORY_TYPE_SHADER = 0x48000000, /* used for shader memory, with VA = 0 */
172 AGX_MEMORY_TYPE_CMDBUF_32 = 0x58000000, /* used for command buffers, with VA < 32-bit */
173 AGX_MEMORY_TYPE_FRAMEBUFFER = 0x00888F00, /* used for framebuffer backing */
174 };
175
176 static inline const char *
agx_memory_type_name(uint32_t type)177 agx_memory_type_name(uint32_t type)
178 {
179 switch (type) {
180 case AGX_MEMORY_TYPE_NORMAL: return "normal";
181 case AGX_MEMORY_TYPE_UNK: return "unk";
182 case AGX_MEMORY_TYPE_CMDBUF_64: return "cmdbuf_64";
183 case AGX_MEMORY_TYPE_SHADER: return "shader";
184 case AGX_MEMORY_TYPE_CMDBUF_32: return "cmdbuf_32";
185 case AGX_MEMORY_TYPE_FRAMEBUFFER: return "framebuffer";
186 default: return NULL;
187 }
188 }
189
190 struct agx_allocate_resource_req {
191 uint32_t unk0[5];
192 uint32_t mode;
193 uint32_t unk6[6];
194 uint64_t cpu_fixed;
195 uint64_t cpu_fixed_parent;
196 uint32_t size;
197 uint32_t unk17;
198
199 /* Handle of the parent resource when a suballocation is requested.
200 * Based on an assertion failure, this corresponds to:
201 *
202 * -[IOGPUMetalBuffer initWithPrimaryBuffer:heapIndex:bufferIndex:bufferOffset:length:args:argsSize:]
203 */
204 uint32_t parent;
205
206 uint32_t unk19;
207 uint32_t flags;
208 uint32_t unk21[3];
209 } __attribute__((packed));
210
211 struct agx_allocate_resource_resp {
212 /* Returned GPU virtual address */
213 uint64_t gpu_va;
214
215 /* Returned CPU virtual address */
216 uint64_t cpu;
217
218 uint32_t unk4[3];
219
220 /* Handle used to identify the resource in the segment list */
221 uint32_t handle;
222
223 /* Size of the root resource from which we are allocated. If this is not a
224 * suballocation, this is equal to the size.
225 */
226 uint64_t root_size;
227
228 /* Globally unique identifier for the resource, shown in Instruments */
229 uint32_t guid;
230
231 uint32_t unk11[7];
232
233 /* Maximum size of the suballocation. For a suballocation, this equals:
234 *
235 * sub_size = root_size - (sub_cpu - root_cpu)
236 *
237 * For root allocations, this equals the size.
238 */
239 uint64_t sub_size;
240 } __attribute__((packed));
241
242 struct agx_notification_queue {
243 #ifdef __APPLE__
244 mach_port_t port;
245 IODataQueueMemory *queue;
246 #else
247 unsigned port;
248 void *queue;
249 #endif
250 unsigned id;
251 };
252
253 struct agx_command_queue {
254 unsigned id;
255 struct agx_notification_queue notif;
256 };
257
258 struct agx_map_header {
259 /* IOAccelSegmentListHeader */
260 uint64_t cmdbuf_id; // GUID
261 uint32_t segment_count;
262 uint16_t length;
263 uint16_t unk; // 0x8000
264 uint64_t encoder_id; // GUID
265
266 /* IOAccelSegmentResourceListHeader */
267 uint32_t kernel_commands_start_offset;
268 uint32_t kernel_commands_end_offset;
269 uint32_t padding[2];
270 uint32_t total_resources;
271 uint32_t resource_group_count;
272 } __attribute__((packed));
273
274 /* IOAccelSegmentResourceList_ResourceGroup */
275 struct agx_map_entry {
276 uint32_t resource_id[6];
277 uint32_t resource_unk[6];
278 uint16_t resource_flags[6];
279 uint16_t unka; // ff ff
280 uint16_t resource_count;
281 } __attribute__((packed));
282
283 uint64_t
284 agx_get_global_id(struct agx_device *dev);
285
286 #endif
287