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