1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
//! This is the main API that exposes CoreVM-specific host-calls via exports with C linkage. At
//! present there are Rust and C convenience wrappers for this API available in this crate. In
//! future more languages might be added.
//!
//!
//! # Input
//!
//! ## Blocking vs. non-blocking behaviour
//!
//! Some of the host-calls might use blocking or non-blocking mode. In blocking mode (which is
//! usually the default) a host-call that can't complete within the space-time frame of the current
//! work package suspends VM execution. The builder then supplies the data that the host-call
//! needed and resumes execution in another work package. For the guest this happens
//! transparently, i.e. it's not possible to detect whether the VM was suspended in a host-call or
//! not.
//!
//! In a non-blocking mode VM is not suspended and host-call returns some special value to indicate
//! that it can't complete normally.
//!
//! For example, in blocking mode [`read_console_data`] suspends the VM if the standard input stream
//! is empty. In non-blocking mode the same host-call simply returns `u64::MAX` to indicate that the
//! stream is empty.
//!
//!
//! ## Retaining vs. discarding input on VM suspension
//!
//! When VM is suspended the unprocessed input data is discarded by default. This applies to video,
//! audio, console input and also host messages. Unprocessed inter-service messages, however, are
//! retained by the builder; they are supplied to the guest in the next work package as the input.
//!
//!
//! # Transactions
//!
//! ## Synchronous execution
//!
//! CoreVM implements synchronous transactions, i.e. transactions that are guaranteed to complete
//! within one time-slot. Each transaction involves two or more guests and is coordinated by a
//! single CoreVM service. To achieve synchronicity the service co-schedules all guests in a single
//! work package and executes them in lockstep until the transaction completes (successfully or
//! not). If either of the guests runs out of resources, the transaction is rolled back to be
//! re-executed in some subsequent time slot. This happens transaprently to the guests.
//!
//! ## Example
//!
//! Transaction initiator:
//! ```no_run,ignore
//! initiate_transaction(...);
//! // Do the changes here...
//! if commit_transaction().is_err() {
//! // Rollback the changes here...
//! }
//! // Confirm that the transaction was finished successfully or was successfully rolled back.
//! end_transaction();
//! ```
//!
//! Transaction processor:
//! ```no_run,ignore
//! recv_transaction(...);
//! ...
//! if commit_transaction().is_err() {
//! // Rollback the changes here...
//! }
//! // Confirm that the transaction was finished successfully or was successfully rolled back.
//! end_transaction();
//! ```
/// Get the current amount of gas.
pub unsafe extern "C"
/// Allocate memory block of the specified size.
///
/// - The size is rounded up to the next multiple of page size.
/// - The returned address is aligned at a page boundary.
///
/// Returns the address of the allocated memory region or `0` if the allocation was
/// unsuccessful.
pub unsafe extern "C"
/// Deallocate the memory block of the specified size starting at `address`.
///
/// Deallocates *all* the pages that overlap the memory block.
pub unsafe extern "C"
/// Append the data from `address..address + len` to the _console_ output stream.
///
/// The `stream` can be `1` for standard output and `2` for standard error.
pub unsafe extern "C"
/// Append a video frame from `address..address + len` to the _video_ output stream.
///
/// `format` is frame encoding format; see [`VideoFrameFormat`](crate::VideoFrameFormat) for the
/// list of supported formats.
pub unsafe extern "C"
/// Set video monitor output mode.
///
/// Parameters:
/// - `width` is frame width,
/// - `height` is frame height,
/// - `refresh_rate` is monitor refresh rate measured as the number of frames per second,
/// - `flags` additional flags that fine-tune video encoder.
///
/// Flags:
/// | Name | Bits | Description |
/// |------|------|-------------|
/// | `quantization_level` | 0-3 | Video codec quantization level. Non-zero value makes encoding lossy. |
/// | `chroma_subsampling` | 4 | Determines whether 4:2:0 chroma subsampling is enabled or not. Enabling this makes encoding lossy. |
/// | `raw` | 5 | Enables raw mode: every frame is recorded as is without any encoding. This is useful when running the code in a RISCV interpreter. |
///
/// By default video output is disabled.
///
///
/// # Lossy vs. lossless encoding
///
/// Lossy encoding works best for images with smooth color transitions (e.g. any depictions of
/// physical reality). For such images enabling lossy encoding might significantly reduce video
/// output size without noticeable change in quality.
///
/// Lossless encoding works best for images with sharp edges such as dialog boxes and vector
/// graphics in general. By default video encoding is lossless.
pub unsafe extern "C"
/// Append audio samples from `address..address + len` to the _audio_ output stream.
///
/// The format of the sample is defined by [`audio_mode`](crate::audio_mode).
///
/// When both video and audio output is enabled the guest is expected to yield exactly _N_ video
/// frames worth of audio samples per second, where _N_ is the number of frames per second. To
/// improve synchrony approximately one video frame worth of audio samples should be yielded
/// together with the video frame, however this is not a strict requirement because the monitor
/// usually buffers video and audio streams. The following example shows a typical program
/// that yields both video and audio frames.
///
/// ```no_run,ignore
/// loop {
/// yield_video_frame(...);
/// yield_audio_samples(...);
/// }
/// ```
pub unsafe extern "C"
/// Set audio output mode.
///
/// Parameters:
/// - `channels` is the number of channels,
/// - `sample_rate` is the number of samples per second per channel,
/// - `sample_format` is the audio sample format; see
/// [`AudioSampleFormat`](crate::AudioSampleFormat) for the list of supported formats.
///
/// By default audio output is disabled.
pub unsafe extern "C"
/// Pop a message from another service from the queue.
///
/// Returns the length of the message in the first 32 bits and the ID of the sender in the second
/// 32 bits of the return value.
///
/// - If the message length is less than or equal to `len` the message is popped from the queue and
/// copied to `address..address + len`.
/// - If the message length is larger than `len` or the `address` is zero, then the message length
/// and source are returned but message isn't popped and its contents are not copied.
/// - If the queue is empty, then the call will block until new messages are pushed to the queue.
///
/// # Flags
///
/// - [`NON_BLOCKING`](corevm_types::flags::RecvMessage::NON_BLOCKING). Setting this flag makes the
/// call non-blocking. If the queue is empty and this flag is set, then the call immediately
/// returns `u64::MAX` instead of waiting for new messages to arrive.
/// - [`DISCARD`](corevm_types::flags::RecvMessage::DISCARD). Setting this flag makes the call
/// silently discard messages longer than `len`.
///
///
/// # VM suspension behaviour
///
/// When VM is suspended the remaining messages are _retained_ in the queue by the builder and will
/// be available to the guest as soon as the VM is resumed.
pub unsafe extern "C"
/// Send the message stored at `address..address + len` to the CoreVM service with id
/// `guest_id`.
pub unsafe extern "C"
/// Pop a message from the host from the queue.
///
/// The interface of this function is similar to [`recv_message`] but it does not return the ID of
/// the sender.
///
/// # VM suspension behaviour
///
/// When VM is suspended the remaining messages are _discarded_ by the builder.
pub unsafe extern "C"
/// Wait for the data to appear in any of the specified input streams.
///
/// Parameters:
/// - `streams`: a bitset that specifies which [input streams](corevm_types::InputStreams) to wait
/// for.
/// - `flags`: a bitset with [polling options](corevm_types::flags::Poll). values.
///
/// Returns a bitset that specifies which streams are available for the input.
///
/// # Flags
///
/// - [`NON_BLOCKING`](corevm_types::flags::Poll::NON_BLOCKING). Setting this flag makes the call
/// return immediately even if all streams are empty.
pub unsafe extern "C"
/// Read the data from _console_ input stream (standard input).
///
/// The data is copied to `address..address + len` and the host-call returns the number of bytes
/// copied. If console input stream is empty, the call blocks until new data arrives.
///
/// # Flags
///
/// - [`NON_BLOCKING`](corevm_types::flags::ReadConsoleData::NON_BLOCKING). Setting this flag makes
/// the call non-blocking: if there is no data in the standard input, then the call immediately
/// returns `u64::MAX`.
///
/// # VM suspension behaviour
///
/// When VM is suspended the remaining console data is _discarded_ by the builder.
pub unsafe extern "C"
/// Get video input mode.
///
/// The data is encoded as follows.
///
/// - `VideoMode::width`: bits `0..16`.
/// - `VideoMode::height`: bits `16..32`.
/// - `VideoMode::refresh_rate`: bits `32..48`.
/// - `VideoMode::format`: bits `48..56`. See [`VideoFrameFormat`](crate::VideoFrameFormat) for the
/// list of possible values.
///
/// Returns `0` if there is no video input.
pub unsafe extern "C"
/// Get the next frame from the video input.
///
/// The frame is copied to `address..address + len`. If the frame length is larger than
/// `len` or the `address` is zero, then the frame length is returned and nothing is
/// copied. If video input stream is empty, then the call blocks until new data arrives.
///
/// `format` is frame encoding format; see [`VideoFrameFormat`](crate::VideoFrameFormat)
/// for the list of supported formats.
///
/// # Flags
///
/// - [`NON_BLOCKING`](corevm_types::flags::ReadVideoFrame::NON_BLOCKING). If the flag is set and
/// the video input is empty, then the call immediately returns `u64::MAX`.
///
/// # VM suspension behaviour
///
/// When VM is suspended the remaining video frames are _discarded_ by the builder.
pub unsafe extern "C"
/// Get audio input mode.
///
/// The data is encoded as follows.
///
/// - `AudioMode::sample_rate`: bits `0..32`.
/// - `AudioMode::channels`: bits `32..40`.
/// - `AudioMode::sample_format`: bits `40..48`.
///
/// Returns 0 if there is no audio input.
pub unsafe extern "C"
/// Get the frames from the audio input.
///
/// The frames are copied to `address..address + len` and the host-call returns the number of bytes
/// copied. This host-call copies as many complete frames as can fit into `len` (a frame is an array
/// of samples, one sample for each channel). If audio input stream is empty, the call blocks until
/// new data arrives.
///
/// # Flags
///
/// - [`NON_BLOCKING`](corevm_types::flags::ReadAudioFrames::NON_BLOCKING). Setting this flag makes
/// the call non-blocking. In this mode if there is no data in the audio input, then the call
/// immediately returns `u64::MAX`.
///
/// # VM suspension behaviour
///
/// When VM is suspended the remaining audio frames are _discarded_ by the builder.
pub unsafe extern "C"
/// Initiate a transaction with guest ID `target` with payload from `address..address + len`.
///
/// Blocks until the target guest copies the payload from `address..address + len` to the target
/// guest's address space via [`recv_transaction`].
///
/// Returns zero on success. Returns non-zero when any of the following is true:
/// - bad address supplied to [`recv_transaction`] or [`initiate_transaction`],
/// - target guest couldn't be found,
/// - any other low-level error is encountered.
///
/// # Transactions with more than two guests
///
/// To involve more than two guests in the transaction simply call `initiate_transaction` multiple
/// times _before_ calling `commit_transaction` or `rollback_transaction`. You have to use a
/// different `target` each time and might use a different payload as well. All such transactions
/// will be combined into one and a subsequent call to `commit_transaction` or
/// `rollback_transaction` will affect all of them at once. For such transaction to succeed every
/// guest should call `commit_transaction`.
///
/// # Synchronous execution
///
/// This is synchronous operation, i.e. all guests that participate in the transaction are executed
/// in lockstep until the transaction completes.
pub unsafe extern "C"
/// Receive a transaction from the transaction initiator.
///
/// Returns the length of the payload in the first 32 bits and the guest ID of the sender in the
/// second 32 bits of the return value.
///
/// - If the payload length is less than or equal to `len` the payload copied to `address..address +
/// len`.
/// - If the payload length is larger than `len` or the `address` is zero, then the payload length
/// and source are returned but payload contents are not copied.
/// - If there is no pending transaction, then the call will block until some transaction is
/// initiated.
/// - If any low-level error occures the transaction is ignored and the next one is tried until the
/// call either succeeds or runs out of pending transactions.
///
/// # Flags
///
/// - [`NON_BLOCKING`](corevm_types::flags::RecvTransaction::NON_BLOCKING). Setting this flag makes
/// the call non-blocking. If the queue is empty and this flag is set, then the call immediately
/// returns `u64::MAX` instead of waiting for new messages to arrive.
///
/// # Synchronous execution
///
/// This is synchronous operation, i.e. all guests that participate in the transaction are executed
/// in lockstep until the transaction completes. The transaction is considered received when the
/// payload is copied to the guest address space.
pub unsafe extern "C"
/// Commit the transaction that was started by either [`initiate_transaction`] or
/// [`recv_transaction`].
///
/// Blocks until the other guest either calls [`commit_transaction`], or [`rollback_transaction`],
/// or panics.
///
/// This call singnifies that the current guest finished its part of the transaction. Calling this
/// function without an active transaction will result in panic.
///
/// Returns zero on success. Returns non-zero when any of the following is true:
/// - the other side of this transaction panics or calls [`rollback_transaction`],
/// - any other low-level error is encountered.
///
/// # Synchronous execution
///
/// This is synchronous operation, i.e. all guests that participate in the transaction are executed
/// in lockstep until the transaction completes. The transaction is considered complete when either
/// both guests call [`end_transaction`] or one of the guests panics.
pub unsafe extern "C"
/// Cancel the transaction that was started by either [`initiate_transaction`] or
/// [`recv_transaction`].
///
/// This call singnifies that the current guest is unable to finish its part of the transaction.
/// Calling this function without an active transaction will result in panic.
///
/// # Synchronous execution
///
/// This is synchronous operation, i.e. all guests that participate in the transaction are executed
/// in lockstep until the transaction completes. The transaction is considered complete when either
/// both guests call [`end_transaction`] or one of the guests panics.
pub unsafe extern "C"
/// Finish the transaction.
///
/// This call confirms that either the transaction was finished successfully or the changes were
/// rolled back successfully.
///
/// # Synchronous execution
///
/// This is synchronous operation, i.e. all guests that participate in the transaction are executed
/// in lockstep until the transaction completes. The transaction is considered complete when either
/// both guests call [`end_transaction`] or one of the guests panics.
pub unsafe extern "C"