CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
microvium_port.h
Go to the documentation of this file.
1/*
2
3# Instructions
4
5Make a copy of this file and name it exactly `microvium_port.h`. Put the copy
6somewhere in your project where it is accessible by a `#include
7"microvium_port.h"` directive.
8
9Customize your copy of the port file with platform-specific configurations.
10
11The recommended workflow is to keep the vm source files separate from your
12custom port file, so that you can update the vm source files regularly with bug
13fixes and improvement from the original github or npm repository.
14
15*/
16#pragma once
17
18#include <assert.h>
19#include <cheri-builtins.h>
20#include <compartment.h>
21#include <stdbool.h>
22#include <stdint.h>
23#include <string.h>
24
25/**
26 * The version of the port interface that this file is implementing.
27 */
28#define MVM_PORT_VERSION 1
29
30/**
31 * Number of bytes to use for the stack.
32 *
33 * Note: the that stack is fixed-size, even though the heap grows dynamically
34 * as-needed.
35 */
36#define MVM_STACK_SIZE 256
37
38/**
39 * When more space is needed for the VM heap, the VM will malloc blocks with a
40 * minimum of this size from the host.
41 *
42 * Note that the VM can also allocate blocks larger than this. It will do so if
43 * it needs a larger contiguous space than will fit in a standard block, and
44 * also during heap compaction (`runGC`) where it defragments the heap into as
45 * few mallocd blocks as possible to make access more efficient.
46 */
47#define MVM_ALLOCATION_BUCKET_SIZE 256
48
49/**
50 * The maximum size of the virtual heap before an MVM_E_OUT_OF_MEMORY error is
51 * given.
52 *
53 * When the VM reaches this level, it will first try to perform a garbage
54 * collection cycle. If a GC cycle does not free enough memory, a fatal
55 * MVM_E_OUT_OF_MEMORY error is given.
56 *
57 * Note: this is the space in the virtual heap (the amount consumed by
58 * allocations in the VM), not the physical space malloc'd from the host, the
59 * latter of which can peak at roughly twice the virtual space during a garbage
60 * collection cycle in the worst case.
61 */
62#define MVM_MAX_HEAP_SIZE 1024
63
64/**
65 * Set to 1 if a `void*` pointer is natively 16-bit (e.g. if compiling for
66 * 16-bit architectures). This allows some optimizations since then a native
67 * pointer can fit in a Microvium value slot.
68 */
69#define MVM_NATIVE_POINTER_IS_16_BIT 0
70
71/**
72 * Set to 1 to compile in support for floating point operations (64-bit). This
73 * adds significant cost in smaller devices, but is required if you want the VM
74 * to be compliant with the ECMAScript standard.
75 *
76 * When float support is disabled, operations on floats will throw.
77 */
78#define MVM_SUPPORT_FLOAT 0
79
80#if MVM_SUPPORT_FLOAT
81
82/**
83 * The type to use for double-precision floating point. Note that anything other
84 * than an IEEE 754 double-precision float is not compliant with the ECMAScript
85 * spec and results may not always be as expected. Also remember that the
86 * bytecode is permitted to have floating point literals embedded in it, and
87 * these must match the exact format specification used here if doubles are to
88 * persist correctly across a snapshot.
89 *
90 * Note that on some embedded systems, the `double` type is actually 32-bit, so
91 * this may need to be `long double` or whatever the equivalent 64-bit type is
92 * on your system.
93 */
94# define MVM_FLOAT64 double
95
96/**
97 * Value to use for NaN
98 */
99# define MVM_FLOAT64_NAN ((MVM_FLOAT64)(INFINITY * 0.0))
100
101#endif // MVM_SUPPORT_FLOAT
102
103/**
104 * Set to `1` to enable additional internal consistency checks, or `0` to
105 * disable them. Note that consistency at the API boundary is always checked,
106 * regardless of this setting. Consistency checks make the VM *significantly*
107 * bigger and slower, and are really only intended for testing.
108 */
109#define MVM_SAFE_MODE 0
110
111/**
112 * Set to `1` to enable extra validation checks of bytecode while executing.
113 * This is _beyond_ the basic version and CRC checks that are done upon loading,
114 * and should only be enabled if you expect bugs in the bytecode compiler.
115 */
116#define MVM_DONT_TRUST_BYTECODE 1
117
118/**
119 * Not recommended!
120 *
121 * Set to `1` to enable extra checks for pointer safety within the engine. In
122 * particular, this triggers a GC collection cycle at every new allocation in
123 * order to find potential dangling pointer issues, and each GC collection
124 * shifts the address space a little to invalidate native pointers early.
125 * This option is only intended for testing purposes.
126 */
127#define MVM_VERY_EXPENSIVE_MEMORY_CHECKS 0
128
129/**
130 * A long pointer is a type that can refer to either ROM or RAM. It is not size
131 * restricted.
132 *
133 * On architectures where bytecode is directly addressable with a normal
134 * pointer, this can just be `void*` (e.g. 32-bit architectures). On
135 * architectures where bytecode can be addressed with a special pointer, this
136 * might be something like `__data20 void*` (MSP430). On Harvard architectures
137 * such as AVR8 where ROM and RAM are in different address spaces,
138 * `MVM_LONG_PTR_TYPE` can be some integer type such as `uint32_t`, where you
139 * use part of the value to distinguish which address space and part of the
140 * value as the actual pointer value.
141 *
142 * The chosen representation/encoding of `MVM_LONG_PTR_TYPE` must be an integer
143 * or pointer type, such that `0`/`NULL` represents the null pointer.
144 *
145 * Microvium doesn't access data through pointers of this type directly -- it
146 * does so through macro operations in this port file.
147 */
148#define MVM_LONG_PTR_TYPE void *
149
150/**
151 * Convert a normal pointer to a long pointer
152 */
153#define MVM_LONG_PTR_NEW(p) ((MVM_LONG_PTR_TYPE)p)
154
155/**
156 * Truncate a long pointer to a normal pointer.
157 *
158 * This will only be invoked on pointers to VM RAM data.
159 */
160#define MVM_LONG_PTR_TRUNCATE(p) ((void *)p)
161
162/**
163 * Add an offset `s` in bytes onto a long pointer `p`. The result must be a
164 * MVM_LONG_PTR_TYPE.
165 *
166 * The maximum offset that will be passed is 16-bit.
167 *
168 * Offset may be negative
169 */
170#define MVM_LONG_PTR_ADD(p, s) \
171 ((MVM_LONG_PTR_TYPE)((uint8_t *)p + (ptrdiff_t)s))
172
173/**
174 * Subtract two long pointers to get an offset. The result must be a signed
175 * 16-bit integer of p2 - p1 (where p2 is the FIRST param).
176 */
177#define MVM_LONG_PTR_SUB(p2, p1) ((int16_t)((uint8_t *)p2 - (uint8_t *)p1))
178
179/*
180 * Read memory of 1 or 2 bytes
181 */
182#define MVM_READ_LONG_PTR_1(lpSource) (*((uint8_t *)lpSource))
183#define MVM_READ_LONG_PTR_2(lpSource) (*((uint16_t *)lpSource))
184
185/**
186 * Reference to an implementation of memcmp where p1 and p2 are LONG_PTR
187 */
188#define MVM_LONG_MEM_CMP(p1, p2, size) memcmp(p1, p2, size)
189
190/**
191 * Reference to an implementation of memcpy where `source` is a LONG_PTR
192 */
193#define MVM_LONG_MEM_CPY(target, source, size) memcpy(target, source, size)
194
195/**
196 * This is invoked when the virtual machine encounters a critical internal error
197 * and execution of the VM should halt.
198 *
199 * Note that API-level errors are communicated via returned error codes from
200 * each of the API functions and will not trigger a fatal error.
201 *
202 * Note: if malloc fails, this is considered a fatal error since many embedded
203 * systems cannot safely continue when they run out of memory.
204 *
205 * If you need to halt the VM without halting the host, consider running the VM
206 * in a separate RTOS thread, or using setjmp/longjmp to escape the VM without
207 * returning to it. Either way, the VM should NOT be allowed to continue
208 * executing after MVM_FATAL_ERROR (control should not return).
209 */
210#define MVM_FATAL_ERROR(vm, e) panic()
211
212/**
213 * Set MVM_ALL_ERRORS_FATAL to 1 to have the MVM_FATAL_ERROR handler called
214 * eagerly when a new error is encountered, rather than returning an error code
215 * from `mvm_call`. This is mainly for debugging the VM itself, since the
216 * MVM_FATAL_ERROR handler is called before unwinding the C stack.
217 */
218#define MVM_ALL_ERRORS_FATAL 0
219
220#define MVM_SWITCH(tag, upper) switch (tag)
221#define MVM_CASE(value) case value
222
223/**
224 * Macro that evaluates to true if the CRC of the given data matches the
225 * expected value. Note that this is evaluated against the bytecode, so lpData
226 * needs to be a long pointer type. If you don't want the overhead of validating
227 * the CRC, just return `true`.
228 */
229#define MVM_CHECK_CRC16_CCITT(lpData, size, expected) \
230 (crc16(lpData, size) == expected)
231
232static uint16_t crc16(MVM_LONG_PTR_TYPE lp, uint16_t size)
233{
234 uint16_t r = 0xFFFF;
235 while (size--)
236 {
237 r = (uint8_t)(r >> 8) | (r << 8);
238 r ^= MVM_READ_LONG_PTR_1(lp);
239 lp = MVM_LONG_PTR_ADD(lp, 1);
240 r ^= (uint8_t)(r & 0xff) >> 4;
241 r ^= (r << 8) << 4;
242 r ^= ((r & 0xff) << 4) << 1;
243 }
244 return r;
245}
246
247/**
248 * Set to 1 to compile in the ability to generate snapshots (mvm_createSnapshot)
249 */
250#define MVM_INCLUDE_SNAPSHOT_CAPABILITY 0
251
252/**
253 * Set to 1 to compile support for the debug API (mvm_dbg_*)
254 */
255#define MVM_INCLUDE_DEBUG_CAPABILITY 0
256
257#if MVM_INCLUDE_SNAPSHOT_CAPABILITY
258/**
259 * Calculate the CRC. This is only used when generating snapshots.
260 *
261 * Unlike MVM_CHECK_CRC16_CCITT, pData here is a pointer to RAM.
262 */
263# define MVM_CALC_CRC16_CCITT(pData, size) (crc16(pData, size))
264#endif // MVM_INCLUDE_SNAPSHOT_CAPABILITY
265
266/**
267 * On architectures like small ARM MCUs where there is a large address space
268 * (e.g. 32-bit) but only a small region of that is used for heap allocations,
269 * Microvium is more efficient if you can tell it the high bits of the addresses
270 * so it can store the lower 16-bits.
271 *
272 * If MVM_USE_SINGLE_RAM_PAGE is set to 1, then MVM_RAM_PAGE_ADDR must be
273 * the address of the page.
274 */
275#define MVM_USE_SINGLE_RAM_PAGE 0
276
277#if MVM_USE_SINGLE_RAM_PAGE
278/**
279 * Address of the RAM page to use, such that all pointers to RAM are between
280 * MVM_RAM_PAGE_ADDR and (MVM_RAM_PAGE_ADDR + 0xFFFF)
281 */
282# define MVM_RAM_PAGE_ADDR 0x12340000
283#endif
284
285/// All public functions are exported from the library.
286#define MVM_EXPORT __cheri_libcall
287
288/**
289 * Implementation of malloc and free to use.
290 *
291 * Note that MVM_CONTEXTUAL_FREE needs to accept null pointers as well.
292 *
293 * If MVM_USE_SINGLE_RAM_PAGE is set, pointers returned by MVM_CONTEXTUAL_MALLOC
294 * must always be within 64kB of MVM_RAM_PAGE_ADDR.
295 *
296 * The `context` passed to these macros is whatever value that the host passes
297 * to `mvm_restore`. It can be any value that fits in a pointer.
298 *
299 * Similarly to `malloc` and `calloc`, this will only ever
300 * block to wait for the quarantine to be processed.
301 */
302#define MVM_CONTEXTUAL_MALLOC(size, context) \
303 ({ \
304 Timeout t = {0, MALLOC_WAIT_TICKS}; \
305 void *ret = heap_allocate(&t, \
306 (AllocatorCapability)context, \
307 size, \
308 AllocateWaitRevocationNeeded); \
309 if (!__builtin_cheri_tag_get(ret)) \
310 { \
311 ret = NULL; \
312 } \
313 ret; \
314 })
315#define MVM_CONTEXTUAL_FREE(ptr, context) \
316 (void)heap_free((AllocatorCapability)context, ptr)
317
318/**
319 * Expose the timeout APIs.
320 */
321#define MVM_GAS_COUNTER
322
323/**
324 * Helper for Microvium to convert strings to integers.
325 *
326 * This is used only in the int to string coercion function, so is marked as
327 * always inline to ensure that we don't get a function call.
328 */
329__always_inline static int mvm_int_to_string_helper(char buffer[12],
330 int32_t value)
331{
332 char *insert = buffer;
333
334 // If this is negative, add a minus sign and negate it.
335 if (value < 0)
336 {
337 *(insert++) = '-';
338 value = 0 - value;
339 }
340 const char Digits[] = "0123456789";
341 // To skip leading zeroes, write the value backwards into this buffer and
342 // then scan it forward, skipping zeroes, inserting into the output buffer.
343 char tmp[10];
344 for (int i = sizeof(tmp) - 1; i >= 0; i--)
345 {
346 tmp[i] = Digits[value % 10];
347 value /= 10;
348 }
349 bool skipZero = true;
350 for (int i = 0; i < sizeof(tmp); i++)
351 {
352 char c = tmp[i];
353 if (skipZero && (c == '0'))
354 {
355 continue;
356 }
357 skipZero = false;
358 *(insert++) = c;
359 }
360 if (skipZero)
361 {
362 *(insert++) = '0';
363 }
364 return insert - buffer;
365}
366
367#define MVM_INT32TOSTRING(buffer, i) mvm_int_to_string_helper(buffer, i)
368
369/**
370 * Apply bounds to the buffer.
371 */
372#define MVM_POINTER_SET_BOUNDS(ptr, bounds) \
373 __builtin_cheri_bounds_set(ptr, bounds)
374
375/**
376 * Remove all permissions except load and global.
377 */
378#define MVM_POINTER_MAKE_IMMUTABLE(ptr) \
379 __builtin_cheri_perms_and(ptr, CHERI_PERM_GLOBAL | CHERI_PERM_LOAD)
#define MVM_LONG_PTR_TYPE
A long pointer is a type that can refer to either ROM or RAM.
static int mvm_int_to_string_helper(char buffer[12], int32_t value)
Helper for Microvium to convert strings to integers.
#define MVM_LONG_PTR_ADD(p, s)
Add an offset s in bytes onto a long pointer p.