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