caveat/ecall.c: refactor proxy ecall
[cavatools.git] / caveat / ecall.c
1 #include <unistd.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <assert.h>
6 #include <errno.h>
7 #include <sys/mman.h>
8 #include <sys/syscall.h>
9 #include <sys/utsname.h>
10 #include <math.h>
11 #include <string.h>
12
13 //#include "encoding.h"
14
15 //#define NO_FP_MACROS
16 #include "caveat_fp.h"
17 #include "arith.h"
18
19 #include "caveat.h"
20 #include "ecall.h"
21 #include "opcodes.h"
22 #include "insn.h"
23 #include "shmfifo.h"
24 #include "core.h"
25 #include "riscv-opc.h"
26
27 //#define DEBUG
28
29
30 static Addr_t emulate_brk(Addr_t addr, struct pinfo_t* info)
31 {
32 Addr_t newbrk = addr;
33 if (addr < info->brk_min)
34 newbrk = info->brk_min;
35 else if (addr > info->brk_max)
36 newbrk = info->brk_max;
37
38 if (info->brk == 0)
39 info->brk = ROUNDUP(info->brk_min, RISCV_PGSIZE);
40
41 uintptr_t newbrk_page = ROUNDUP(newbrk, RISCV_PGSIZE);
42 if (info->brk > newbrk_page)
43 munmap((void*)newbrk_page, info->brk - newbrk_page);
44 else if (info->brk < newbrk_page)
45 assert(mmap((void*)info->brk, newbrk_page - info->brk, -1, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) == (void*)info->brk);
46 info->brk = newbrk_page;
47
48 return newbrk;
49 }
50
51 int proxy_ecall( struct core_t* cpu )
52 {
53 static long previous = 0;
54 assert(insn(cpu->pc)->op_code == Op_ecall);
55 long args[6];
56 long guest = ecall_fetch(cpu, args);
57 if (guest < 0) {
58 no_mapping:
59 fprintf(stderr, "system call %ld has no mapping to host system\n", guest);
60 fprintf(stderr, "Arguments(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx)\n",
61 args[0], args[1], args[2], args[3], args[4], args[5]);
62 abort();
63 }
64 struct ecall_entry const *entry = ecall_entry(guest);
65 long host = entry->number;
66 char const *name = entry->name;
67
68 #ifdef DEBUG
69 fprintf(stderr, "%10ld: %s[%ld:%ld](%lx, %lx, %lx, %lx, %lx, %lx)", cpu->counter.insn_executed-previous,
70 name, guest, host,
71 args[0], args[1], args[2], args[3], args[4], args[5]);
72 previous = cpu->counter.insn_executed;
73 #endif
74 switch (host) {
75 case -1:
76 goto no_mapping;
77 case -2:
78 fprintf(stderr, "system call %s(#%ld) not supported on host system\n", name, host);
79 abort();
80
81 #if 0
82 case 12: /* sys_brk */
83 args[0] = emulate_brk(args[0], &current);
84 break;
85 #endif
86
87 #if (defined(SYS_exit) || defined(SYS_exit_group))
88 #ifdef SYS_exit
89 case SYS_exit:
90 #endif
91 #ifdef SYS_exit_group
92 case SYS_exit_group:
93 #endif
94 return 1;
95 #endif /* (defined(SYS_exit) || defined(SYS_exit_group)) */
96
97 #ifdef SYS_rt_sigaction
98 case SYS_rt_sigaction:
99 fprintf(stderr, "Trying to call rt_sigaction, always succeed without error.\n");
100 args[0] = 0; // always succeed without error
101 break;
102 #endif /* SYS_rt_sigaction */
103
104 #ifdef SYS_clone
105 case SYS_clone:
106 abort();
107 #endif
108
109 #ifdef SYS_gettimeofday
110 case SYS_gettimeofday:
111 #define PRETEND_MIPS 1000
112 #ifdef PRETEND_MIPS
113 {
114 struct timeval tv;
115 tv.tv_sec = (cpu->counter.insn_executed / PRETEND_MIPS) / 1000000;
116 tv.tv_usec = (cpu->counter.insn_executed / PRETEND_MIPS) % 1000000;
117 tv.tv_sec += cpu->counter.start_timeval.tv_sec;
118 tv.tv_usec += cpu->counter.start_timeval.tv_usec;
119 tv.tv_sec += tv.tv_usec / 1000000; // microseconds overflow
120 tv.tv_usec %= 1000000;
121 // fprintf(stderr, "gettimeofday(sec=%ld, usec=%4ld)\n", tv.tv_sec, tv.tv_usec);
122 memcpy((void *)args[0], &tv, sizeof tv);
123 args[0] = 0;
124 }
125 break;
126 #else
127 goto default_case;
128 #endif
129 #endif /* SYS_gettimeofday */
130
131 #ifdef SYS_close
132 case SYS_close:
133 if (args[0] <= 2) { // Don't close stdin, stdout, stderr
134 args[0] = 0;
135 break;
136 }
137 goto default_case;
138 #endif
139
140 default:
141 default_case:
142 args[0] = syscall(host, args[0], args[1], args[2], args[3], args[4], args[5]);
143 break;
144 }
145 #ifdef DEBUG
146 fprintf(stderr, " return %lx\n", args[0]);
147 #endif
148
149 ecall_store(args, cpu);
150
151 return 0;
152 }
153
154
155 static void set_csr( struct core_t* cpu, int which, long val )
156 {
157 switch (which) {
158 case CSR_USTATUS:
159 cpu->state.ustatus = val;
160 return;
161 case CSR_FFLAGS:
162 cpu->state.fcsr.flags = val;
163 #ifdef SOFT_FP
164 softfloat_exceptionFlags = val;
165 #else
166 #endif
167 return;
168 case CSR_FRM:
169 cpu->state.fcsr.rmode = val;
170 break;
171 case CSR_FCSR:
172 cpu->state.fcsr_v = val;
173 break;
174 default:
175 fprintf(stderr, "Unsupported set_csr(%d, val=%lx)\n", which, val);
176 abort();
177 }
178 #ifdef SOFT_FP
179 softfloat_roundingMode = cpu->state.fcsr.rmode;
180 #else
181 fesetround(riscv_to_c_rm(cpu->state.fcsr.rmode));
182 #endif
183 }
184
185 static long get_csr( struct core_t* cpu, int which )
186 {
187 switch (which) {
188 case CSR_USTATUS:
189 return cpu->state.ustatus;
190 case CSR_FFLAGS:
191 #ifdef SOFT_FP
192 cpu->state.fcsr.flags = softfloat_exceptionFlags;
193 #else
194 #endif
195 return cpu->state.fcsr.flags;
196 case CSR_FRM:
197 return cpu->state.fcsr.rmode;
198 case CSR_FCSR:
199 return cpu->state.fcsr_v;
200 default:
201 fprintf(stderr, "Unsupported get_csr(%d)\n", which);
202 abort();
203 }
204 }
205
206 void proxy_csr( struct core_t* cpu, const struct insn_t* p, int which )
207 {
208 enum Opcode_t op = p->op_code;
209 int regop = op==Op_csrrw || op==Op_csrrs || op==Op_csrrc;
210 long old_val = 0;
211 long value = regop ? p->op_rs1 : p->op_constant>>12;
212 if (op==Op_csrrw || op==Op_csrrwi) {
213 if (p->op_rd != 0)
214 old_val = get_csr(cpu, which);
215 set_csr(cpu, which, value);
216 }
217 else {
218 old_val = get_csr(cpu, which);
219 if (regop || value != 0) {
220 if (op==Op_csrrs || op==Op_csrrsi)
221 value = old_val | value;
222 else
223 value = old_val & ~value;
224 set_csr(cpu, which, value);
225 }
226 }
227 cpu->reg[p->op_rd].l = old_val;
228 }