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
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
|
/* Copyright (c) Fortanix, Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![cfg_attr(
not(feature = "docs"),
doc = "**You are viewing the internals documentation."
)]
#![cfg_attr(
not(feature = "docs"),
doc = "You probably want to compile the documentation with the “docs” feature.**"
)]
#![cfg_attr(not(feature = "docs"), doc = "---")]
//! The Fortanix SGX ABI (compiler target `x86_64-fortanix-unknown-sgx`) is an
//! interface for Intel SGX enclaves. It is a small yet functional interface
//! suitable for writing larger enclaves. In contrast to other enclave
//! interfaces, this interface is primarly designed for running entire
//! applications in an enclave.
//!
//! The Fortanix SGX ABI specification consists of two parts:
//!
//! 1. The calling convention (see FORTANIX-SGX-ABI.md)
//! 2. The execution environment and [usercalls](struct.Usercalls.html) (this document)
//!
//! Whereas the calling convention describes how information is passed to and
//! from the enclave, this document ascribes meaning to those values.
//!
//! The execution environment and usercalls have been designed with the
//! following goals in mind:
//!
//! 1. *Compatible with most Rust code:* Rust code that doesn't link to other C
//! libraries and that doesn't use files (see no. 5) should compile out of
//! the box.
//! 2. *Designed for SGX:* The SGX environment is unique and not compatible
//! with other application environments out there. The primitives specified
//! in this document are designed to work well with SGX, not to be similar
//! to or compatible with primitives known from other environments.
//! 3. *Designed for network services:* The most interesting usecase for SGX is
//! to run applications remotely in an untrusted environment, e.g. the
//! cloud. Therefore, there is a primary focus on supporting functionality
//! needed in those situations.
//! 4. *No filesystem:* Encrypted filesystems are hard. Especially in SGX,
//! consistency and freshness are big concerns. In this initial version,
//! there is no filesystem support, which is fine for most network services,
//! which would want to keep their state with a database service anyway.
//! Support might be added in the future.
//! 5. *Not POSIX:* The POSIX API is huge and contains many elements that are
//! not directly supported by the SGX instruction set, such as fork and
//! mmap. It is explicitly a non-goal of this specification to support all
//! of POSIX.
//! 6. *Designed to be portable:* Enclaves don't interact directly with the OS,
//! so there should be no need to recompile an enclave when running it with
//! a different OS. This specification does not require any particular
//! primitives or behavior from the OS.
//!
//! Like on regular operating systems, there are two types of enclaves:
//! *executable*-type and *library*-type. The main difference between the two
//! different types is how the enclave may be entered. Once an enclave TCS is
//! entered, the different types act virtually identically. More information on
//! the two different types, TCSs, and enclave entry may be found in the
//! [`entry`](entry/index.html) module.
//!
//! Once an enclave TCS is entered, it may performs *synchronous usercalls* as
//! described in the calling convention. The TCS maintains its execution state
//! between doing a usercall and returning from the usercall. Only when the TCS
//! exits, either through a non-usercall exit or through the
//! [`exit`](struct.Usercalls.html#method.exit) usercall, is the TCS state
//! destroyed. This is depicted in the following diagram.
//!
//! ![Enclave execution lifecycle](https://edp.fortanix.com/img/docs/enclave-execution-lifecycle.png)
//!
//! Enclaves may also perform *asynchronous usercalls*. This is detailed in the
//! [`async`](async/index.html) module. Most usercalls can be submitted either
//! synchronously or asynchronously.
#![allow(unused)]
#![no_std]
#![cfg_attr(feature = "rustc-dep-of-std", feature(staged_api))]
#![cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
#![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg",
html_favicon_url = "https://edp.fortanix.com/favicon.ico",
html_root_url = "https://edp.fortanix.com/docs/api/")]
use core::ptr::NonNull;
use core::sync::atomic::AtomicUsize;
macro_rules! invoke_with_abi_spec [
( $m:ident ) => [ $m![
/// Specification of TCS and entry function requirements.
///
/// Once an enclave has called the
/// [`exit`](../../struct.Usercalls.html#method.exit) usercall, if userspace
/// enters a TCS normally, the enclave must panic. If userspace returns a
/// usercall on a TCS, the enclave may decide whether to handle it normally or
/// to panic.
pub mod entry {
/// Specifies the entry points for libraries.
///
/// The specification for library support is **experimental** and is
/// subject to change.
///
/// When a user application wishes to call into the enclave library,
/// userspace may use any available TCS. Libraries may keep state between
/// invocations, but the library must not assume that subsequent calls will
/// go to the same TCS.
///
/// The use of asynchronous usercalls with libraries is not recommended, as
/// userspace will not be able to wake up the appropriate thread in a
/// multi-threaded library scenario.
///
/// Automatically launching threads using the
/// [`launch_thread`](../../struct.Usercalls.html#method.launch_thread)
/// usercall is not supported. Libraries that want to leverage
/// multi-threading must rely on application support to call into the
/// enclave from different threads.
pub mod library {
/// The entry point of every TCS.
///
/// If a library wishes to expose multiple different functions, it must
/// implement this by multiplexing on one of the input parameters. It
/// is recommended to use `p1` for this purpose.
///
/// The `_ignore` parameter may be set to any value by userspace. The
/// value observed by the enclave may be different from the value
/// passed by userspace and must therefore be ignored by the enclave.
pub fn entry(p1: u64, p2: u64, p3: u64, _ignore: u64, p4: u64, p5: u64) -> (u64, u64) { unimplemented!() }
}
/// Specifies the entry points for executables.
pub mod executable {
use ByteBuffer;
/// The main entry point of the enclave. This will be the entry point
/// of the first TCS.
///
/// The enclave must not return from this entry. Instead, it must call
/// the [`exit`](../../struct.Usercalls.html#method.exit) usercall. If
/// the enclave does return from this TCS, and userspace subsequently
/// re-enters this TCS, the enclave must panic.
///
/// Arbitrary “command-line arguments” may be passed in from userspace.
/// The enclave must ensure that the all buffers pointed to are
/// outside the enclave. The enclave should deallocate each
/// [`ByteBuffer`] as specified by the type. The enclave should
/// deallocate the main buffer by calling
/// [`free`]`(args, len * size_of::<ByteBuffer>, 1)`.
///
/// [`free`]: ../../struct.Usercalls.html#method.free
/// [`ByteBuffer`]: ../../struct.ByteBuffer.html
pub fn main_entry(args: *const ByteBuffer, len: usize) -> ! { unimplemented!() }
/// The entry point of additional threads of the enclave, for non-first
/// TCSs.
///
/// When returning from this TCS, userspace may re-enter this TCS after
/// another call to [`launch_thread`].
///
/// The enclave must keep track of whether it expects another thread to
/// be launched, e.g. by keeping track of how many times it called
/// [`launch_thread`]. If a TCS with this entry point is entered even
/// though the enclave didn't request it, the enclave must panic.
///
/// [`launch_thread`]: ../../struct.Usercalls.html#method.launch_thread
pub fn thread_entry() { unimplemented!() }
}
}
/// An arbitrary-sized buffer of bytes in userspace, allocated by userspace.
///
/// This type is used when userspace may return arbitrary-sized data from a
/// usercall. When reading from the buffer, if `len` is not `0`, the enclave
/// must ensure the entire buffer is in the user memory range. Once the enclave
/// is done with the buffer, it should deallocate the buffer buffer by calling
/// [`free`]`(data, len, 1)`.
///
/// If `len` is `0`, the enclave should ignore `data`. It should not call
/// `free`.
///
/// [`free`]: ./struct.Usercalls.html#method.launch_thread
#[repr(C)]
#[derive(Copy, Clone)]
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub struct ByteBuffer {
pub data: *const u8,
pub len: usize
}
/// Error code definitions and space allocation.
///
/// Only non-zero positive values are valid errors. The variants are designed
/// to map to [std::io::ErrorKind]. See the source for the value mapping.
///
/// [std::io::ErrorKind]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
#[repr(i32)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub enum Error {
PermissionDenied = 0x01,
NotFound = 0x02,
Interrupted = 0x04,
WouldBlock = 0x0b,
AlreadyExists = 0x11,
InvalidInput = 0x16,
BrokenPipe = 0x20,
AddrInUse = 0x62,
AddrNotAvailable = 0x63,
ConnectionAborted = 0x67,
ConnectionReset = 0x68,
NotConnected = 0x6b,
TimedOut = 0x6e,
ConnectionRefused = 0x6f,
InvalidData = 0x2000_0000,
WriteZero = 0x2000_0001,
UnexpectedEof = 0x2000_0002,
/// This value is reserved for `Other`, but all undefined values also map
/// to `Other`.
Other = 0x3fff_ffff,
/// Start of the range of values reserved for user-defined errors.
UserRangeStart = 0x4000_0000,
/// End (inclusive) of the range of values reserved for user-defined errors.
UserRangeEnd = 0x7fff_ffff,
}
/// A value indicating that the operation was succesful.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const RESULT_SUCCESS: Result = 0;
/// The first return value of usercalls that might fail.
///
/// [`RESULT_SUCCESS`](constant.RESULT_SUCCESS.html) or an error code from the
/// [`Error`](enum.Error.html) type.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub type Result = i32;
/// The list of all usercalls.
///
/// *This is not a real structure, it's just a convenient way to group
/// functions with `rustdoc`.*
///
/// The usercall number is passed in the first register. Up to 4 arguments may
/// be passed in the remaining registers. Unspecified arguments and return
/// values must be 0. Userspace must check the arguments and the enclave must
/// check the return value.
///
/// The usercall number may be one of the predefined numbers associated with
/// one of the usercalls defined below, or, if bit
/// [`USERCALL_USER_DEFINED`](constant.USERCALL_USER_DEFINED.html) is set, an
/// otherwise arbitrary number with an application-defined meaning.
///
/// Raw pointers must always point to user memory. When receiving raw pointers
/// from userspace, the enclave must verify that the entire pointed-to memory
/// space is outside the enclave memory range. It must then copy all data in
/// user memory to enclave memory before operating on it.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub struct Usercalls;
/// Usercall numbers with this bit set will never be defined by this specification.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const USERCALL_USER_DEFINED: u64 = 0x8000_0000;
/// A file descriptor.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub type Fd = u64;
/// Standard input file descriptor. Input read this way is not secure.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const FD_STDIN: Fd = 0;
/// Standard output file descriptor. This is not a secure output channel.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const FD_STDOUT: Fd = 1;
/// Standard error file descriptor. This is not a secure output channel.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const FD_STDERR: Fd = 2;
/// # Streams
///
/// The enclave must not assume anything about data read or written using these
/// calls. Data written may be piped directly to `/dev/null` by userspace, or
/// it may be published in the local newspaper. Similarly, data read may be
/// arbitrarily deleted, inserted, or changed. The enclave must use additional
/// security primitives such as sealing or TLS to obtain stronger guarantees.
///
/// When a stream is read from by multiple threads simultaneously, the read
/// calls may be serialized in any order. This means the data returned by a
/// read call appeared that way in the stream, and every single byte in the
/// stream will be read and be read only once. However, the order in which all
/// stream data is returned is not defined. The same applies when
/// simultaneously submitting multiple read calls for the same stream
/// asynchronously. This all applies similarly to writing to a stream.
///
/// To make sure to be able to re-assemble the stream, the enclave can take one
/// of the following approaches:
///
/// 1. Submit all read calls to a single stream on a single thread.
/// 2. Serializing read calls by synchronizing access to a single stream.
///
/// In addition, the enclave should use cryptographic integrity protection of
/// the stream data to ensure the stream data has not been tampered with.
impl Usercalls {
/// Read up to `len` bytes from stream `fd`.
///
/// `buf` must point to a buffer in userspace with a size of at least
/// `len`. On a succesful return, the number of bytes written is returned.
/// The enclave must check that the returned length is no more than `len`.
/// If `len` is `0`, this call should block until the stream is ready for
/// reading. If `len` is `0` or end of stream is reached, `0` may be
/// returned.
///
/// The enclave may mix calls to [`read`](#method.read) and
/// [`read_alloc`](#method.read_alloc).
pub fn read(fd: Fd, buf: *mut u8, len: usize) -> (Result, usize) { unimplemented!() }
/// Read some data from stream `fd`, letting the callee choose the amount.
///
/// `buf` must point to a [`ByteBuffer`] in userspace, and `buf.data` must
/// contain `null`. On success, userspace will allocate memory for the read
/// data and populate `ByteBuffer` appropriately. The enclave must handle
/// and deallocate the buffer according to the `ByteBuffer` documentation.
///
/// Since every read operation using this usercall requires two usercalls,
/// it is recommended to only call this usercall asynchronously.
///
/// The enclave may mix calls to [`read`](#method.read) and
/// [`read_alloc`](#method.read_alloc).
///
/// [`ByteBuffer`]: ./struct.ByteBuffer.html
pub fn read_alloc(fd: Fd, buf: *mut ByteBuffer) -> Result { unimplemented!() }
/// Write up to `len` bytes to stream `fd`.
///
/// `buf` must point to a buffer in userspace with a size of at least
/// `len`. On a succesful return, the number of bytes written is returned.
/// The enclave must check that the returned length is no more than `len`.
/// If `len` is `0`, this call should block until the stream is ready for
/// writing. If `len` is `0` or the stream is closed, `0` may be returned.
pub fn write(fd: Fd, buf: *const u8, len: usize) -> (Result, usize) { unimplemented!() }
/// Flush stream `fd`, ensuring that all intermediately buffered contents
/// reach their destination.
pub fn flush(fd: Fd) -> Result { unimplemented!() }
/// Close stream `fd`.
///
/// Once the stream is closed, no further data may be read or written.
/// Userspace may reuse the `fd` in the future for a different stream.
pub fn close(fd: Fd) { unimplemented!() }
}
/// # Networking
///
/// In keeping with the design goals for this specification, the
/// networking/socket interface doesn't use `sockaddr` types and doesn't
/// have a separate API for name resolution. Userspace can't be trusted to
/// do name resolution correctly, and even if it did, *userspace can't be
/// trusted to actually connect streams to the correct address* specified by
/// the enclave. Therefore, addresses specified should merely be treated as a
/// suggestion, and additional measures must be taken by an enclave to verify
/// the stream is connected to the correct peer, e.g. TLS.
///
/// The networking API works with strings as addresses. All byte buffers
/// representing network addresses should contain a valid UTF-8 string. The
/// enclave should panic if it is passed an invalid string by userspace. It is
/// suggested that userspace supports at least the following notations:
///
/// * `hostname:port-number` (e.g. `example.com:123`)
/// * `dotted-octet-ipv4-address:port-number` (e.g. `192.0.2.1:123`)
/// * `[ipv6-address]:port-number` (e.g. `[2001:db8::1]:123`)
///
/// Additionally, other forms may be accepted, for example service names:
///
/// * `fully-qualified-service-name` (e.g. `_example._tcp.example.com`)
/// * `address:service-name` (e.g. `address:example`)
///
/// # Errors
///
/// Networking calls taking an address may return the [`InvalidInput`] error if
/// the address could not be interpreted by userspace.
///
/// [`InvalidInput`]: enum.Error.html#variant.InvalidInput
impl Usercalls {
/// Setup a listening socket.
///
/// The socket is bound to the address specified in `addr`. `addr` must be
/// a buffer in user memory with a size of at least `len`.
///
/// On success, a file descriptor is returned which may be passed to
/// [`accept_stream`](#method.accept_stream) or [`close`](#method.close).
///
/// The enclave may optionally request the local socket address be returned
/// in `local_addr`. On success, if `local_addr` is not NULL, userspace
/// will allocate memory for the address and populate [`ByteBuffer`]
/// appropriately. The enclave must handle and deallocate the buffer
/// according to the `ByteBuffer` documentation.
///
/// The enclave must not make any security decisions based on the local
/// address received.
///
/// [`ByteBuffer`]: ./struct.ByteBuffer.html
pub fn bind_stream(addr: *const u8, len: usize, local_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() }
/// Accept a new connection from a listening socket.
///
/// `fd` should be a file descriptor previously returned from
/// [`bind_stream`](#method.bind_stream).
///
/// The enclave may optionally request the local or peer socket addresses
/// be returned in `local_addr` or `peer_addr`, respectively. On success,
/// if `local_addr` and/or `peer_addr` is not NULL, userspace will allocate
/// memory for the address and populate the respective [`ByteBuffer`]
/// appropriately.
///
/// The enclave must handle and deallocate each buffer according to the
/// `ByteBuffer` documentation.
///
/// The enclave must not make any security decisions based on the local or
/// peer address received.
///
/// [`ByteBuffer`]: ./struct.ByteBuffer.html
pub fn accept_stream(fd: Fd, local_addr: *mut ByteBuffer, peer_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() }
/// Create a new stream connection to the specified address.
///
/// The enclave may optionally request the local or peer socket addresses
/// be returned in `local_addr` or `peer_addr`, respectively. On success,
/// if `local_addr` and/or `peer_addr` is not NULL, userspace will allocate
/// memory for the address and populate the respective [`ByteBuffer`]
/// appropriately.
///
/// The enclave must handle and deallocate each buffer according to the
/// `ByteBuffer` documentation.
///
/// The enclave must not make any security decisions based on the local or
/// peer address received.
///
/// [`ByteBuffer`]: ./struct.ByteBuffer.html
pub fn connect_stream(addr: *const u8, len: usize, local_addr: *mut ByteBuffer, peer_addr: *mut ByteBuffer) -> (Result, Fd) { unimplemented!() }
}
/// The absolute address of a TCS in the current enclave.
// FIXME: `u8` should be some `extern type` instead.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub type Tcs = NonNull<u8>;
/// An event that will be triggered by userspace when the usercall queue is not
/// or no longer full.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const EV_USERCALLQ_NOT_FULL: u64 = 0b0000_0000_0000_0001;
/// An event that will be triggered by userspace when the return queue is not
/// or no longer empty.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const EV_RETURNQ_NOT_EMPTY: u64 = 0b0000_0000_0000_0010;
/// An event that enclaves can use for synchronization.
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const EV_UNPARK: u64 = 0b0000_0000_0000_0100;
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const WAIT_NO: u64 = 0;
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub const WAIT_INDEFINITE: u64 = !0;
/// # Execution control
///
/// ## TCS event queues
///
/// Userspace will maintain a queue for each running TCS with events to be
/// delivered. Each event is characterized by a bitset. Userspace or the
/// enclave (using the `send` usercall) can put events on this queue. If the
/// enclave isn't waiting for an event when an event is queued, the event
/// remains on the queue until it delivered to the enclave in a later `wait`
/// usercall. If an enclave is waiting for an event, and the queue contains an
/// event that is a subset of the waited-for event mask, that event is removed
/// from the queue and execution control is returned to the enclave.
///
/// Events not defined in this specification should not be generated.
impl Usercalls {
/// In [executables](entry/executable/index.html), this will instruct
/// userspace to enter another TCS in another thread. This TCS should have
/// the [`thread_entry`] entrypoint. As documented in [`thread_entry`], the
/// enclave should keep track of how many threads it launched and reconcile
/// this with the number of entries into [`thread_entry`]. If no free TCSes
/// are immediately available, this may return an error.
///
/// This function will never be succesful in [libraries]. See the
/// [`library`] documentation on how to use threads with libraries.
///
/// [`thread_entry`]: entry/executable/fn.thread_entry.html
/// [libraries]: entry/library/index.html
/// [`library`]: entry/library/index.html
pub fn launch_thread() -> Result { unimplemented!() }
/// Signals to userspace that this enclave needs to be destroyed.
///
/// The enclave must not rely on userspace to terminate other threads still
/// running. Similarly, the enclave must not trust that it will no longer
/// be entered by userspace, and it must safeguard against that in the
/// entrypoints.
///
/// If `panic` is set to `true`, the enclave has exited due to a panic
/// condition. If the enclave was running in debug mode, the enclave may
/// have output a debug message according to the calling convention.
pub fn exit(panic: bool) -> ! { unimplemented!() }
/// Wait for an event to occur, or check if an event is currently pending.
///
/// `timeout` must be [`WAIT_NO`] or [`WAIT_INDEFINITE`]. If it is another
/// value, userspace will return an error.
///
/// If `timeout` is [`WAIT_INDEFINITE`], this call will block and return
/// once a matching event is queued on this TCS. If `timeout` is
/// [`WAIT_NO`], this call will return immediately, and the return value
/// will indicate if an event was pending. If it was, it has been dequeued.
/// If not, the [`WouldBlock`] error value will be returned.
///
/// A matching event is one whose bits are equal to or a subset of
/// `event_mask`. If `event_mask` is `0`, this call will never return due
/// to an event. If `timeout` is also [`WAIT_INDEFINITE`], this call will
/// simply never return.
///
/// Enclaves must not assume that this call only returns in response to
/// valid events generated by the enclave. This call may return for invalid
/// event sets, or before `timeout` has expired even though no event is
/// pending.
///
/// When executed synchronously, this gives userspace an opportunity to
/// schedule something else in a cooperative multitasking environment.
///
/// When executed asynchronously, this may trigger an
/// [`EV_RETURNQ_NOT_EMPTY`] event on this or other TCSes. It is not
/// recommended to execute this call asynchronously with a `timeout` value
/// other than [`WAIT_NO`].
///
/// [`WAIT_NO`]: constant.WAIT_NO.html
/// [`WAIT_INDEFINITE`]: constant.WAIT_INDEFINITE.html
/// [`EV_RETURNQ_NOT_EMPTY`]: constant.EV_RETURNQ_NOT_EMPTY.html
/// [`WouldBlock`]: enum.Error.html#variant.WouldBlock
pub fn wait(event_mask: u64, timeout: u64) -> (Result, u64) { unimplemented!() }
/// Send an event to one or all TCSes.
///
/// If `tcs` is `None`, send the event `event_set` to all TCSes of this
/// enclave, otherwise, send it to the TCS specified in `tcs`.
///
/// # Error
///
/// This will return the [`InvalidInput`] error if `tcs` is set but doesn't
/// specify a valid TCS address.
///
/// [`InvalidInput`]: enum.Error.html#variant.InvalidInput
pub fn send(event_set: u64, tcs: Option<Tcs>) -> Result { unimplemented!() }
}
/// # Miscellaneous
impl Usercalls {
/// This returns the number of nanoseconds since midnight UTC on January 1,
/// 1970\. The enclave must not rely on the accuracy of this time for
/// security purposes, such as checking credential expiry or preventing
/// rollback.
pub fn insecure_time() -> u64 { unimplemented!() }
}
/// # Memory
///
/// The enclave must not use any memory outside the enclave, except for memory
/// explicitly returned from usercalls. You can obtain arbitrary memory in
/// userspace using [`alloc`](#method.alloc).
impl Usercalls {
/// Request user memory.
///
/// Request an allocation in user memory of size `size` and with alignment
/// `align`. If succesful, a pointer to this memory will be returned. The
/// enclave must check the pointer is correctly aligned and that the entire
/// range of memory pointed to is outside the enclave.
///
/// It is an error to call this function with `size` equal to `0`.
pub fn alloc(size: usize, alignment: usize) -> (Result, *mut u8) { unimplemented!() }
/// Free user memory.
///
/// This must be called to deallocate memory in userspace. The pointer
/// `ptr` must have previously been returned by a usercall. The `size` and
/// `alignment` specified must exactly match what was allocated. This
/// function must be called exactly once for each user memory buffer.
///
/// Calling this function with `size` equal to `0` is a no-op.
pub fn free(ptr: *mut u8, size: usize, alignment: usize) { unimplemented!() }
}
/// Asynchronous usercall specification.
///
/// An asynchronous usercall allows an enclave to submit a usercall without
/// exiting the enclave. This is necessary since enclave entries and exists are
/// slow (see academic work on [SCONE], [HotCalls]). In addition, the enclave
/// can perform other tasks while it waits for the usercall to complete. Those
/// tasks may include issuing other usercalls, either synchronously or
/// asynchronously.
///
/// Two [MPSC queues] are [allocated per enclave]. One queue is used by any
/// enclave thread to submit usercalls to userspace. Userspace will read the
/// calls from this queue and handle them. Another queue is used by userspace
/// to return completed usercalls to the enclave.
///
/// Each call is identified by an enclave-specified `id`. Userspace must
/// provide the same `id` when returning. The enclave must not submit multiple
/// concurrent usercalls with the same `id`, but it may reuse an `id` once the
/// original usercall with that `id` has returned.
///
/// *TODO*: Add diagram.
///
/// [MPSC queues]: struct.FifoDescriptor.html
/// [allocated per enclave]: ../struct.Usercalls.html#method.async_queues
/// [SCONE]: https://www.usenix.org/conference/osdi16/technical-sessions/presentation/arnautov
/// [HotCalls]: http://www.ofirweisse.com/ISCA17_Ofir_Weisse.pdf
///
/// # Enclave/userspace synchronization
///
/// When the enclave needs to wait on a queue, it executes the [`wait()`]
/// usercall synchronously, specifying [`EV_USERCALLQ_NOT_FULL`],
/// [`EV_RETURNQ_NOT_EMPTY`], or both in the `event_mask`. Userspace will wake
/// any or all threads waiting on the appropriate event when it is triggered.
///
/// When userspace needs to wait on a queue, it will park the current thread
/// (or do whatever else is appropriate for the synchronization model currently
/// in use by userspace). Any synchronous usercall will wake the blocked thread
/// (or otherwise signal that either queue is ready).
///
/// [`wait()`]: ../struct.Usercalls.html#method.wait
/// [`EV_USERCALLQ_NOT_FULL`]: ../constant.EV_USERCALLQ_NOT_FULL.html
/// [`EV_RETURNQ_NOT_EMPTY`]: ../constant.EV_RETURNQ_NOT_EMPTY.html
pub mod async {
use super::*;
use core::sync::atomic::AtomicUsize;
/// An identified usercall.
#[repr(C)]
#[derive(Copy, Clone)]
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub struct Usercall {
/// `0` indicates this slot is empty.
pub id: u64,
/// The elements correspond to the RDI, RSI, RDX, R8, and R9 registers
/// in the synchronous calling convention.
pub args: (u64, u64, u64, u64, u64)
}
/// The return value of an identified usercall.
#[repr(C)]
#[derive(Copy, Clone)]
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub struct Return {
/// `0` indicates this slot is empty.
pub id: u64,
/// The elements correspond to the RSI and RDX registers in the
/// synchronous calling convention.
pub value: (u64, u64)
}
/// A circular buffer used as a FIFO queue with atomic reads and writes.
///
/// The read offset is the element that was most recently read by the
/// receiving end of the queue. The write offset is the element that was
/// most recently written by the sending end. If the two offsets are equal,
/// the queue is either empty or full.
///
/// The size of the buffer is such that not all the bits of the offset are
/// necessary to encode the current offset. The next highest unused bit is
/// used to keep track of the number of times the offset has wrapped
/// around. If the offsets are the same and the bit is the same in the read
/// and write offsets, the queue is empty. If the bit is different in the
/// read and write offsets, the queue is full.
///
/// The following procedures will operate the queues in a multiple producer
/// single consumer (MPSC) fashion.
///
/// ## Push operation
///
/// To push an element onto the queue:
///
/// 1. Load the current offsets.
/// 2. If the queue is full, wait, then go to step 1.
/// 3. Add 1 to the write offset and do an atomic compare-and-swap (CAS)
/// with the current offsets. If the CAS was not succesful, go to step
/// 1\.
/// 4. Write the data, then the `id`.
/// 5. If the queue was empty in step 1, signal the reader to wake up.
///
/// ## Pop operation
///
/// To pop an element off the queue:
///
/// 1. Load the current offsets.
/// 2. If the queue is empty, wait, then go to step 1.
/// 3. Add 1 to the read offset.
/// 4. Read the `id` at the new read offset.
/// 5. If `id` is `0`, go to step 4 (spin). Spinning is OK because data is
/// expected to be written imminently.
/// 6. Read the data, then store `0` in the `id`.
/// 7. Store the new read offset.
/// 8. If the queue was full in step 1, signal the writer to wake up.
#[repr(C)]
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
pub struct FifoDescriptor<T> {
/// Pointer to the queue memory. Must have a size of
/// `len * size_of::<T>()` bytes and have alignment `align_of::<T>`.
pub data: *mut T,
/// The number of elements pointed to by `data`. Must be a power of two
/// less than or equal to 2³¹.
pub len: usize,
/// Actually a `(u32, u32)` tuple, aligned to allow atomic operations
/// on both halves simultaneously. The first element (low dword) is
/// the read offset and the second element (high dword) is the write
/// offset.
pub offsets: *const AtomicUsize,
}
// not using `#[derive]` because that would require T: Clone
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
impl<T> Clone for FifoDescriptor<T> {
fn clone(&self) -> Self {
*self
}
}
// not using `#[derive]` because that would require T: Copy
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
impl<T> Copy for FifoDescriptor<T> {}
/// # Asynchronous usercalls
///
/// *Due to `rustdoc`, this section may appear at the top of the
/// `Usercalls` documentation. You might want to read the other sections
/// first and then come back to this one.*
///
/// See also the [`async` module](async/index.html) documentation.
impl Usercalls {
/// Request FIFO queues for asynchronous usercalls. `usercall_queue`
/// and `return_queue` must point to valid user memory with the correct
/// size and alignment for their types. On return, userspace will have
/// filled these structures with information about the queues. A single
/// set of queues will be allocated per enclave. Once this usercall has
/// returned succesfully, calling this usercall again is equivalent to
/// calling `exit(true)`.
///
/// May fail if the platform does not support asynchronous usercalls.
///
/// The enclave must ensure that the data pointed to in the fields of
/// [`FifoDescriptor`] is outside the enclave.
///
/// [`FifoDescriptor`]: async/struct.FifoDescriptor.html
pub fn async_queues(usercall_queue: *mut FifoDescriptor<Usercall>, return_queue: *mut FifoDescriptor<Return>) -> Result { unimplemented!() }
}
}
]; ] ];
// docs: Just render the docs verbatim
macro_rules! docs {
($($tt:tt)*) => ($($tt)*)
}
#[cfg(feature = "docs")]
invoke_with_abi_spec!(docs);
// types: flatten the module structure and ignore any items that are not types.
macro_rules! types {
// flatten modules
($(#[$meta:meta])* pub mod $modname:ident { $($contents:tt)* } $($remainder:tt)*) =>
{ types!($($contents)*); types!($($remainder)*); };
// ignore impls
($(#[$meta:meta])* impl Usercalls { $($contents:tt)* } $($remainder:tt)* ) =>
{ types!($($remainder)*); };
// ignore `struct Usercalls`
($(#[$meta:meta])* pub struct Usercalls; $($remainder:tt)* ) =>
{ types!($($remainder)*); };
// ignore free functions
($(#[$meta:meta])* pub fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)* { unimplemented!() } $($remainder:tt)* ) =>
{ types!($($remainder)*); };
// ignore use statements
(use $($tt:tt)::*; $($remainder:tt)* ) =>
{ types!($($remainder)*); };
// copy all other items verbatim
($item:item $($remainder:tt)*) =>
{ $item types!($($remainder)*); };
() => {};
}
#[cfg(not(feature = "docs"))]
invoke_with_abi_spec!(types);
// Define a macro that will call a second macro providing the list of all
// function declarations inside all `impl Usercalls` blocks.
macro_rules! define_invoke_with_usercalls {
// collect all usercall function declarations in a list
(@ [$($accumulated:tt)*] $(#[$meta1:meta])* impl Usercalls { $($(#[$meta2:meta])* pub fn $f:ident($($n:ident: $t:ty),*) $(-> $r:ty)* { unimplemented!() } )* } $($remainder:tt)* ) =>
{ define_invoke_with_usercalls!(@ [$($accumulated)* $(fn $f($($n: $t),*) $(-> $r)*;)*] $($remainder)*); };
// visit modules
(@ $accumulated:tt $(#[$meta:meta])* pub mod $modname:ident { $($contents:tt)* } $($remainder:tt)*) =>
{ define_invoke_with_usercalls!(@ $accumulated $($contents)* $($remainder)*); };
// ignore all other items
(@ $accumulated:tt $item:item $($remainder:tt)*) =>
{ define_invoke_with_usercalls!(@ $accumulated $($remainder)*); };
// Define the macro
(@ $accumulated:tt) => {
/// Call the macro `$m`, passing a semicolon-separated list of usercall
/// function declarations.
///
/// The passed in macro could for example use the following pattern:
///
/// ```text
/// ($(fn $f:ident($($n:ident: $t:ty),*) $(-> $r:tt)*; )*)
/// ```
#[macro_export]
#[cfg_attr(feature = "rustc-dep-of-std", unstable(feature = "sgx_platform", issue = "56975"))]
macro_rules! invoke_with_usercalls {
($m:ident) => { $m! $accumulated; }
}
};
// start collection with an empty list
($($tt:tt)*) => {
define_invoke_with_usercalls!(@ [] $($tt)*);
}
}
#[cfg(not(feature = "docs"))]
invoke_with_abi_spec!(define_invoke_with_usercalls);
|