vm.c (13833B)
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include "vm.h"
4 #include "syscall.h"
5 #include "abi.h"
6 #include "api.h"
7 #include "tab.h"
8 #include "screen.h"
9 #include "scene.h"
10
11 #define OP_LOAD 0x03
12 #define OP_MISC_MEM 0x0F
13 #define OP_OP_IMM 0x13
14 #define OP_AUIPC 0x17
15 #define OP_STORE 0x23
16 #define OP_OP 0x33
17 #define OP_LUI 0x37
18 #define OP_BRANCH 0x63
19 #define OP_JALR 0x67
20 #define OP_JAL 0x6F
21 #define OP_SYSTEM 0x73
22
23 static int32_t sign_extend(uint32_t value, uint32_t bits) {
24 uint32_t mask = (uint32_t)1 << (bits - 1);
25
26 return (int32_t)((value ^ mask) - mask);
27 }
28
29 static uint32_t shift_right_arith(uint32_t value, uint32_t amount) {
30 uint32_t result = value >> amount;
31
32 if (amount != 0 && (value & 0x80000000u) != 0) {
33 result |= ~(0xFFFFFFFFu >> amount);
34 }
35
36 return result;
37 }
38
39 static uint32_t mul_high_unsigned(uint32_t a, uint32_t b) {
40 uint32_t a_lo = a & 0xFFFFu;
41 uint32_t a_hi = a >> 16;
42 uint32_t b_lo = b & 0xFFFFu;
43 uint32_t b_hi = b >> 16;
44 uint32_t lo_lo = a_lo * b_lo;
45 uint32_t hi_lo = a_hi * b_lo;
46 uint32_t lo_hi = a_lo * b_hi;
47 uint32_t hi_hi = a_hi * b_hi;
48 uint32_t cross = (lo_lo >> 16) + (hi_lo & 0xFFFFu) + lo_hi;
49
50 return hi_hi + (hi_lo >> 16) + (cross >> 16);
51 }
52
53 static uint32_t mul_high_signed(uint32_t a, uint32_t b) {
54 uint32_t result = mul_high_unsigned(a, b);
55
56 if (a & 0x80000000u) {
57 result -= b;
58 }
59
60 if (b & 0x80000000u) {
61 result -= a;
62 }
63
64 return result;
65 }
66
67 static uint32_t mul_high_signed_unsigned(uint32_t a, uint32_t b) {
68 uint32_t result = mul_high_unsigned(a, b);
69
70 if (a & 0x80000000u) {
71 result -= b;
72 }
73
74 return result;
75 }
76
77 static uint32_t div_signed(uint32_t a, uint32_t b) {
78 if (b == 0) {
79 return 0xFFFFFFFFu;
80 }
81
82 if (a == 0x80000000u && b == 0xFFFFFFFFu) {
83 return a;
84 }
85
86 return (uint32_t)((int32_t)a / (int32_t)b);
87 }
88
89 static uint32_t rem_signed(uint32_t a, uint32_t b) {
90 if (b == 0) {
91 return a;
92 }
93
94 if (a == 0x80000000u && b == 0xFFFFFFFFu) {
95 return 0;
96 }
97
98 return (uint32_t)((int32_t)a % (int32_t)b);
99 }
100
101 static bool in_bounds(const ta_vm *vm, uint32_t addr, uint32_t size) {
102 return size <= vm->mem_size && addr <= vm->mem_size - size;
103 }
104
105 bool ta_vm_read8(const ta_vm *vm, uint32_t addr, uint8_t *out) {
106 if (!in_bounds(vm, addr, 1)) {
107 return false;
108 }
109
110 *out = vm->mem[addr];
111
112 return true;
113 }
114
115 bool ta_vm_read32(const ta_vm *vm, uint32_t addr, uint32_t *out) {
116 if (!in_bounds(vm, addr, 4)) {
117 return false;
118 }
119
120 const uint8_t *p = vm->mem + addr;
121
122 *out = (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
123
124 return true;
125 }
126
127 bool ta_vm_write32(ta_vm *vm, uint32_t addr, uint32_t value) {
128 if (!in_bounds(vm, addr, 4)) {
129 return false;
130 }
131
132 uint8_t *p = vm->mem + addr;
133
134 p[0] = (uint8_t)value;
135 p[1] = (uint8_t)(value >> 8);
136 p[2] = (uint8_t)(value >> 16);
137 p[3] = (uint8_t)(value >> 24);
138
139 return true;
140 }
141
142 static bool load_value(const ta_vm *vm, uint32_t addr, uint32_t size, uint32_t *out) {
143 if (!in_bounds(vm, addr, size)) {
144 return false;
145 }
146
147 const uint8_t *p = vm->mem + addr;
148 uint32_t value = 0;
149
150 for (uint32_t i = 0; i < size; i++) {
151 value |= (uint32_t)p[i] << (i * 8);
152 }
153
154 *out = value;
155
156 return true;
157 }
158
159 static bool store_value(ta_vm *vm, uint32_t addr, uint32_t size, uint32_t value) {
160 if (!in_bounds(vm, addr, size)) {
161 return false;
162 }
163
164 uint8_t *p = vm->mem + addr;
165
166 for (uint32_t i = 0; i < size; i++) {
167 p[i] = (uint8_t)(value >> (i * 8));
168 }
169
170 return true;
171 }
172
173 void *ta_vm_translate_pointer(const ta_vm *vm, uint32_t addr, uint32_t size) {
174 if (size == 0 || !in_bounds(vm, addr, size)) {
175 return NULL;
176 }
177
178 return vm->mem + addr;
179 }
180
181 const char *ta_vm_cstr(const ta_vm *vm, uint32_t addr) {
182 const char *string = (const char *)ta_vm_translate_pointer(vm, addr, 1);
183
184 if (!string) {
185 return NULL;
186 }
187
188 for (uint32_t offset = addr; offset < vm->mem_size; offset++) {
189 if (vm->mem[offset] == 0) {
190 return string;
191 }
192 }
193
194 return NULL;
195 }
196
197 void ta_vm_init(ta_vm *vm, uint8_t *mem, uint32_t mem_size, ta_vm_ecall ecall, void *user) {
198 vm->mem = mem;
199 vm->mem_size = mem_size;
200 vm->ecall = ecall;
201 vm->user = user;
202 ta_vm_reset(vm, 0, mem_size);
203 }
204
205 void ta_vm_reset(ta_vm *vm, uint32_t entry, uint32_t stack_top) {
206 for (uint32_t i = 0; i < 32; i++) {
207 vm->regs[i] = 0;
208 }
209
210 vm->regs[TA_VM_REG_SP] = stack_top & ~(uint32_t)15;
211 vm->pc = entry;
212 vm->steps = 0;
213 }
214
215 ta_vm_status ta_vm_run(ta_vm *vm, uint32_t max_steps) {
216 uint32_t *x = vm->regs;
217 uint32_t pc = vm->pc;
218 uint32_t steps = 0;
219 ta_vm_status status = TA_VM_RUNNING;
220
221 while (steps < max_steps) {
222 uint32_t inst;
223
224 if ((pc & 3) != 0) {
225 status = TA_VM_FAULT_MISALIGNED;
226 break;
227 }
228
229 if (!ta_vm_read32(vm, pc, &inst)) {
230 status = TA_VM_FAULT_MEMORY;
231 break;
232 }
233
234 uint32_t opcode = inst & 0x7F;
235 uint32_t rd = (inst >> 7) & 31;
236 uint32_t funct3 = (inst >> 12) & 7;
237 uint32_t rs1 = (inst >> 15) & 31;
238 uint32_t rs2 = (inst >> 20) & 31;
239 uint32_t funct7 = inst >> 25;
240 uint32_t a = x[rs1];
241 uint32_t b = x[rs2];
242 uint32_t next = pc + 4;
243 uint32_t result = 0;
244 bool write = false;
245
246 switch (opcode) {
247 case OP_LUI: {
248 result = inst & 0xFFFFF000u;
249 write = true;
250 break;
251 }
252 case OP_AUIPC: {
253 result = pc + (inst & 0xFFFFF000u);
254 write = true;
255 break;
256 }
257 case OP_JAL: {
258 uint32_t imm = ((inst >> 31) << 20) | (((inst >> 12) & 0xFF) << 12) | (((inst >> 20) & 1) << 11) | (((inst >> 21) & 0x3FF) << 1);
259
260 result = next;
261 write = true;
262 next = pc + (uint32_t)sign_extend(imm, 21);
263 break;
264 }
265 case OP_JALR: {
266 if (funct3 != 0) {
267 status = TA_VM_FAULT_ILLEGAL;
268 break;
269 }
270
271 result = next;
272 write = true;
273 next = (a + (uint32_t)sign_extend(inst >> 20, 12)) & ~(uint32_t)1;
274 break;
275 }
276 case OP_BRANCH: {
277 uint32_t imm = ((inst >> 31) << 12) | (((inst >> 7) & 1) << 11) | (((inst >> 25) & 0x3F) << 5) | (((inst >> 8) & 0xF) << 1);
278 bool taken = false;
279
280 switch (funct3) {
281 case 0: taken = a == b; break;
282 case 1: taken = a != b; break;
283 case 4: taken = (int32_t)a < (int32_t)b; break;
284 case 5: taken = (int32_t)a >= (int32_t)b; break;
285 case 6: taken = a < b; break;
286 case 7: taken = a >= b; break;
287 default: status = TA_VM_FAULT_ILLEGAL; break;
288 }
289
290 if (taken) {
291 next = pc + (uint32_t)sign_extend(imm, 13);
292 }
293
294 break;
295 }
296 case OP_LOAD: {
297 uint32_t addr = a + (uint32_t)sign_extend(inst >> 20, 12);
298 uint32_t value = 0;
299 uint32_t size = 0;
300
301 switch (funct3) {
302 case 0: case 4: size = 1; break;
303 case 1: case 5: size = 2; break;
304 case 2: size = 4; break;
305 default: status = TA_VM_FAULT_ILLEGAL; break;
306 }
307
308 if (status != TA_VM_RUNNING) {
309 break;
310 }
311
312 if (!load_value(vm, addr, size, &value)) {
313 status = TA_VM_FAULT_MEMORY;
314 break;
315 }
316
317 if (funct3 == 0) {
318 value = (uint32_t)sign_extend(value, 8);
319 } else if (funct3 == 1) {
320 value = (uint32_t)sign_extend(value, 16);
321 }
322
323 result = value;
324 write = true;
325 break;
326 }
327 case OP_STORE: {
328 uint32_t imm = ((inst >> 25) << 5) | ((inst >> 7) & 31);
329 uint32_t addr = a + (uint32_t)sign_extend(imm, 12);
330 uint32_t size = 0;
331
332 switch (funct3) {
333 case 0: size = 1; break;
334 case 1: size = 2; break;
335 case 2: size = 4; break;
336 default: status = TA_VM_FAULT_ILLEGAL; break;
337 }
338
339 if (status != TA_VM_RUNNING) {
340 break;
341 }
342
343 if (!store_value(vm, addr, size, b)) {
344 status = TA_VM_FAULT_MEMORY;
345 }
346
347 break;
348 }
349 case OP_OP_IMM: {
350 uint32_t imm = (uint32_t)sign_extend(inst >> 20, 12);
351 uint32_t shamt = rs2;
352
353 switch (funct3) {
354 case 0: result = a + imm; break;
355 case 2: result = (int32_t)a < (int32_t)imm; break;
356 case 3: result = a < imm; break;
357 case 4: result = a ^ imm; break;
358 case 6: result = a | imm; break;
359 case 7: result = a & imm; break;
360 case 1: {
361 if (funct7 != 0x00) {
362 status = TA_VM_FAULT_ILLEGAL;
363 }
364
365 result = a << shamt;
366 break;
367 }
368 default: {
369 if (funct7 == 0x00) {
370 result = a >> shamt;
371 } else if (funct7 == 0x20) {
372 result = shift_right_arith(a, shamt);
373 } else {
374 status = TA_VM_FAULT_ILLEGAL;
375 }
376
377 break;
378 }
379 }
380
381 write = true;
382 break;
383 }
384 case OP_OP: {
385 if (funct7 == 0x01) {
386 switch (funct3) {
387 case 0: result = a * b; break;
388 case 1: result = mul_high_signed(a, b); break;
389 case 2: result = mul_high_signed_unsigned(a, b); break;
390 case 3: result = mul_high_unsigned(a, b); break;
391 case 4: result = div_signed(a, b); break;
392 case 5: result = b == 0 ? 0xFFFFFFFFu : a / b; break;
393 case 6: result = rem_signed(a, b); break;
394 default: result = b == 0 ? a : a % b; break;
395 }
396 } else if (funct7 == 0x00 || funct7 == 0x20) {
397 bool alt = funct7 == 0x20;
398
399 if (alt && funct3 != 0 && funct3 != 5) {
400 status = TA_VM_FAULT_ILLEGAL;
401 break;
402 }
403
404 switch (funct3) {
405 case 0: result = alt ? a - b : a + b; break;
406 case 1: result = a << (b & 31); break;
407 case 2: result = (int32_t)a < (int32_t)b; break;
408 case 3: result = a < b; break;
409 case 4: result = a ^ b; break;
410 case 5: result = alt ? shift_right_arith(a, b & 31) : a >> (b & 31); break;
411 case 6: result = a | b; break;
412 default: result = a & b; break;
413 }
414 } else {
415 status = TA_VM_FAULT_ILLEGAL;
416 break;
417 }
418
419 write = true;
420 break;
421 }
422 case OP_MISC_MEM: {
423 break;
424 }
425 case OP_SYSTEM: {
426 uint32_t imm = inst >> 20;
427
428 if (funct3 != 0 || rd != 0 || rs1 != 0) {
429 status = TA_VM_FAULT_ILLEGAL;
430 } else if (imm == 0) {
431 vm->pc = pc;
432
433 if (vm->ecall) {
434 status = vm->ecall(vm, vm->user);
435 } else {
436 status = TA_VM_FAULT_ILLEGAL;
437 }
438 } else if (imm == 1) {
439 status = TA_VM_FAULT_EBREAK;
440 } else {
441 status = TA_VM_FAULT_ILLEGAL;
442 }
443
444 break;
445 }
446 default: {
447 status = TA_VM_FAULT_ILLEGAL;
448 break;
449 }
450 }
451
452 if (status != TA_VM_RUNNING && status != TA_VM_HALTED) {
453 break;
454 }
455
456 if (write && rd != 0) {
457 x[rd] = result;
458 }
459
460 x[0] = 0;
461 pc = next;
462 steps++;
463
464 if (status != TA_VM_RUNNING) {
465 break;
466 }
467 }
468
469 vm->pc = pc;
470 vm->steps = steps;
471
472 return status;
473 }
474
475 static ta_vm guest;
476 static uint8_t *ram;
477 static ta_vm_status guest_status;
478 static int32_t exit_code;
479 static uint32_t return_stub;
480 static uint32_t call_result;
481 static bool ready;
482 static bool returned;
483 static bool exec_pending;
484 static uint8_t exec_file;
485 bool ta_vm_running;
486
487 static ta_vm_status guest_ecall(ta_vm *machine, void *user) {
488 (void)user;
489
490 uint32_t *registers = machine->regs;
491
492 switch (registers[TA_VM_REG_A7]) {
493 case TA_SYS_EXIT: {
494 exit_code = (int32_t)registers[TA_VM_REG_A0];
495
496 return TA_VM_HALTED;
497 }
498 case TA_SYS_READY: {
499 return_stub = registers[TA_VM_REG_A0];
500 ready = true;
501
502 return TA_VM_YIELDED;
503 }
504 case TA_SYS_RETURN: {
505 call_result = registers[TA_VM_REG_A0];
506 returned = true;
507
508 return TA_VM_YIELDED;
509 }
510 default: {
511 return ta_syscall_handler(machine);
512 }
513 }
514 }
515
516 static void finish_run(bool completed) {
517 if (completed) {
518 guest_status = TA_VM_RUNNING;
519 ta_vm_running = true;
520
521 return;
522 }
523
524 if (guest_status != TA_VM_HALTED) {
525 exit_code = 1;
526 }
527
528 ta_vm_running = false;
529 }
530
531 bool ta_vm_load(const uint8_t *binary, size_t size) {
532 ta_vm_unload();
533
534 if (!binary || size < 4 || size > TA_VM_RAM_SIZE) {
535 return false;
536 }
537
538 ram = (uint8_t *)malloc(TA_VM_RAM_SIZE);
539
540 if (!ram) {
541 return false;
542 }
543
544 for (size_t i = 0; i < TA_VM_RAM_SIZE; i++) {
545 ram[i] = i < size ? binary[i] : 0;
546 }
547
548 ta_vm_init(&guest, ram, TA_VM_RAM_SIZE, guest_ecall, NULL);
549 guest.pc = 0;
550 guest_status = TA_VM_RUNNING;
551 exit_code = 0;
552 ready = false;
553 returned = false;
554 ta_vm_running = true;
555
556 guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
557
558 if (guest_status == TA_VM_YIELDED && exec_pending) {
559 finish_run(true);
560
561 return true;
562 }
563
564 if (guest_status != TA_VM_YIELDED || !ready) {
565 finish_run(false);
566 ta_vm_unload();
567
568 return false;
569 }
570
571 finish_run(true);
572
573 return true;
574 }
575
576 bool ta_vm_load_tab(const char *path) {
577 if (!ta_tab_load(path)) {
578 return false;
579 }
580
581 const uint8_t *data;
582 uint32_t size;
583
584 if (!ta_tab_find(TA_TAB_MAIN_PROGRAM, &data, &size)) {
585 return false;
586 }
587
588 return ta_vm_load(data, size);
589 }
590
591 void ta_vm_request_exec(uint8_t file) {
592 exec_file = file;
593 exec_pending = true;
594 }
595
596 bool ta_vm_exec_pending(void) {
597 return exec_pending;
598 }
599
600 bool ta_vm_exec_apply(void) {
601 while (exec_pending) {
602 const uint8_t *data;
603 uint32_t size;
604 uint8_t file = exec_file;
605
606 exec_pending = false;
607
608 if (!ta_tab_find(file, &data, &size)) {
609 ta_vm_unload();
610 exit_code = 1;
611
612 return false;
613 }
614
615 ta_screen_init();
616 ta_scene_invalidate();
617
618 if (!ta_vm_load(data, size)) {
619 exit_code = 1;
620
621 return false;
622 }
623 }
624
625 return ta_vm_running;
626 }
627
628 bool ta_vm_call(uint32_t function, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t *result) {
629 if (!ta_vm_running || !ready || function < 4 || (function & 3) != 0 || function >= TA_VM_RAM_SIZE) {
630 return false;
631 }
632
633 ta_vm_reset(&guest, function, TA_VM_RAM_SIZE);
634 guest.regs[TA_VM_REG_RA] = return_stub;
635 guest.regs[TA_VM_REG_A0] = arg0;
636 guest.regs[TA_VM_REG_A1] = arg1;
637 guest.regs[TA_VM_REG_A2] = arg2;
638 returned = false;
639
640 guest_status = ta_vm_run(&guest, TA_VM_STEPS_PER_CALL);
641
642 if (guest_status == TA_VM_YIELDED && exec_pending) {
643 finish_run(true);
644
645 return false;
646 }
647
648 bool completed = guest_status == TA_VM_YIELDED && returned;
649
650 finish_run(completed);
651
652 if (completed && result) {
653 *result = call_result;
654 }
655
656 return completed;
657 }
658
659 int32_t ta_vm_exit_code(void) {
660 return exit_code;
661 }
662
663 void ta_vm_unload(void) {
664 ta_syscall_reset();
665
666 if (ram) {
667 free(ram);
668 ram = NULL;
669 }
670
671 ready = false;
672 returned = false;
673 exec_pending = false;
674 guest_status = TA_VM_HALTED;
675 ta_vm_running = false;
676 }