master
c 444 lines 14.3 KB
Raw
1 /*
2 * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the Open Source and Linux Lab nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "qemu/osdep.h"
29 #include "cpu.h"
30 #include "chardev/char-fe.h"
31 #include "exec/helper-proto.h"
32 #include "exec/target_page.h"
33 #include "semihosting/semihost.h"
34 #include "semihosting/uaccess.h"
35 #include "system/memory.h"
36 #include "qapi/error.h"
37 #include "qemu/log.h"
38 #include "qemu/plugin.h"
39
40 enum {
41 TARGET_SYS_exit = 1,
42 TARGET_SYS_read = 3,
43 TARGET_SYS_write = 4,
44 TARGET_SYS_open = 5,
45 TARGET_SYS_close = 6,
46 TARGET_SYS_lseek = 19,
47 TARGET_SYS_select_one = 29,
48
49 TARGET_SYS_argc = 1000,
50 TARGET_SYS_argv_sz = 1001,
51 TARGET_SYS_argv = 1002,
52 TARGET_SYS_memset = 1004,
53 };
54
55 enum {
56 SELECT_ONE_READ = 1,
57 SELECT_ONE_WRITE = 2,
58 SELECT_ONE_EXCEPT = 3,
59 };
60
61 enum {
62 TARGET_EPERM = 1,
63 TARGET_ENOENT = 2,
64 TARGET_ESRCH = 3,
65 TARGET_EINTR = 4,
66 TARGET_EIO = 5,
67 TARGET_ENXIO = 6,
68 TARGET_E2BIG = 7,
69 TARGET_ENOEXEC = 8,
70 TARGET_EBADF = 9,
71 TARGET_ECHILD = 10,
72 TARGET_EAGAIN = 11,
73 TARGET_ENOMEM = 12,
74 TARGET_EACCES = 13,
75 TARGET_EFAULT = 14,
76 TARGET_ENOTBLK = 15,
77 TARGET_EBUSY = 16,
78 TARGET_EEXIST = 17,
79 TARGET_EXDEV = 18,
80 TARGET_ENODEV = 19,
81 TARGET_ENOTDIR = 20,
82 TARGET_EISDIR = 21,
83 TARGET_EINVAL = 22,
84 TARGET_ENFILE = 23,
85 TARGET_EMFILE = 24,
86 TARGET_ENOTTY = 25,
87 TARGET_ETXTBSY = 26,
88 TARGET_EFBIG = 27,
89 TARGET_ENOSPC = 28,
90 TARGET_ESPIPE = 29,
91 TARGET_EROFS = 30,
92 TARGET_EMLINK = 31,
93 TARGET_EPIPE = 32,
94 TARGET_EDOM = 33,
95 TARGET_ERANGE = 34,
96 TARGET_ENOSYS = 88,
97 TARGET_ELOOP = 92,
98 };
99
100 static uint32_t errno_h2g(int host_errno)
101 {
102 switch (host_errno) {
103 case 0: return 0;
104 case EPERM: return TARGET_EPERM;
105 case ENOENT: return TARGET_ENOENT;
106 case ESRCH: return TARGET_ESRCH;
107 case EINTR: return TARGET_EINTR;
108 case EIO: return TARGET_EIO;
109 case ENXIO: return TARGET_ENXIO;
110 case E2BIG: return TARGET_E2BIG;
111 case ENOEXEC: return TARGET_ENOEXEC;
112 case EBADF: return TARGET_EBADF;
113 case ECHILD: return TARGET_ECHILD;
114 case EAGAIN: return TARGET_EAGAIN;
115 case ENOMEM: return TARGET_ENOMEM;
116 case EACCES: return TARGET_EACCES;
117 case EFAULT: return TARGET_EFAULT;
118 #ifdef ENOTBLK
119 case ENOTBLK: return TARGET_ENOTBLK;
120 #endif
121 case EBUSY: return TARGET_EBUSY;
122 case EEXIST: return TARGET_EEXIST;
123 case EXDEV: return TARGET_EXDEV;
124 case ENODEV: return TARGET_ENODEV;
125 case ENOTDIR: return TARGET_ENOTDIR;
126 case EISDIR: return TARGET_EISDIR;
127 case EINVAL: return TARGET_EINVAL;
128 case ENFILE: return TARGET_ENFILE;
129 case EMFILE: return TARGET_EMFILE;
130 case ENOTTY: return TARGET_ENOTTY;
131 #ifdef ETXTBSY
132 case ETXTBSY: return TARGET_ETXTBSY;
133 #endif
134 case EFBIG: return TARGET_EFBIG;
135 case ENOSPC: return TARGET_ENOSPC;
136 case ESPIPE: return TARGET_ESPIPE;
137 case EROFS: return TARGET_EROFS;
138 case EMLINK: return TARGET_EMLINK;
139 case EPIPE: return TARGET_EPIPE;
140 case EDOM: return TARGET_EDOM;
141 case ERANGE: return TARGET_ERANGE;
142 case ENOSYS: return TARGET_ENOSYS;
143 #ifdef ELOOP
144 case ELOOP: return TARGET_ELOOP;
145 #endif
146 };
147
148 return TARGET_EINVAL;
149 }
150
151 typedef struct XtensaSimConsole {
152 CharFrontend fe;
153 struct {
154 char buffer[16];
155 size_t offset;
156 } input;
157 } XtensaSimConsole;
158
159 static XtensaSimConsole *sim_console;
160
161 static IOCanReadHandler sim_console_can_read;
162 static int sim_console_can_read(void *opaque)
163 {
164 XtensaSimConsole *p = opaque;
165
166 return sizeof(p->input.buffer) - p->input.offset;
167 }
168
169 static IOReadHandler sim_console_read;
170 static void sim_console_read(void *opaque, const uint8_t *buf, int size)
171 {
172 XtensaSimConsole *p = opaque;
173 size_t copy = sizeof(p->input.buffer) - p->input.offset;
174
175 if (size < copy) {
176 copy = size;
177 }
178 memcpy(p->input.buffer + p->input.offset, buf, copy);
179 p->input.offset += copy;
180 }
181
182 void xtensa_sim_open_console(Chardev *chr)
183 {
184 static XtensaSimConsole console;
185
186 qemu_chr_fe_init(&console.fe, chr, &error_abort);
187 qemu_chr_fe_set_handlers(&console.fe,
188 sim_console_can_read,
189 sim_console_read,
190 NULL, NULL, &console,
191 NULL, true);
192 sim_console = &console;
193 }
194
195 void HELPER(simcall)(CPUXtensaState *env)
196 {
197 const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
198 CPUState *cs = env_cpu(env);
199 AddressSpace *as = cs->as;
200 uint32_t *regs = env->regs;
201 uint64_t last_pc = env->pc;
202
203 switch (regs[2]) {
204 case TARGET_SYS_exit:
205 exit(regs[3]);
206 break;
207
208 case TARGET_SYS_read:
209 case TARGET_SYS_write:
210 {
211 bool is_write = regs[2] == TARGET_SYS_write;
212 uint32_t fd = regs[3];
213 uint32_t vaddr = regs[4];
214 uint32_t len = regs[5];
215 uint32_t len_done = 0;
216
217 while (len > 0) {
218 TranslateForDebugResult tres;
219 uint32_t page_left =
220 TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1));
221 uint32_t io_sz = page_left < len ? page_left : len;
222 hwaddr sz = io_sz;
223 void *buf = NULL;
224 uint32_t io_done;
225 bool error = false;
226
227 if (cpu_translate_for_debug(cs, vaddr, &tres)) {
228 buf = address_space_map(as, tres.physaddr, &sz,
229 !is_write, attrs);
230 }
231
232 if (buf) {
233 vaddr += io_sz;
234 len -= io_sz;
235 if (fd < 3 && sim_console) {
236 if (is_write && (fd == 1 || fd == 2)) {
237 io_done = qemu_chr_fe_write_all(&sim_console->fe,
238 buf, io_sz);
239 regs[3] = errno_h2g(errno);
240 } else if (!is_write && fd == 0) {
241 if (sim_console->input.offset) {
242 io_done = sim_console->input.offset;
243 if (io_sz < io_done) {
244 io_done = io_sz;
245 }
246 memcpy(buf, sim_console->input.buffer, io_done);
247 memmove(sim_console->input.buffer,
248 sim_console->input.buffer + io_done,
249 sim_console->input.offset - io_done);
250 sim_console->input.offset -= io_done;
251 qemu_chr_fe_accept_input(&sim_console->fe);
252 } else {
253 io_done = -1;
254 regs[3] = TARGET_EAGAIN;
255 }
256 } else {
257 qemu_log_mask(LOG_GUEST_ERROR,
258 "%s fd %d is not supported with chardev console\n",
259 is_write ?
260 "writing to" : "reading from", fd);
261 io_done = -1;
262 regs[3] = TARGET_EBADF;
263 }
264 } else {
265 io_done = is_write ?
266 write(fd, buf, io_sz) :
267 read(fd, buf, io_sz);
268 regs[3] = errno_h2g(errno);
269 }
270 if (io_done == -1) {
271 error = true;
272 io_done = 0;
273 }
274 address_space_unmap(as, buf, sz, !is_write, io_done);
275 } else {
276 error = true;
277 regs[3] = TARGET_EINVAL;
278 break;
279 }
280 if (error) {
281 if (!len_done) {
282 len_done = -1;
283 }
284 break;
285 }
286 len_done += io_done;
287 if (io_done < io_sz) {
288 break;
289 }
290 }
291 regs[2] = len_done;
292 }
293 break;
294
295 case TARGET_SYS_open:
296 {
297 char name[1024];
298 int rc;
299 int i;
300
301 for (i = 0; i < ARRAY_SIZE(name); ++i) {
302 rc = cpu_memory_rw_debug(cs, regs[3] + i,
303 (uint8_t *)name + i, 1, 0);
304 if (rc != 0 || name[i] == 0) {
305 break;
306 }
307 }
308
309 if (rc == 0 && i < ARRAY_SIZE(name)) {
310 regs[2] = open(name, regs[4], regs[5]);
311 regs[3] = errno_h2g(errno);
312 } else {
313 regs[2] = -1;
314 regs[3] = TARGET_EINVAL;
315 }
316 }
317 break;
318
319 case TARGET_SYS_close:
320 if (regs[3] < 3) {
321 regs[2] = regs[3] = 0;
322 } else {
323 regs[2] = close(regs[3]);
324 regs[3] = errno_h2g(errno);
325 }
326 break;
327
328 case TARGET_SYS_lseek:
329 regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]);
330 regs[3] = errno_h2g(errno);
331 break;
332
333 case TARGET_SYS_select_one:
334 {
335 uint32_t fd = regs[3];
336 uint32_t rq = regs[4];
337 uint32_t target_tv = regs[5];
338
339 struct timeval tv = {0};
340
341 if (target_tv) {
342 get_user_u32(tv.tv_sec, target_tv);
343 get_user_u32(tv.tv_sec, target_tv + 4);
344 }
345 if (fd < 3 && sim_console) {
346 if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) {
347 regs[2] = 1;
348 } else if (fd == 0 && rq == SELECT_ONE_READ) {
349 regs[2] = sim_console->input.offset > 0;
350 } else {
351 regs[2] = 0;
352 }
353 regs[3] = 0;
354 } else {
355 fd_set fdset;
356
357 FD_ZERO(&fdset);
358 FD_SET(fd, &fdset);
359 regs[2] = select(fd + 1,
360 rq == SELECT_ONE_READ ? &fdset : NULL,
361 rq == SELECT_ONE_WRITE ? &fdset : NULL,
362 rq == SELECT_ONE_EXCEPT ? &fdset : NULL,
363 target_tv ? &tv : NULL);
364 regs[3] = errno_h2g(errno);
365 }
366 }
367 break;
368
369 case TARGET_SYS_argc:
370 regs[2] = semihosting_get_argc();
371 regs[3] = 0;
372 break;
373
374 case TARGET_SYS_argv_sz:
375 {
376 int argc = semihosting_get_argc();
377 int sz = (argc + 1) * sizeof(uint32_t);
378 int i;
379
380 for (i = 0; i < argc; ++i) {
381 sz += 1 + strlen(semihosting_get_arg(i));
382 }
383 regs[2] = sz;
384 regs[3] = 0;
385 }
386 break;
387
388 case TARGET_SYS_argv:
389 {
390 int argc = semihosting_get_argc();
391 int str_offset = (argc + 1) * sizeof(uint32_t);
392 int i;
393 uint32_t argptr;
394
395 for (i = 0; i < argc; ++i) {
396 const char *str = semihosting_get_arg(i);
397 int str_size = strlen(str) + 1;
398
399 put_user_u32(regs[3] + str_offset,
400 regs[3] + i * sizeof(uint32_t));
401 cpu_memory_rw_debug(cs,
402 regs[3] + str_offset,
403 (uint8_t *)str, str_size, 1);
404 str_offset += str_size;
405 }
406 argptr = 0;
407 cpu_memory_rw_debug(cs,
408 regs[3] + i * sizeof(uint32_t),
409 (uint8_t *)&argptr, sizeof(argptr), 1);
410 regs[3] = 0;
411 }
412 break;
413
414 case TARGET_SYS_memset:
415 {
416 uint32_t base = regs[3];
417 uint32_t sz = regs[5];
418
419 while (sz) {
420 hwaddr len = sz;
421 void *buf = address_space_map(as, base, &len, true, attrs);
422
423 if (buf && len) {
424 memset(buf, regs[4], len);
425 address_space_unmap(as, buf, len, true, len);
426 } else {
427 len = 1;
428 }
429 base += len;
430 sz -= len;
431 }
432 regs[2] = regs[3];
433 regs[3] = 0;
434 }
435 break;
436
437 default:
438 qemu_log_mask(LOG_GUEST_ERROR, "%s(%d): not implemented\n", __func__, regs[2]);
439 regs[2] = -1;
440 regs[3] = TARGET_ENOSYS;
441 break;
442 }
443 qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
444 }