1 // Copyright 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 #pragma once
15 
16 #include "aemu/base/ring_buffer.h"
17 
18 #include <functional>
19 #include <optional>
20 
21 // This file defines common types for address space graphics and provides
22 // documentation.
23 
24 // Address space graphics======================================================
25 //
26 // Basic idea
27 //
28 // Address space graphics (ASG) is a subdevice of the address space device that
29 // provides a way to run graphics commands and data with fewer VM exits by
30 // leveraging shared memory ring buffers.
31 //
32 // Each GL/Vk thread in the guest is associated with a context (asg_context).
33 // asg_context consists of pointers into the shared memory that view it as a
34 // collection of ring buffers and a common write buffer.
35 //
36 // Consumer concept
37 //
38 // ASG does not assume a particular rendering backend (though we will use
39 // RenderThread's). This is for ease of coding/testing and flexibility; the
40 // implementation is not coupled to emugl/libOpenglRender.
41 //
42 // Instead, there is the concept of a "Consumer" of ASG that will do something
43 // with the data arriving from the shared memory region, and possibly reply
44 // back to the guest. We register functions to construct and deconstruct
45 // Consumers as part of emulator init (setConsumer).
46 //
47 // Guest workflow
48 //
49 // 1. Open address space device
50 //
51 // 2. Create the graphics context as the subdevice
52 //
53 // 3. ping(ASG_GET_RING) to get the offset/size of the ring buffer admin. info
54 //
55 // 4. ping(ASG_GET_BUFFER) to get the offset/size of the shared transfer buffer.
56 //
57 // 5. ioctl(CLAIM_SHARED) and mmap on those two offset/size pairs to get a
58 // guest-side mapping.
59 //
60 // 6. call asg_context_create on the ring and buffer pointers to create the asg_context.
61 //
62 // 7. Now the guest and host share asg_context pts and can communicate.
63 //
64 // 8. But usually the guest will sometimes need to ping(ASG_NOTIFY_AVAILABLE)
65 // so that the host side (which is usually a separate thread that we don't want
66 // to spin too much) wakes up and processes data.
67 
68 namespace android {
69 namespace base {
70 
71 class Stream;
72 
73 } // namespace base
74 } // namespace android
75 
76 #define ADDRESS_SPACE_GRAPHICS_DEVICE_ID 0
77 #define ADDRESS_SPACE_GRAPHICS_PAGE_SIZE 4096
78 #define ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE (16ULL * 1048576ULL)
79 
80 // AddressSpaceGraphicsContext shares memory with
81 // the guest via the following layout:
82 extern "C" {
83 
84 struct asg_ring_storage { // directly shared with guest
85     char to_host[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
86     char to_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
87     char from_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
88 };
89 
90 // Set by the address space graphics device to notify the guest that the host
91 // has slept or is able to consume something, or we are exiting, or there is an
92 // error.
93 enum asg_host_state {
94     // The host renderthread is asleep and needs to be woken up.
95     ASG_HOST_STATE_NEED_NOTIFY = 0,
96 
97     // The host renderthread is active and can consume new data
98     // without notification.
99     ASG_HOST_STATE_CAN_CONSUME = 1,
100 
101     // Normal exit
102     ASG_HOST_STATE_EXIT = 2,
103 
104     // Error: Something weird happened and we need to exit.
105     ASG_HOST_STATE_ERROR = 3,
106 
107     // Rendering
108     ASG_HOST_STATE_RENDERING = 4,
109 };
110 
111 struct asg_ring_config;
112 
113 // Each context has a pair of ring buffers for communication
114 // to and from the host. There is another ring buffer for large xfers
115 // to the host (all xfers from the host are already considered "large").
116 //
117 // Each context also comes with _one_ auxiliary buffer to hold both its own
118 // commands and to perform private DMA transfers.
119 struct asg_context { // ptrs into RingStorage
120     struct ring_buffer* to_host;
121     char* buffer;
122     asg_host_state* host_state;
123     asg_ring_config* ring_config;
124     struct ring_buffer_with_view to_host_large_xfer;
125     struct ring_buffer_with_view from_host_large_xfer;
126 };
127 
128 // Helper function that will be common between guest and host:
129 // Given ring storage and a write buffer, returns asg_context that
130 // is the correct view into it.
asg_context_create(char * ring_storage,char * buffer,uint32_t buffer_size)131 inline struct asg_context asg_context_create(
132     char* ring_storage,
133     char* buffer,
134     uint32_t buffer_size) {
135 
136     struct asg_context res;
137 
138     res.to_host =
139         reinterpret_cast<struct ring_buffer*>(
140             ring_storage +
141             offsetof(struct asg_ring_storage, to_host));
142     res.to_host_large_xfer.ring =
143         reinterpret_cast<struct ring_buffer*>(
144             ring_storage +
145             offsetof(struct asg_ring_storage, to_host_large_xfer));
146     res.from_host_large_xfer.ring =
147         reinterpret_cast<struct ring_buffer*>(
148             ring_storage +
149             offsetof(struct asg_ring_storage, from_host_large_xfer));
150 
151     ring_buffer_init(res.to_host);
152 
153     res.buffer = buffer;
154     res.host_state =
155         reinterpret_cast<asg_host_state*>(
156             &res.to_host->state);
157     res.ring_config =
158         reinterpret_cast<asg_ring_config*>(
159             res.to_host->config);
160 
161     ring_buffer_view_init(
162         res.to_host_large_xfer.ring,
163         &res.to_host_large_xfer.view,
164         (uint8_t*)res.buffer, buffer_size);
165 
166     ring_buffer_view_init(
167         res.from_host_large_xfer.ring,
168         &res.from_host_large_xfer.view,
169         (uint8_t*)res.buffer, buffer_size);
170 
171     return res;
172 }
173 
174 // During operation, the guest sends commands and data over the auxiliary
175 // buffer while using the |to_host| ring to communicate what parts of the auxiliary
176 // buffer is outstanding traffic needing to be consumed by the host.
177 // After a transfer completes to the host, the host may write back data.
178 // The guest then reads the results on the same auxiliary buffer
179 // while being notified of which parts to read via the |from_host| ring.
180 //
181 // The size of the auxiliary buffer and flush interval is defined by
182 // the following config.ini android_hw setting:
183 //
184 // 1) android_hw->hw_gltransport_asg_writeBufferSize
185 // 2) android_hw->hw_gltransport_asg_writeStepSize
186 //
187 // 1) the size for the auxiliary buffer
188 // 2) the step size over which commands are flushed to the host
189 //
190 // When transferring commands, command data is built up in writeStepSize
191 // chunks and flushed to the host when either writeStepSize is reached or
192 // the guest flushes explicitly.
193 //
194 // Command vs. Data Modes
195 //
196 // For command data larger than writeStepSize or when transferring data, we
197 // fall back to using a different mode where the entire auxiliary buffer is
198 // used to perform the transfer, |asg_writeBufferSize| steps at a time. The
199 // host is also notified of the total transport size.
200 //
201 // When writing back to the guest, it is assumed that the write buffer will
202 // be completely empty as the guest has already flushed and the host has
203 // already consumed all commands/data, and is writing back. In this case,
204 // the full auxiliary buffer is used at the same time for writing back to
205 // the guest.
206 //
207 // Larger / Shared transfers
208 //
209 // Each of |to_host| and |from_host| can contain elements of type 1, 2, or 3:
210 // Type 1: 8 bytes: 4 bytes offset, 4 bytes size. Relative to write buffer.
211 struct __attribute__((__packed__)) asg_type1_xfer {
212     uint32_t offset;
213     uint32_t size;
214 };
215 // Type 2: 16 bytes: 16 bytes offset into address space PCI space, 8 bytes
216 // size.
217 struct __attribute__((__packed__)) asg_type2_xfer {
218     uint64_t physAddr;
219     uint64_t size;
220 };
221 // Type 3: There is a large transfer of known size and the entire write buffer
222 // will be used to send it over.
223 //
224 // For type 1 transfers, we get the corresponding host virtual address by
225 // adding the offset to the beginning of the write buffer.  For type 2
226 // transfers, we need to calculate the guest physical address and then call
227 // addressspacecontrolops.gethostptr, which is slower since it goes through
228 // a data structure map of existing mappings.
229 //
230 // The rings never contain a mix of type 1 and 2 elements. For to_host,
231 // the guest initiates changes between type 1 and 2.
232 //
233 // The config fields:
234 //
235 struct asg_ring_config {
236     // config[0]: size of the auxiliary buffer
237     uint32_t buffer_size;
238 
239     // config[1]: flush interval for the auxiliary buffer
240     uint32_t flush_interval;
241 
242     // the position of the interval in the auxiliary buffer
243     // that the host has read so far
244     uint32_t host_consumed_pos;
245 
246     // the start of the places the guest might write to next
247     uint32_t guest_write_pos;
248 
249     // 1 if transfers are of type 1, 2 if transfers of type 2,
250     // 3 if the overall transfer size is known and we are sending something large.
251     uint32_t transfer_mode;
252 
253     // the size of the transfer, used if transfer size is known.
254     // Set before setting config[2] to 3.
255     uint32_t transfer_size;
256 
257     // error state
258     uint32_t in_error;
259 };
260 
261 // State/config changes may only occur if the ring is empty, or the state
262 // is transitioning to Error. That way, the host and guest have a chance to
263 // synchronize on the same state.
264 //
265 // Thus far we've established how commands and data are transferred
266 // to and from the host. Next, let's discuss how AddressSpaceGraphicsContext
267 // talks to the code that actually does something with the commands
268 // and sends data back.
269 
270 } // extern "C"
271 
272 namespace android {
273 namespace emulation {
274 namespace asg {
275 
276 // Consumer Concept
277 //
278 // AddressSpaceGraphicsContext's are each associated with a consumer that
279 // takes data off the auxiliary buffer and to_host, while sending back data
280 // over the auxiliary buffer / from_host.
281 //
282 // will read the commands and write back data.
283 //
284 // The consumer type is fixed at startup. The interface is as follows:
285 
286 // Called by the consumer, implemented in AddressSpaceGraphicsContext:
287 //
288 // Called when the consumer doesn't find anything to
289 // read in to_host. Will make the consumer sleep
290 // until another Ping(NotifyAvailable).
291 using OnUnavailableReadCallback =
292     std::function<int()>;
293 
294 // Unpacks a type 2 transfer into host pointer and size.
295 using GetPtrCallback =
296     std::function<char*(uint64_t)>;
297 
298 struct ConsumerCallbacks {
299     OnUnavailableReadCallback onUnavailableRead;
300     GetPtrCallback getPtr;
301 };
302 
303 using ConsumerCreateCallback =
304     std::function<void* (struct asg_context, base::Stream*, ConsumerCallbacks,
305                          uint32_t virtioGpuContextId, uint32_t virtioGpuCapsetId,
306                          std::optional<std::string> nameOpt)>;
307 using ConsumerDestroyCallback =
308     std::function<void(void*)>;
309 using ConsumerPreSaveCallback =
310     std::function<void(void*)>;
311 using ConsumerGlobalPreSaveCallback =
312     std::function<void()>;
313 using ConsumerSaveCallback =
314     std::function<void(void*, base::Stream*)>;
315 using ConsumerGlobalPostSaveCallback =
316     std::function<void()>;
317 using ConsumerPostSaveCallback =
318     std::function<void(void*)>;
319 using ConsumerPostLoadCallback = ConsumerPostSaveCallback;
320 using ConsumerGlobalPreLoadCallback = ConsumerGlobalPostSaveCallback;
321 
322 struct ConsumerInterface {
323     ConsumerCreateCallback create;
324     ConsumerDestroyCallback destroy;
325 
326     ConsumerPreSaveCallback preSave;
327     ConsumerGlobalPreSaveCallback globalPreSave;
328 
329     ConsumerSaveCallback save;
330 
331     ConsumerGlobalPostSaveCallback globalPostSave;
332     ConsumerPostSaveCallback postSave;
333 
334     ConsumerPostLoadCallback postLoad;
335 
336     ConsumerGlobalPreLoadCallback globalPreLoad;
337 };
338 
339 } // namespace asg
340 } // namespace emulation
341 } // namespace android
342 
343 // The interface for the guest:
344 
345 extern "C" {
346 // Handled outside in address_space_device.cpp:
347 //
348 // Ping(device id): Create the device. On the host, the two rings and
349 // auxiliary buffer are allocated. The two rings are allocated up front.
350 // Both the auxiliary buffers and the rings are allocated from blocks of
351 // rings and auxiliary buffers. New blocks are created if we run out either
352 // way.
353 enum asg_command {
354     // Ping(get_ring): Returns, in the fields:
355     // metadata: offset to give to claimShared and mmap() in the guest
356     // size: size to give to claimShared and mmap() in the guest
357     ASG_GET_RING = 0,
358 
359     // Ping(get_buffer): Returns, in the fields:
360     // metadata: offset to give to claimShared and mmap() in the guest
361     // size: size to give to claimShared and mmap() in the guest
362     ASG_GET_BUFFER = 1,
363 
364     // Ping(set_version): Run after the guest reads and negotiates its
365     // version of the device with the host. The host now knows the guest's
366     // version and can proceed with a protocol that works for both.
367     // size (in): the version of the guest
368     // size (out): the version of the host
369     // After this command runs, the consumer is
370     // implicitly created.
371     ASG_SET_VERSION = 2,
372 
373     // Ping(notiy_available): Wakes up the consumer from sleep so it
374     // can read data via toHost
375     ASG_NOTIFY_AVAILABLE = 3,
376 
377     // Retrieve the config.
378     ASG_GET_CONFIG = 4,
379 };
380 
381 } // extern "C"
382