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