├── .gitignore ├── LICENSE ├── Makefile ├── README ├── able.c ├── config.mk.def ├── host.c ├── host.h ├── libable_shim.c ├── term.c ├── term.h ├── trap.c └── trap.h /.gitignore: -------------------------------------------------------------------------------- 1 | able 2 | config.mk 3 | *.o 4 | *~ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Mark Smith 2 | Copyright (c) 2015 Ryan Siddle 3 | Copyright (c) 2015 Merj Ltd 4 | 5 | Permission to use, copy, modify, and distribute this software for any purpose 6 | with or without fee is hereby granted, provided that the above copyright notice 7 | and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 13 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 14 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 15 | THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN?=able 2 | SRCS=libable_shim.c trap.c host.c term.c able.c 3 | LIBS=-lable_misc -lable -lpthread 4 | 5 | .include "config.mk" 6 | 7 | OBJS+=${SRCS:N*.h:R:S/$/.o/} 8 | 9 | .PHONY: build clean install uninstall 10 | 11 | build: ${BIN} 12 | 13 | clean: 14 | -rm -vf ${OBJS} ${BIN} 15 | -rm -vf ${BIN}.core 16 | 17 | install: ${BIN} 18 | @mkdir -p ${BINDIR} 19 | install -m 0755 ${BIN} ${BINDIR}/${BIN} 20 | 21 | uninstall: 22 | -rm -vf ${BINDIR}/${BIN} 23 | 24 | ${BIN}: ${OBJS} 25 | ${LD} ${LDFLAGS} -o ${BIN} ${OBJS} ${LIBS} 26 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | able 2 | 3 | able implements an efficient, portable and secure general-purpose virtual 4 | machine and virtual network using libable 5 | 6 | REQUIREMENTS 7 | 8 | BSD Make 9 | Clang 10 | libable 11 | libable_misc 12 | 13 | GETTING STARTED 14 | 15 | Building and installing on OpenBSD 16 | 17 | $ cp config.mk.def config.mk 18 | $ make install 19 | ... 20 | $ 21 | 22 | Building and installing on Ubuntu 23 | 24 | # apt install bmake clang 25 | ... 26 | # exit 27 | $ cp config.mk.def config.mk 28 | $ bmake -DCOMPAT_LINUX install 29 | ... 30 | $ 31 | 32 | Building and installing on macOS 33 | 34 | # brew install bmake 35 | ... 36 | # exit 37 | $ cp config.mk.def config.mk 38 | $ bmake -DCOMPAT_MACOS install 39 | ... 40 | $ 41 | 42 | GETTING INVOLVED 43 | 44 | Contact Details 45 | 46 | Find us online at ablevm.org or email us at team@ablevm.org 47 | 48 | Code of Conduct 49 | 50 | Respect each other and please don't spam 51 | 52 | LICENSE 53 | 54 | ISC-style license 55 | 56 | DETAILS 57 | 58 | Extended Instruction Set 59 | 60 | 84 now 61 | 85 reset 62 | 86 depth 63 | FE debug 64 | -------------------------------------------------------------------------------- /able.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "trap.h" 16 | #include "host.h" 17 | #include "term.h" 18 | 19 | extern char *__progname; 20 | 21 | void 22 | usage() { 23 | fprintf(stderr, "usage: %s [-h] [-d size] [-c size] [-r size] [-m size] [-b base] [file]\n", __progname); 24 | exit(1); 25 | } 26 | 27 | long 28 | eatoi(const char *s) { 29 | long v; 30 | char *ep; 31 | v = strtol(s, &ep, 10); 32 | switch (ep[0]) { 33 | case 'G': 34 | v *= 1024; 35 | case 'M': 36 | v *= 1024; 37 | case 'K': 38 | case 'B': 39 | v *= 1024; 40 | } 41 | return v; 42 | } 43 | 44 | int 45 | main(int argc, char *argv[]) { 46 | bool dopt; 47 | dopt = false; 48 | uint16_t doptarg; 49 | doptarg = 32; 50 | bool copt; 51 | copt = false; 52 | uint16_t coptarg; 53 | coptarg = 32; 54 | bool ropt; 55 | ropt = false; 56 | uint8_t roptarg; 57 | roptarg = 32; 58 | bool mopt; 59 | mopt = false; 60 | uint64_t moptarg; 61 | moptarg = 0; 62 | bool bopt; 63 | bopt = false; 64 | uint64_t boptarg; 65 | boptarg = 0; 66 | 67 | char c; 68 | while ((c = getopt(argc, argv, "hd:c:r:m:b:")) != -1) { 69 | switch (c) { 70 | case 'd': 71 | if (dopt) 72 | usage(); 73 | dopt = true; 74 | doptarg = eatoi(optarg); 75 | if (doptarg > UINT16_MAX) 76 | usage(); 77 | break; 78 | case 'c': 79 | if (copt) 80 | usage(); 81 | copt = true; 82 | coptarg = eatoi(optarg); 83 | if (coptarg > UINT16_MAX) 84 | usage(); 85 | break; 86 | case 'r': 87 | if (ropt) 88 | usage(); 89 | ropt = true; 90 | roptarg = eatoi(optarg); 91 | if (roptarg > UINT8_MAX) 92 | usage(); 93 | break; 94 | case 'm': 95 | if (mopt) 96 | usage(); 97 | mopt = true; 98 | moptarg = eatoi(optarg); 99 | break; 100 | case 'b': 101 | if (bopt) 102 | usage(); 103 | bopt = true; 104 | boptarg = eatoi(optarg); 105 | break; 106 | case 'h': 107 | default: 108 | usage(); 109 | } 110 | } 111 | 112 | argc -= optind; 113 | argv += optind; 114 | 115 | char *ifn; 116 | switch (argc) { 117 | case 0: 118 | asprintf(&ifn, "%s.img", __progname); 119 | break; 120 | case 1: 121 | ifn = argv[0]; 122 | break; 123 | default: 124 | usage(); 125 | } 126 | 127 | if (moptarg == 0) { 128 | struct stat ifs; 129 | int y; 130 | y = stat(ifn, &ifs); 131 | if (y == -1) 132 | err(3, "%s", ifn); 133 | moptarg = ifs.st_size; 134 | } 135 | 136 | if (boptarg > moptarg) 137 | usage(); 138 | 139 | int ifd; 140 | ifd = open(ifn, O_RDWR); 141 | if (ifd == -1) 142 | err(3, "open"); 143 | uint8_t *m; 144 | m = mmap(NULL, moptarg, PROT_READ|PROT_WRITE, MAP_SHARED, ifd, 0); 145 | if (m == MAP_FAILED) 146 | err(3, "mmap"); 147 | close(ifd); 148 | 149 | // virtual machine config 150 | 151 | // terminal input 152 | term_recv_t i0; 153 | memset(&i0, 0, sizeof(i0)); 154 | 155 | able_task_t ti0; 156 | memset(&ti0, 0, sizeof(ti0)); 157 | ti0.ef = (able_task_exec_t)term_recv_exec; 158 | ti0.t = &i0; 159 | 160 | // terminal output 161 | void *o0s; 162 | o0s = malloc(1024); 163 | if (o0s == NULL) 164 | err(4, "malloc"); 165 | term_send_t o0; 166 | memset(&o0, 0, sizeof(o0)); 167 | able_node_init(&o0.n); 168 | o0.s = o0s; 169 | o0.sc = 1024; 170 | 171 | able_task_t to0; 172 | memset(&to0, 0, sizeof(to0)); 173 | to0.ef = (able_task_exec_t)term_send_exec; 174 | to0.t = &o0; 175 | 176 | // host 0 177 | able_node_t h0n; 178 | able_node_init(&h0n); 179 | able_edge_t h0e[256]; 180 | memset(h0e, 0, sizeof(h0e)); 181 | able_misc_host_buff_t h0b[256]; 182 | memset(h0b, 0, sizeof(h0b)); 183 | able_link_t h0l_[256]; 184 | memset(h0l_, 0, sizeof(h0l_)); 185 | void *h0l[256]; 186 | for (int i = 0; i < 256; i++) 187 | h0l[i] = &h0l_[i]; 188 | int64_t *h0d; 189 | h0d = calloc(sizeof(int64_t), doptarg); 190 | if (h0d == NULL) 191 | err(4, "calloc"); 192 | uint64_t *h0c; 193 | h0c = calloc(sizeof(uint64_t), coptarg); 194 | if (h0c == NULL) 195 | err(4, "calloc"); 196 | uint64_t *h0r; 197 | h0r = calloc(sizeof(uint64_t), roptarg); 198 | if (h0r == NULL) 199 | err(4, "calloc"); 200 | able_misc_host_t h0; 201 | memset(&h0, 0, sizeof(h0)); 202 | h0.n = &h0n; 203 | h0.e = h0e; 204 | h0.ec = 256; 205 | h0.b = h0b; 206 | h0.bc = 256; 207 | h0.l = h0l; 208 | h0.lc = 256; 209 | h0.c.m = m + boptarg; 210 | h0.c.mc = moptarg - boptarg; 211 | h0.c.d = h0d; 212 | h0.c.dc = doptarg; 213 | h0.c.c = h0c; 214 | h0.c.cc = coptarg; 215 | h0.c.r = h0r; 216 | h0.c.rc = roptarg; 217 | h0.ts = 1000; 218 | host_init(&h0); 219 | 220 | able_task_t th0; 221 | memset(&th0, 0, sizeof(th0)); 222 | th0.ef = (able_task_exec_t)host_exec; 223 | th0.t = &h0; 224 | 225 | trap_data.u = &h0; 226 | signal(SIGINT, trap); 227 | 228 | // virtual network config 229 | 230 | // host 0 231 | able_link_join(&i0.l, &h0.e[0], 1, h0.n); 232 | able_link_join(h0.l[0], &o0.e, 0, &o0.n); 233 | able_link_join(&o0.l, &h0.e[0], 0, h0.n); 234 | 235 | able_task_fork_exec(&ti0); 236 | able_task_fork_exec(&to0); 237 | able_task_exec(&th0); 238 | 239 | // should not happen 240 | return 2; 241 | } 242 | -------------------------------------------------------------------------------- /config.mk.def: -------------------------------------------------------------------------------- 1 | PREFIX?=/usr/local 2 | BINDIR=${PREFIX}/bin 3 | INCDIR=${PREFIX}/include 4 | LIBDIR=${PREFIX}/lib 5 | MANDIR=${PREFIX}/man 6 | 7 | CC=clang 8 | CFLAGS=-I${INCDIR} -g -O2 -std=c11 -pedantic -Wall -Wno-zero-length-array -Wno-gnu-label-as-value -Wno-gnu-designator -Wno-gnu-empty-struct 9 | 10 | .ifdef DEBUG 11 | CFLAGS+=-DDEBUG=${DEBUG} 12 | .endif 13 | 14 | .ifdef COMPAT_LINUX 15 | CFLAGS+=-DABLE_COMPAT_LINUX -D_GNU_SOURCE 16 | .endif 17 | 18 | LD=${CC} 19 | LDFLAGS=-L${LIBDIR} 20 | -------------------------------------------------------------------------------- /host.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "host.h" 6 | #include 7 | #include 8 | #include 9 | #include "trap.h" 10 | 11 | #define DSO ABLE_MISC_CORE_DSO 12 | #define DSI ABLE_MISC_CORE_DSI 13 | 14 | #define DS0 ABLE_MISC_CORE_DSV(&host->c, 1) 15 | #define DS1 ABLE_MISC_CORE_DSV(&host->c, 2) 16 | #define CS0 ABLE_MISC_CORE_CSV(&host->c, 1) 17 | 18 | #define DSR(C) \ 19 | (C)->dp = 0; 20 | 21 | #define CSR(C) \ 22 | (C)->cp = 0; 23 | 24 | #define T(C, Y) { \ 25 | int y; \ 26 | y = (Y); \ 27 | if (DSO(C, 2)) { \ 28 | DSR(C); \ 29 | CSR(C); \ 30 | y = 9; \ 31 | } \ 32 | DSI(C); \ 33 | DS0 = host->c.p - 1; \ 34 | DSI(C); \ 35 | DS0 = y; \ 36 | host->c.p = 0; \ 37 | } 38 | 39 | #define P(C, Y) { \ 40 | int64_t d0; \ 41 | d0 = (C)->dp < 1 ? 0 : DS0; \ 42 | int64_t d1; \ 43 | d1 = (C)->dp < 2 ? 0 : DS1; \ 44 | uint64_t c0; \ 45 | c0 = (C)->cp < 1 ? 0 : CS0; \ 46 | fprintf(stderr, "%02"PRIX8"(%d) %"PRId64" %08"PRIX64" %"PRId64"/%"PRIX64" %"PRId64"/%"PRIX64" (%"PRIu16"); %"PRId64"/%"PRIX64" (%"PRIu16")\n", \ 47 | (C)->i, \ 48 | (Y), \ 49 | (C)->ts, \ 50 | (C)->p, \ 51 | d1, d1, \ 52 | d0, d0, \ 53 | (C)->dp, \ 54 | c0, c0, \ 55 | (C)->cp); \ 56 | } 57 | 58 | int 59 | host_init(able_misc_host_t *host) { 60 | T(&host->c, 1); 61 | return 0; 62 | } 63 | 64 | int 65 | host_exec(able_misc_host_t *host) { 66 | for (;;) { 67 | if (host == trap_data.u) { 68 | int q; 69 | q = atomic_exchange(&trap_data.q, 0); 70 | if (q == 1) { 71 | DSR(&host->c); 72 | CSR(&host->c); 73 | T(&host->c, 0); 74 | } 75 | } 76 | int y; 77 | y = able_misc_host_exec(host); 78 | switch (y) { 79 | case -1: // end of timeslice 80 | able_misc_host_node_wait_shim(host->n, NULL, NULL); 81 | return y; 82 | case -2: // bad memory access 83 | T(&host->c, 2); 84 | break; 85 | case -3: // divide by zero 86 | T(&host->c, 3); 87 | break; 88 | case -4: // illegal instruction 89 | switch (host->c.i) { 90 | case 0x84: { // now ( - t) 91 | if (DSO(&host->c, 1)) 92 | return -7; 93 | struct timespec ts; 94 | clock_gettime(CLOCK_REALTIME, &ts); 95 | DSI(&host->c); 96 | DS0 = ts.tv_sec * 1000000000 + ts.tv_nsec; 97 | break; 98 | } 99 | case 0x85: // reset ( - *) 100 | DSR(&host->c); 101 | CSR(&host->c); 102 | T(&host->c, 0); 103 | break; 104 | case 0x86: { // depth ( - u1 u2) 105 | if (DSO(&host->c, 2)) { 106 | DSR(&host->c); 107 | CSR(&host->c); 108 | T(&host->c, 7); 109 | break; 110 | } 111 | uint8_t dp; 112 | dp = host->c.dp; 113 | uint8_t cp; 114 | cp = host->c.cp; 115 | DSI(&host->c); 116 | DS0 = cp; 117 | DSI(&host->c); 118 | DS0 = dp; 119 | break; 120 | } 121 | case 0xfe: // debug ( - ~) 122 | P(&host->c, y); 123 | break; 124 | default: 125 | #ifdef DEBUG 126 | P(&host->c, y); 127 | #endif 128 | T(&host->c, 4); 129 | break; 130 | } 131 | break; 132 | case -5: // start of timeslice 133 | return y; 134 | case -6: // stack underflow 135 | DSR(&host->c); 136 | CSR(&host->c); 137 | T(&host->c, 6); 138 | break; 139 | case -7: // stack overflow 140 | DSR(&host->c); 141 | CSR(&host->c); 142 | T(&host->c, 7); 143 | break; 144 | case -8: // illegal register 145 | T(&host->c, 8); 146 | break; 147 | } 148 | } 149 | 150 | // should not happen 151 | return 1; 152 | } 153 | -------------------------------------------------------------------------------- /host.h: -------------------------------------------------------------------------------- 1 | int 2 | host_init(able_misc_host_t *host); 3 | 4 | int 5 | host_exec(able_misc_host_t *host); 6 | -------------------------------------------------------------------------------- /libable_shim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int 6 | able_link_node_post_shim(void *node, const able_edge_t *edge) { 7 | return able_node_post(node, edge); 8 | } 9 | 10 | int 11 | able_misc_host_node_wait_shim(void *node, const able_edge_t *edge, const struct timespec *time) { 12 | return able_node_wait(node, edge, time); 13 | } 14 | 15 | int 16 | able_misc_host_link_send_shim(void *link, const void *data, size_t size) { 17 | return able_mesg_link_send(link, data, size); 18 | } 19 | -------------------------------------------------------------------------------- /term.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "term.h" 5 | #include 6 | #include 7 | #include 8 | 9 | int 10 | term_recv_exec(term_recv_t *term_recv) { 11 | struct { 12 | uint8_t t; 13 | uint8_t b[1024]; 14 | } mb; 15 | int bc; 16 | bc = 0; 17 | int q; 18 | q = 0; 19 | while (q == 0) { 20 | int c; 21 | c = getchar(); 22 | if (c == EOF && feof(stdin)) { 23 | q = 1; 24 | c = 0; 25 | } 26 | if (c != '\n') 27 | mb.b[bc++] = c; 28 | if (q == 1 || c == '\n' || bc == sizeof(mb.b)) { 29 | mb.t = 0; 30 | while (able_mesg_link_send(&term_recv->l, &mb, sizeof(mb.t) + bc) < 0); 31 | bc = 0; 32 | } 33 | } 34 | 35 | return 0; 36 | } 37 | 38 | int 39 | term_send_exec(term_send_t *term_send) { 40 | uint8_t *r; 41 | r = NULL; 42 | size_t rc; 43 | rc = 0; 44 | for (;;) { 45 | if (rc == 0) { 46 | int y; 47 | while((y = able_edge_clip(&term_send->e, term_send->s, term_send->sc)) < 0); 48 | if (y == 0) 49 | r = term_send->s; 50 | while((rc += able_edge_recv(&term_send->e)) == 0) 51 | able_node_wait(&term_send->n, &term_send->e, NULL); 52 | } 53 | able_mesg_t *m; 54 | if (rc < sizeof(*m)) 55 | continue; 56 | m = (able_mesg_t *)r; 57 | r += m->sc; 58 | rc -= m->sc; 59 | struct { 60 | uint8_t t; 61 | uint8_t b[0]; 62 | } *mb; 63 | mb = (void *)m->b; 64 | switch (mb->t) { 65 | case 0: { // type 66 | size_t bc; 67 | bc = m->bc - sizeof(mb->t); 68 | if (bc == 0) 69 | break; 70 | bool f; 71 | f = false; 72 | int i; 73 | for (i = 0; i < bc; i++) { 74 | char c; 75 | c = mb->b[i]; 76 | if (c == '\n') 77 | f = true; 78 | putchar(c); 79 | } 80 | if (f) 81 | fflush(stdout); 82 | break; 83 | } 84 | case 1: // bye 85 | exit(0); 86 | break; 87 | } 88 | } 89 | 90 | // should not happen 91 | return 1; 92 | } 93 | -------------------------------------------------------------------------------- /term.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | able_link_t l; 3 | } term_recv_t; 4 | 5 | int 6 | term_recv_exec(term_recv_t *term_recv); 7 | 8 | typedef struct { 9 | able_node_t n; 10 | able_edge_t e; 11 | able_link_t l; 12 | uint8_t *s; 13 | size_t sc; 14 | } term_send_t; 15 | 16 | int 17 | term_send_exec(term_send_t *term_send); 18 | -------------------------------------------------------------------------------- /trap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "trap.h" 3 | #include 4 | 5 | trap_data_t trap_data; 6 | 7 | void 8 | trap(int kind) { 9 | int q; 10 | q = atomic_fetch_add(&trap_data.q, 1); 11 | if (q == 1) 12 | exit(4); 13 | } 14 | -------------------------------------------------------------------------------- /trap.h: -------------------------------------------------------------------------------- 1 | typedef struct { 2 | atomic_int q; 3 | void *u; 4 | } trap_data_t; 5 | 6 | extern trap_data_t trap_data; 7 | 8 | void 9 | trap(int kind); 10 | --------------------------------------------------------------------------------