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