• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #ifndef MSM_PROTO_H_
7 #define MSM_PROTO_H_
8 
9 /**
10  * General protocol notes:
11  * 1) Request (req) messages are generally sent over DRM_VIRTGPU_EXECBUFFER
12  *    but can also be sent via DRM_VIRTGPU_RESOURCE_CREATE_BLOB (in which
13  *    case they are processed by the host before ctx->get_blob())
14  * 2) Response (rsp) messages are returned via shmem->rsp_mem, at an offset
15  *    specified by the guest in the req message.  Not all req messages have
16  *    a rsp.
17  * 3) Host and guest could have different pointer sizes, ie. 32b guest and
18  *    64b host, or visa versa, so similar to kernel uabi, req and rsp msgs
19  *    should be explicitly padded to avoid 32b vs 64b struct padding issues
20  */
21 
22 /**
23  * Defines the layout of shmem buffer used for host->guest communication.
24  */
25 struct msm_shmem {
26    /**
27     * The sequence # of last cmd processed by the host
28     */
29    uint32_t seqno;
30 
31    /**
32     * Offset to the start of rsp memory region in the shmem buffer.  This
33     * is set by the host when the shmem buffer is allocated, to allow for
34     * extending the shmem buffer with new fields.  The size of the rsp
35     * memory region is the size of the shmem buffer (controlled by the
36     * guest) minus rsp_mem_offset.
37     *
38     * The guest should use the msm_shmem_has_field() macro to determine
39     * if the host supports a given field, ie. to handle compatibility of
40     * newer guest vs older host.
41     *
42     * Making the guest userspace responsible for backwards compatibility
43     * simplifies the host VMM.
44     */
45    uint32_t rsp_mem_offset;
46 
47 #define msm_shmem_has_field(shmem, field) ({                         \
48       struct msm_shmem *_shmem = (shmem);                            \
49       (_shmem->rsp_mem_offset > offsetof(struct msm_shmem, field));  \
50    })
51 
52    /**
53     * Counter that is incremented on asynchronous errors, like SUBMIT
54     * or GEM_NEW failures.  The guest should treat errors as context-
55     * lost.
56     */
57    uint32_t async_error;
58 };
59 
60 #define DEFINE_CAST(parent, child)                                             \
61    static inline struct child *to_##child(const struct parent *x)              \
62    {                                                                           \
63       return (struct child *)x;                                                \
64    }
65 
66 /*
67  * Possible cmd types for "command stream", ie. payload of EXECBUF ioctl:
68  */
69 enum msm_ccmd {
70    MSM_CCMD_NOP = 1,         /* No payload, can be used to sync with host */
71    MSM_CCMD_IOCTL_SIMPLE,
72    MSM_CCMD_GEM_NEW,
73    MSM_CCMD_GEM_SET_IOVA,
74    MSM_CCMD_GEM_CPU_PREP,
75    MSM_CCMD_GEM_SET_NAME,
76    MSM_CCMD_GEM_SUBMIT,
77    MSM_CCMD_GEM_UPLOAD,
78    MSM_CCMD_SUBMITQUEUE_QUERY,
79    MSM_CCMD_WAIT_FENCE,
80    MSM_CCMD_SET_DEBUGINFO,
81    MSM_CCMD_LAST,
82 };
83 
84 struct msm_ccmd_req {
85    uint32_t cmd;
86    uint32_t len;
87    uint32_t seqno;
88 
89    /* Offset into shmem ctrl buffer to write response.  The host ensures
90     * that it doesn't write outside the bounds of the ctrl buffer, but
91     * otherwise it is up to the guest to manage allocation of where responses
92     * should be written in the ctrl buf.
93     */
94    uint32_t rsp_off;
95 };
96 
97 struct msm_ccmd_rsp {
98    uint32_t len;
99 };
100 
101 #define MSM_CCMD(_cmd, _len) (struct msm_ccmd_req){ \
102        .cmd = MSM_CCMD_##_cmd,                      \
103        .len = (_len),                               \
104    }
105 
106 /*
107  * MSM_CCMD_NOP
108  */
109 struct msm_ccmd_nop_req {
110    struct msm_ccmd_req hdr;
111 };
112 
113 /*
114  * MSM_CCMD_IOCTL_SIMPLE
115  *
116  * Forward simple/flat IOC_RW or IOC_W ioctls.  Limited ioctls are supported.
117  */
118 struct msm_ccmd_ioctl_simple_req {
119    struct msm_ccmd_req hdr;
120 
121    uint32_t cmd;
122    uint8_t payload[];
123 };
124 DEFINE_CAST(msm_ccmd_req, msm_ccmd_ioctl_simple_req)
125 
126 struct msm_ccmd_ioctl_simple_rsp {
127    struct msm_ccmd_rsp hdr;
128 
129    /* ioctl return value, interrupted syscalls are handled on the host without
130     * returning to the guest.
131     */
132    int32_t ret;
133 
134    /* The output payload for IOC_RW ioctls, the payload is the same size as
135     * msm_context_cmd_ioctl_simple_req.
136     *
137     * For IOC_W ioctls (userspace writes, kernel reads) this is zero length.
138     */
139    uint8_t payload[];
140 };
141 
142 /*
143  * MSM_CCMD_GEM_NEW
144  *
145  * GEM buffer allocation, maps to DRM_MSM_GEM_NEW plus DRM_MSM_GEM_INFO to
146  * set the BO's iova (to avoid extra guest -> host trip)
147  *
148  * No response.
149  */
150 struct msm_ccmd_gem_new_req {
151    struct msm_ccmd_req hdr;
152 
153    uint64_t iova;
154    uint64_t size;
155    uint32_t flags;
156    uint32_t blob_id;
157 };
158 DEFINE_CAST(msm_ccmd_req, msm_ccmd_gem_new_req)
159 
160 /*
161  * MSM_CCMD_GEM_SET_IOVA
162  *
163  * Set the buffer iova (for imported BOs).  Also used to release the iova
164  * (by setting it to zero) when a BO is freed.
165  */
166 struct msm_ccmd_gem_set_iova_req {
167    struct msm_ccmd_req hdr;
168 
169    uint64_t iova;
170    uint32_t res_id;
171 };
172 DEFINE_CAST(msm_ccmd_req, msm_ccmd_gem_set_iova_req)
173 
174 /*
175  * MSM_CCMD_GEM_CPU_PREP
176  *
177  * Maps to DRM_MSM_GEM_CPU_PREP
178  *
179  * Note: Since we don't want to block the single threaded host, this returns
180  * immediately with -EBUSY if the fence is not yet signaled.  The guest
181  * should poll if needed.
182  */
183 struct msm_ccmd_gem_cpu_prep_req {
184    struct msm_ccmd_req hdr;
185 
186    uint32_t res_id;
187    uint32_t op;
188 };
189 DEFINE_CAST(msm_ccmd_req, msm_ccmd_gem_cpu_prep_req)
190 
191 struct msm_ccmd_gem_cpu_prep_rsp {
192    struct msm_ccmd_rsp hdr;
193 
194    int32_t ret;
195 };
196 
197 /*
198  * MSM_CCMD_GEM_SET_NAME
199  *
200  * Maps to DRM_MSM_GEM_INFO:MSM_INFO_SET_NAME
201  *
202  * No response.
203  */
204 struct msm_ccmd_gem_set_name_req {
205    struct msm_ccmd_req hdr;
206 
207    uint32_t res_id;
208    /* Note: packet size aligned to 4 bytes, so the string name may
209     * be shorter than the packet header indicates.
210     */
211    uint32_t len;
212    uint8_t  payload[];
213 };
214 DEFINE_CAST(msm_ccmd_req, msm_ccmd_gem_set_name_req)
215 
216 /*
217  * MSM_CCMD_GEM_SUBMIT
218  *
219  * Maps to DRM_MSM_GEM_SUBMIT
220  *
221  * The actual for-reals cmdstream submission.  Note this intentionally
222  * does not support relocs, since we already require a non-ancient
223  * kernel.
224  *
225  * Note, no in/out fence-fd, that synchronization is handled on guest
226  * kernel side (ugg).. need to come up with a better story for fencing.
227  * We probably need to sort something out for that to handle syncobjs.
228  *
229  * Note that the bo handles referenced are the host handles, so that
230  * they can be directly passed to the host kernel without translation.
231  *
232  * TODO we can pack the payload tighter (and enforce no-relocs) if we
233  * defined our own structs, at the cost of host userspace having to
234  * do a bit more work.  Is it worth it?  It could probably be done
235  * without extra overhead in guest userspace..
236  *
237  * No response.
238  */
239 struct msm_ccmd_gem_submit_req {
240    struct msm_ccmd_req hdr;
241 
242    uint32_t flags;
243    uint32_t queue_id;
244    uint32_t nr_bos;
245    uint32_t nr_cmds;
246 
247    /**
248     * What userspace expects the next seqno fence to be.  To avoid having
249     * to wait for host, the guest tracks what it expects to be the next
250     * returned seqno fence.  This is passed to guest just for error
251     * checking.
252     */
253    uint32_t fence;
254 
255    /**
256     * Payload is first an array of 'struct drm_msm_gem_submit_bo' of
257     * length determined by nr_bos (note that handles are host handles),
258     * followed by an array of 'struct drm_msm_gem_submit_cmd' of length
259     * determined by nr_cmds
260     */
261    int8_t   payload[];
262 };
263 DEFINE_CAST(msm_ccmd_req, msm_ccmd_gem_submit_req)
264 
265 /*
266  * MSM_CCMD_GEM_UPLOAD
267  *
268  * Upload data to a GEM buffer
269  *
270  * No response.
271  */
272 struct msm_ccmd_gem_upload_req {
273    struct msm_ccmd_req hdr;
274 
275    uint32_t res_id;
276    uint32_t pad;
277    uint32_t off;
278 
279    /* Note: packet size aligned to 4 bytes, so the string name may
280     * be shorter than the packet header indicates.
281     */
282    uint32_t len;
283    uint8_t  payload[];
284 };
285 DEFINE_CAST(msm_ccmd_req, msm_ccmd_gem_upload_req)
286 
287 /*
288  * MSM_CCMD_SUBMITQUEUE_QUERY
289  *
290  * Maps to DRM_MSM_SUBMITQUEUE_QUERY
291  */
292 struct msm_ccmd_submitqueue_query_req {
293    struct msm_ccmd_req hdr;
294 
295    uint32_t queue_id;
296    uint32_t param;
297    uint32_t len;   /* size of payload in rsp */
298 };
299 DEFINE_CAST(msm_ccmd_req, msm_ccmd_submitqueue_query_req)
300 
301 struct msm_ccmd_submitqueue_query_rsp {
302    struct msm_ccmd_rsp hdr;
303 
304    int32_t  ret;
305    uint32_t out_len;
306    uint8_t  payload[];
307 };
308 
309 /*
310  * MSM_CCMD_WAIT_FENCE
311  *
312  * Maps to DRM_MSM_WAIT_FENCE
313  *
314  * Note: Since we don't want to block the single threaded host, this returns
315  * immediately with -ETIMEDOUT if the fence is not yet signaled.  The guest
316  * should poll if needed.
317  */
318 struct msm_ccmd_wait_fence_req {
319    struct msm_ccmd_req hdr;
320 
321    uint32_t queue_id;
322    uint32_t fence;
323 };
324 DEFINE_CAST(msm_ccmd_req, msm_ccmd_wait_fence_req)
325 
326 struct msm_ccmd_wait_fence_rsp {
327    struct msm_ccmd_rsp hdr;
328 
329    int32_t ret;
330 };
331 
332 /*
333  * MSM_CCMD_SET_DEBUGINFO
334  *
335  * Set per-guest-process debug info (comm and cmdline).  For GPU faults/
336  * crashes, it isn't too useful to see the crosvm (for ex.) comm/cmdline,
337  * since the host process is only a proxy.  This allows the guest to
338  * pass through the guest process comm and commandline for debugging
339  * purposes.
340  *
341  * No response.
342  */
343 struct msm_ccmd_set_debuginfo_req {
344    struct msm_ccmd_req hdr;
345 
346    uint32_t comm_len;
347    uint32_t cmdline_len;
348 
349    /**
350     * Payload is first the comm string followed by cmdline string, padded
351     * out to a multiple of 4.
352     */
353    int8_t   payload[];
354 };
355 DEFINE_CAST(msm_ccmd_req, msm_ccmd_set_debuginfo_req)
356 
357 #endif /* MSM_PROTO_H_ */
358