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 "hw/semihosting/semihost.h" |
33 | #include "qapi/error.h" |
34 | #include "qemu/log.h" |
35 | |
36 | enum { |
37 | TARGET_SYS_exit = 1, |
38 | TARGET_SYS_read = 3, |
39 | TARGET_SYS_write = 4, |
40 | TARGET_SYS_open = 5, |
41 | TARGET_SYS_close = 6, |
42 | TARGET_SYS_lseek = 19, |
43 | TARGET_SYS_select_one = 29, |
44 | |
45 | TARGET_SYS_argc = 1000, |
46 | TARGET_SYS_argv_sz = 1001, |
47 | TARGET_SYS_argv = 1002, |
48 | TARGET_SYS_memset = 1004, |
49 | }; |
50 | |
51 | enum { |
52 | SELECT_ONE_READ = 1, |
53 | SELECT_ONE_WRITE = 2, |
54 | SELECT_ONE_EXCEPT = 3, |
55 | }; |
56 | |
57 | enum { |
58 | TARGET_EPERM = 1, |
59 | TARGET_ENOENT = 2, |
60 | TARGET_ESRCH = 3, |
61 | TARGET_EINTR = 4, |
62 | TARGET_EIO = 5, |
63 | TARGET_ENXIO = 6, |
64 | TARGET_E2BIG = 7, |
65 | TARGET_ENOEXEC = 8, |
66 | TARGET_EBADF = 9, |
67 | TARGET_ECHILD = 10, |
68 | TARGET_EAGAIN = 11, |
69 | TARGET_ENOMEM = 12, |
70 | TARGET_EACCES = 13, |
71 | TARGET_EFAULT = 14, |
72 | TARGET_ENOTBLK = 15, |
73 | TARGET_EBUSY = 16, |
74 | TARGET_EEXIST = 17, |
75 | TARGET_EXDEV = 18, |
76 | TARGET_ENODEV = 19, |
77 | TARGET_ENOTDIR = 20, |
78 | TARGET_EISDIR = 21, |
79 | TARGET_EINVAL = 22, |
80 | TARGET_ENFILE = 23, |
81 | TARGET_EMFILE = 24, |
82 | TARGET_ENOTTY = 25, |
83 | TARGET_ETXTBSY = 26, |
84 | TARGET_EFBIG = 27, |
85 | TARGET_ENOSPC = 28, |
86 | TARGET_ESPIPE = 29, |
87 | TARGET_EROFS = 30, |
88 | TARGET_EMLINK = 31, |
89 | TARGET_EPIPE = 32, |
90 | TARGET_EDOM = 33, |
91 | TARGET_ERANGE = 34, |
92 | TARGET_ENOSYS = 88, |
93 | TARGET_ELOOP = 92, |
94 | }; |
95 | |
96 | static uint32_t errno_h2g(int host_errno) |
97 | { |
98 | static const uint32_t guest_errno[] = { |
99 | [EPERM] = TARGET_EPERM, |
100 | [ENOENT] = TARGET_ENOENT, |
101 | [ESRCH] = TARGET_ESRCH, |
102 | [EINTR] = TARGET_EINTR, |
103 | [EIO] = TARGET_EIO, |
104 | [ENXIO] = TARGET_ENXIO, |
105 | [E2BIG] = TARGET_E2BIG, |
106 | [ENOEXEC] = TARGET_ENOEXEC, |
107 | [EBADF] = TARGET_EBADF, |
108 | [ECHILD] = TARGET_ECHILD, |
109 | [EAGAIN] = TARGET_EAGAIN, |
110 | [ENOMEM] = TARGET_ENOMEM, |
111 | [EACCES] = TARGET_EACCES, |
112 | [EFAULT] = TARGET_EFAULT, |
113 | #ifdef ENOTBLK |
114 | [ENOTBLK] = TARGET_ENOTBLK, |
115 | #endif |
116 | [EBUSY] = TARGET_EBUSY, |
117 | [EEXIST] = TARGET_EEXIST, |
118 | [EXDEV] = TARGET_EXDEV, |
119 | [ENODEV] = TARGET_ENODEV, |
120 | [ENOTDIR] = TARGET_ENOTDIR, |
121 | [EISDIR] = TARGET_EISDIR, |
122 | [EINVAL] = TARGET_EINVAL, |
123 | [ENFILE] = TARGET_ENFILE, |
124 | [EMFILE] = TARGET_EMFILE, |
125 | [ENOTTY] = TARGET_ENOTTY, |
126 | #ifdef ETXTBSY |
127 | [ETXTBSY] = TARGET_ETXTBSY, |
128 | #endif |
129 | [EFBIG] = TARGET_EFBIG, |
130 | [ENOSPC] = TARGET_ENOSPC, |
131 | [ESPIPE] = TARGET_ESPIPE, |
132 | [EROFS] = TARGET_EROFS, |
133 | [EMLINK] = TARGET_EMLINK, |
134 | [EPIPE] = TARGET_EPIPE, |
135 | [EDOM] = TARGET_EDOM, |
136 | [ERANGE] = TARGET_ERANGE, |
137 | [ENOSYS] = TARGET_ENOSYS, |
138 | #ifdef ELOOP |
139 | [ELOOP] = TARGET_ELOOP, |
140 | #endif |
141 | }; |
142 | |
143 | if (host_errno == 0) { |
144 | return 0; |
145 | } else if (host_errno > 0 && host_errno < ARRAY_SIZE(guest_errno) && |
146 | guest_errno[host_errno]) { |
147 | return guest_errno[host_errno]; |
148 | } else { |
149 | return TARGET_EINVAL; |
150 | } |
151 | } |
152 | |
153 | typedef struct XtensaSimConsole { |
154 | CharBackend be; |
155 | struct { |
156 | char buffer[16]; |
157 | size_t offset; |
158 | } input; |
159 | } XtensaSimConsole; |
160 | |
161 | static XtensaSimConsole *sim_console; |
162 | |
163 | static IOCanReadHandler sim_console_can_read; |
164 | static int sim_console_can_read(void *opaque) |
165 | { |
166 | XtensaSimConsole *p = opaque; |
167 | |
168 | return sizeof(p->input.buffer) - p->input.offset; |
169 | } |
170 | |
171 | static IOReadHandler sim_console_read; |
172 | static void sim_console_read(void *opaque, const uint8_t *buf, int size) |
173 | { |
174 | XtensaSimConsole *p = opaque; |
175 | size_t copy = sizeof(p->input.buffer) - p->input.offset; |
176 | |
177 | if (size < copy) { |
178 | copy = size; |
179 | } |
180 | memcpy(p->input.buffer + p->input.offset, buf, copy); |
181 | p->input.offset += copy; |
182 | } |
183 | |
184 | void xtensa_sim_open_console(Chardev *chr) |
185 | { |
186 | static XtensaSimConsole console; |
187 | |
188 | qemu_chr_fe_init(&console.be, chr, &error_abort); |
189 | qemu_chr_fe_set_handlers(&console.be, |
190 | sim_console_can_read, |
191 | sim_console_read, |
192 | NULL, NULL, &console, |
193 | NULL, true); |
194 | sim_console = &console; |
195 | } |
196 | |
197 | void HELPER(simcall)(CPUXtensaState *env) |
198 | { |
199 | CPUState *cs = env_cpu(env); |
200 | uint32_t *regs = env->regs; |
201 | |
202 | switch (regs[2]) { |
203 | case TARGET_SYS_exit: |
204 | exit(regs[3]); |
205 | break; |
206 | |
207 | case TARGET_SYS_read: |
208 | case TARGET_SYS_write: |
209 | { |
210 | bool is_write = regs[2] == TARGET_SYS_write; |
211 | uint32_t fd = regs[3]; |
212 | uint32_t vaddr = regs[4]; |
213 | uint32_t len = regs[5]; |
214 | uint32_t len_done = 0; |
215 | |
216 | while (len > 0) { |
217 | hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr); |
218 | uint32_t page_left = |
219 | TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1)); |
220 | uint32_t io_sz = page_left < len ? page_left : len; |
221 | hwaddr sz = io_sz; |
222 | void *buf = cpu_physical_memory_map(paddr, &sz, !is_write); |
223 | uint32_t io_done; |
224 | bool error = false; |
225 | |
226 | if (buf) { |
227 | vaddr += io_sz; |
228 | len -= io_sz; |
229 | if (fd < 3 && sim_console) { |
230 | if (is_write && (fd == 1 || fd == 2)) { |
231 | io_done = qemu_chr_fe_write_all(&sim_console->be, |
232 | buf, io_sz); |
233 | regs[3] = errno_h2g(errno); |
234 | } else if (!is_write && fd == 0) { |
235 | if (sim_console->input.offset) { |
236 | io_done = sim_console->input.offset; |
237 | if (io_sz < io_done) { |
238 | io_done = io_sz; |
239 | } |
240 | memcpy(buf, sim_console->input.buffer, io_done); |
241 | memmove(sim_console->input.buffer, |
242 | sim_console->input.buffer + io_done, |
243 | sim_console->input.offset - io_done); |
244 | sim_console->input.offset -= io_done; |
245 | qemu_chr_fe_accept_input(&sim_console->be); |
246 | } else { |
247 | io_done = -1; |
248 | regs[3] = TARGET_EAGAIN; |
249 | } |
250 | } else { |
251 | qemu_log_mask(LOG_GUEST_ERROR, |
252 | "%s fd %d is not supported with chardev console\n" , |
253 | is_write ? |
254 | "writing to" : "reading from" , fd); |
255 | io_done = -1; |
256 | regs[3] = TARGET_EBADF; |
257 | } |
258 | } else { |
259 | io_done = is_write ? |
260 | write(fd, buf, io_sz) : |
261 | read(fd, buf, io_sz); |
262 | regs[3] = errno_h2g(errno); |
263 | } |
264 | if (io_done == -1) { |
265 | error = true; |
266 | io_done = 0; |
267 | } |
268 | cpu_physical_memory_unmap(buf, sz, !is_write, io_done); |
269 | } else { |
270 | error = true; |
271 | regs[3] = TARGET_EINVAL; |
272 | break; |
273 | } |
274 | if (error) { |
275 | if (!len_done) { |
276 | len_done = -1; |
277 | } |
278 | break; |
279 | } |
280 | len_done += io_done; |
281 | if (io_done < io_sz) { |
282 | break; |
283 | } |
284 | } |
285 | regs[2] = len_done; |
286 | } |
287 | break; |
288 | |
289 | case TARGET_SYS_open: |
290 | { |
291 | char name[1024]; |
292 | int rc; |
293 | int i; |
294 | |
295 | for (i = 0; i < ARRAY_SIZE(name); ++i) { |
296 | rc = cpu_memory_rw_debug(cs, regs[3] + i, |
297 | (uint8_t *)name + i, 1, 0); |
298 | if (rc != 0 || name[i] == 0) { |
299 | break; |
300 | } |
301 | } |
302 | |
303 | if (rc == 0 && i < ARRAY_SIZE(name)) { |
304 | regs[2] = open(name, regs[4], regs[5]); |
305 | regs[3] = errno_h2g(errno); |
306 | } else { |
307 | regs[2] = -1; |
308 | regs[3] = TARGET_EINVAL; |
309 | } |
310 | } |
311 | break; |
312 | |
313 | case TARGET_SYS_close: |
314 | if (regs[3] < 3) { |
315 | regs[2] = regs[3] = 0; |
316 | } else { |
317 | regs[2] = close(regs[3]); |
318 | regs[3] = errno_h2g(errno); |
319 | } |
320 | break; |
321 | |
322 | case TARGET_SYS_lseek: |
323 | regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]); |
324 | regs[3] = errno_h2g(errno); |
325 | break; |
326 | |
327 | case TARGET_SYS_select_one: |
328 | { |
329 | uint32_t fd = regs[3]; |
330 | uint32_t rq = regs[4]; |
331 | uint32_t target_tv = regs[5]; |
332 | uint32_t target_tvv[2]; |
333 | |
334 | struct timeval tv = {0}; |
335 | |
336 | if (target_tv) { |
337 | cpu_memory_rw_debug(cs, target_tv, |
338 | (uint8_t *)target_tvv, sizeof(target_tvv), 0); |
339 | tv.tv_sec = (int32_t)tswap32(target_tvv[0]); |
340 | tv.tv_usec = (int32_t)tswap32(target_tvv[1]); |
341 | } |
342 | if (fd < 3 && sim_console) { |
343 | if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) { |
344 | regs[2] = 1; |
345 | } else if (fd == 0 && rq == SELECT_ONE_READ) { |
346 | regs[2] = sim_console->input.offset > 0; |
347 | } else { |
348 | regs[2] = 0; |
349 | } |
350 | regs[3] = 0; |
351 | } else { |
352 | fd_set fdset; |
353 | |
354 | FD_ZERO(&fdset); |
355 | FD_SET(fd, &fdset); |
356 | regs[2] = select(fd + 1, |
357 | rq == SELECT_ONE_READ ? &fdset : NULL, |
358 | rq == SELECT_ONE_WRITE ? &fdset : NULL, |
359 | rq == SELECT_ONE_EXCEPT ? &fdset : NULL, |
360 | target_tv ? &tv : NULL); |
361 | regs[3] = errno_h2g(errno); |
362 | } |
363 | } |
364 | break; |
365 | |
366 | case TARGET_SYS_argc: |
367 | regs[2] = semihosting_get_argc(); |
368 | regs[3] = 0; |
369 | break; |
370 | |
371 | case TARGET_SYS_argv_sz: |
372 | { |
373 | int argc = semihosting_get_argc(); |
374 | int sz = (argc + 1) * sizeof(uint32_t); |
375 | int i; |
376 | |
377 | for (i = 0; i < argc; ++i) { |
378 | sz += 1 + strlen(semihosting_get_arg(i)); |
379 | } |
380 | regs[2] = sz; |
381 | regs[3] = 0; |
382 | } |
383 | break; |
384 | |
385 | case TARGET_SYS_argv: |
386 | { |
387 | int argc = semihosting_get_argc(); |
388 | int str_offset = (argc + 1) * sizeof(uint32_t); |
389 | int i; |
390 | uint32_t argptr; |
391 | |
392 | for (i = 0; i < argc; ++i) { |
393 | const char *str = semihosting_get_arg(i); |
394 | int str_size = strlen(str) + 1; |
395 | |
396 | argptr = tswap32(regs[3] + str_offset); |
397 | |
398 | cpu_memory_rw_debug(cs, |
399 | regs[3] + i * sizeof(uint32_t), |
400 | (uint8_t *)&argptr, sizeof(argptr), 1); |
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 = cpu_physical_memory_map(base, &len, 1); |
422 | |
423 | if (buf && len) { |
424 | memset(buf, regs[4], len); |
425 | cpu_physical_memory_unmap(buf, len, 1, 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 | } |
444 | |