xref: /openbmc/qemu/target/xtensa/xtensa-semi.c (revision 4889d9666076d8164171d1208ffb8b2be10463f6)
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                 hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr);
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 = address_space_map(as, paddr, &sz, !is_write, attrs);
224                 uint32_t io_done;
225                 bool error = false;
226 
227                 if (buf) {
228                     vaddr += io_sz;
229                     len -= io_sz;
230                     if (fd < 3 && sim_console) {
231                         if (is_write && (fd == 1 || fd == 2)) {
232                             io_done = qemu_chr_fe_write_all(&sim_console->fe,
233                                                             buf, io_sz);
234                             regs[3] = errno_h2g(errno);
235                         } else if (!is_write && fd == 0) {
236                             if (sim_console->input.offset) {
237                                 io_done = sim_console->input.offset;
238                                 if (io_sz < io_done) {
239                                     io_done = io_sz;
240                                 }
241                                 memcpy(buf, sim_console->input.buffer, io_done);
242                                 memmove(sim_console->input.buffer,
243                                         sim_console->input.buffer + io_done,
244                                         sim_console->input.offset - io_done);
245                                 sim_console->input.offset -= io_done;
246                                 qemu_chr_fe_accept_input(&sim_console->fe);
247                             } else {
248                                 io_done = -1;
249                                 regs[3] = TARGET_EAGAIN;
250                             }
251                         } else {
252                             qemu_log_mask(LOG_GUEST_ERROR,
253                                           "%s fd %d is not supported with chardev console\n",
254                                           is_write ?
255                                           "writing to" : "reading from", fd);
256                             io_done = -1;
257                             regs[3] = TARGET_EBADF;
258                         }
259                     } else {
260                         io_done = is_write ?
261                             write(fd, buf, io_sz) :
262                             read(fd, buf, io_sz);
263                         regs[3] = errno_h2g(errno);
264                     }
265                     if (io_done == -1) {
266                         error = true;
267                         io_done = 0;
268                     }
269                     address_space_unmap(as, buf, sz, !is_write, io_done);
270                 } else {
271                     error = true;
272                     regs[3] = TARGET_EINVAL;
273                     break;
274                 }
275                 if (error) {
276                     if (!len_done) {
277                         len_done = -1;
278                     }
279                     break;
280                 }
281                 len_done += io_done;
282                 if (io_done < io_sz) {
283                     break;
284                 }
285             }
286             regs[2] = len_done;
287         }
288         break;
289 
290     case TARGET_SYS_open:
291         {
292             char name[1024];
293             int rc;
294             int i;
295 
296             for (i = 0; i < ARRAY_SIZE(name); ++i) {
297                 rc = cpu_memory_rw_debug(cs, regs[3] + i,
298                                          (uint8_t *)name + i, 1, 0);
299                 if (rc != 0 || name[i] == 0) {
300                     break;
301                 }
302             }
303 
304             if (rc == 0 && i < ARRAY_SIZE(name)) {
305                 regs[2] = open(name, regs[4], regs[5]);
306                 regs[3] = errno_h2g(errno);
307             } else {
308                 regs[2] = -1;
309                 regs[3] = TARGET_EINVAL;
310             }
311         }
312         break;
313 
314     case TARGET_SYS_close:
315         if (regs[3] < 3) {
316             regs[2] = regs[3] = 0;
317         } else {
318             regs[2] = close(regs[3]);
319             regs[3] = errno_h2g(errno);
320         }
321         break;
322 
323     case TARGET_SYS_lseek:
324         regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]);
325         regs[3] = errno_h2g(errno);
326         break;
327 
328     case TARGET_SYS_select_one:
329         {
330             uint32_t fd = regs[3];
331             uint32_t rq = regs[4];
332             uint32_t target_tv = regs[5];
333 
334             struct timeval tv = {0};
335 
336             if (target_tv) {
337                 get_user_u32(tv.tv_sec, target_tv);
338                 get_user_u32(tv.tv_sec, target_tv + 4);
339             }
340             if (fd < 3 && sim_console) {
341                 if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) {
342                     regs[2] = 1;
343                 } else if (fd == 0 && rq == SELECT_ONE_READ) {
344                     regs[2] = sim_console->input.offset > 0;
345                 } else {
346                     regs[2] = 0;
347                 }
348                 regs[3] = 0;
349             } else {
350                 fd_set fdset;
351 
352                 FD_ZERO(&fdset);
353                 FD_SET(fd, &fdset);
354                 regs[2] = select(fd + 1,
355                                  rq == SELECT_ONE_READ   ? &fdset : NULL,
356                                  rq == SELECT_ONE_WRITE  ? &fdset : NULL,
357                                  rq == SELECT_ONE_EXCEPT ? &fdset : NULL,
358                                  target_tv ? &tv : NULL);
359                 regs[3] = errno_h2g(errno);
360             }
361         }
362         break;
363 
364     case TARGET_SYS_argc:
365         regs[2] = semihosting_get_argc();
366         regs[3] = 0;
367         break;
368 
369     case TARGET_SYS_argv_sz:
370         {
371             int argc = semihosting_get_argc();
372             int sz = (argc + 1) * sizeof(uint32_t);
373             int i;
374 
375             for (i = 0; i < argc; ++i) {
376                 sz += 1 + strlen(semihosting_get_arg(i));
377             }
378             regs[2] = sz;
379             regs[3] = 0;
380         }
381         break;
382 
383     case TARGET_SYS_argv:
384         {
385             int argc = semihosting_get_argc();
386             int str_offset = (argc + 1) * sizeof(uint32_t);
387             int i;
388             uint32_t argptr;
389 
390             for (i = 0; i < argc; ++i) {
391                 const char *str = semihosting_get_arg(i);
392                 int str_size = strlen(str) + 1;
393 
394                 put_user_u32(regs[3] + str_offset,
395                              regs[3] + i * sizeof(uint32_t));
396                 cpu_memory_rw_debug(cs,
397                                     regs[3] + str_offset,
398                                     (uint8_t *)str, str_size, 1);
399                 str_offset += str_size;
400             }
401             argptr = 0;
402             cpu_memory_rw_debug(cs,
403                                 regs[3] + i * sizeof(uint32_t),
404                                 (uint8_t *)&argptr, sizeof(argptr), 1);
405             regs[3] = 0;
406         }
407         break;
408 
409     case TARGET_SYS_memset:
410         {
411             uint32_t base = regs[3];
412             uint32_t sz = regs[5];
413 
414             while (sz) {
415                 hwaddr len = sz;
416                 void *buf = address_space_map(as, base, &len, true, attrs);
417 
418                 if (buf && len) {
419                     memset(buf, regs[4], len);
420                     address_space_unmap(as, buf, len, true, len);
421                 } else {
422                     len = 1;
423                 }
424                 base += len;
425                 sz -= len;
426             }
427             regs[2] = regs[3];
428             regs[3] = 0;
429         }
430         break;
431 
432     default:
433         qemu_log_mask(LOG_GUEST_ERROR, "%s(%d): not implemented\n", __func__, regs[2]);
434         regs[2] = -1;
435         regs[3] = TARGET_ENOSYS;
436         break;
437     }
438     qemu_plugin_vcpu_hostcall_cb(cs, last_pc);
439 }
440