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