xref: /openbmc/qemu/target/xtensa/xtensa-semi.c (revision fcf5ef2a)
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 "exec/helper-proto.h"
31 #include "qemu/log.h"
32 
33 enum {
34     TARGET_SYS_exit = 1,
35     TARGET_SYS_read = 3,
36     TARGET_SYS_write = 4,
37     TARGET_SYS_open = 5,
38     TARGET_SYS_close = 6,
39     TARGET_SYS_lseek = 19,
40     TARGET_SYS_select_one = 29,
41 
42     TARGET_SYS_argc = 1000,
43     TARGET_SYS_argv_sz = 1001,
44     TARGET_SYS_argv = 1002,
45     TARGET_SYS_memset = 1004,
46 };
47 
48 enum {
49     SELECT_ONE_READ   = 1,
50     SELECT_ONE_WRITE  = 2,
51     SELECT_ONE_EXCEPT = 3,
52 };
53 
54 enum {
55     TARGET_EPERM        =  1,
56     TARGET_ENOENT       =  2,
57     TARGET_ESRCH        =  3,
58     TARGET_EINTR        =  4,
59     TARGET_EIO          =  5,
60     TARGET_ENXIO        =  6,
61     TARGET_E2BIG        =  7,
62     TARGET_ENOEXEC      =  8,
63     TARGET_EBADF        =  9,
64     TARGET_ECHILD       = 10,
65     TARGET_EAGAIN       = 11,
66     TARGET_ENOMEM       = 12,
67     TARGET_EACCES       = 13,
68     TARGET_EFAULT       = 14,
69     TARGET_ENOTBLK      = 15,
70     TARGET_EBUSY        = 16,
71     TARGET_EEXIST       = 17,
72     TARGET_EXDEV        = 18,
73     TARGET_ENODEV       = 19,
74     TARGET_ENOTDIR      = 20,
75     TARGET_EISDIR       = 21,
76     TARGET_EINVAL       = 22,
77     TARGET_ENFILE       = 23,
78     TARGET_EMFILE       = 24,
79     TARGET_ENOTTY       = 25,
80     TARGET_ETXTBSY      = 26,
81     TARGET_EFBIG        = 27,
82     TARGET_ENOSPC       = 28,
83     TARGET_ESPIPE       = 29,
84     TARGET_EROFS        = 30,
85     TARGET_EMLINK       = 31,
86     TARGET_EPIPE        = 32,
87     TARGET_EDOM         = 33,
88     TARGET_ERANGE       = 34,
89     TARGET_ENOSYS       = 88,
90     TARGET_ELOOP        = 92,
91 };
92 
93 static uint32_t errno_h2g(int host_errno)
94 {
95     static const uint32_t guest_errno[] = {
96         [EPERM]         = TARGET_EPERM,
97         [ENOENT]        = TARGET_ENOENT,
98         [ESRCH]         = TARGET_ESRCH,
99         [EINTR]         = TARGET_EINTR,
100         [EIO]           = TARGET_EIO,
101         [ENXIO]         = TARGET_ENXIO,
102         [E2BIG]         = TARGET_E2BIG,
103         [ENOEXEC]       = TARGET_ENOEXEC,
104         [EBADF]         = TARGET_EBADF,
105         [ECHILD]        = TARGET_ECHILD,
106         [EAGAIN]        = TARGET_EAGAIN,
107         [ENOMEM]        = TARGET_ENOMEM,
108         [EACCES]        = TARGET_EACCES,
109         [EFAULT]        = TARGET_EFAULT,
110 #ifdef ENOTBLK
111         [ENOTBLK]       = TARGET_ENOTBLK,
112 #endif
113         [EBUSY]         = TARGET_EBUSY,
114         [EEXIST]        = TARGET_EEXIST,
115         [EXDEV]         = TARGET_EXDEV,
116         [ENODEV]        = TARGET_ENODEV,
117         [ENOTDIR]       = TARGET_ENOTDIR,
118         [EISDIR]        = TARGET_EISDIR,
119         [EINVAL]        = TARGET_EINVAL,
120         [ENFILE]        = TARGET_ENFILE,
121         [EMFILE]        = TARGET_EMFILE,
122         [ENOTTY]        = TARGET_ENOTTY,
123 #ifdef ETXTBSY
124         [ETXTBSY]       = TARGET_ETXTBSY,
125 #endif
126         [EFBIG]         = TARGET_EFBIG,
127         [ENOSPC]        = TARGET_ENOSPC,
128         [ESPIPE]        = TARGET_ESPIPE,
129         [EROFS]         = TARGET_EROFS,
130         [EMLINK]        = TARGET_EMLINK,
131         [EPIPE]         = TARGET_EPIPE,
132         [EDOM]          = TARGET_EDOM,
133         [ERANGE]        = TARGET_ERANGE,
134         [ENOSYS]        = TARGET_ENOSYS,
135 #ifdef ELOOP
136         [ELOOP]         = TARGET_ELOOP,
137 #endif
138     };
139 
140     if (host_errno == 0) {
141         return 0;
142     } else if (host_errno > 0 && host_errno < ARRAY_SIZE(guest_errno) &&
143             guest_errno[host_errno]) {
144         return guest_errno[host_errno];
145     } else {
146         return TARGET_EINVAL;
147     }
148 }
149 
150 void HELPER(simcall)(CPUXtensaState *env)
151 {
152     CPUState *cs = CPU(xtensa_env_get_cpu(env));
153     uint32_t *regs = env->regs;
154 
155     switch (regs[2]) {
156     case TARGET_SYS_exit:
157         qemu_log("exit(%d) simcall\n", regs[3]);
158         exit(regs[3]);
159         break;
160 
161     case TARGET_SYS_read:
162     case TARGET_SYS_write:
163         {
164             bool is_write = regs[2] == TARGET_SYS_write;
165             uint32_t fd = regs[3];
166             uint32_t vaddr = regs[4];
167             uint32_t len = regs[5];
168 
169             while (len > 0) {
170                 hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr);
171                 uint32_t page_left =
172                     TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1));
173                 uint32_t io_sz = page_left < len ? page_left : len;
174                 hwaddr sz = io_sz;
175                 void *buf = cpu_physical_memory_map(paddr, &sz, is_write);
176 
177                 if (buf) {
178                     vaddr += io_sz;
179                     len -= io_sz;
180                     regs[2] = is_write ?
181                         write(fd, buf, io_sz) :
182                         read(fd, buf, io_sz);
183                     regs[3] = errno_h2g(errno);
184                     cpu_physical_memory_unmap(buf, sz, is_write, sz);
185                     if (regs[2] == -1) {
186                         break;
187                     }
188                 } else {
189                     regs[2] = -1;
190                     regs[3] = TARGET_EINVAL;
191                     break;
192                 }
193             }
194         }
195         break;
196 
197     case TARGET_SYS_open:
198         {
199             char name[1024];
200             int rc;
201             int i;
202 
203             for (i = 0; i < ARRAY_SIZE(name); ++i) {
204                 rc = cpu_memory_rw_debug(cs, regs[3] + i,
205                                          (uint8_t *)name + i, 1, 0);
206                 if (rc != 0 || name[i] == 0) {
207                     break;
208                 }
209             }
210 
211             if (rc == 0 && i < ARRAY_SIZE(name)) {
212                 regs[2] = open(name, regs[4], regs[5]);
213                 regs[3] = errno_h2g(errno);
214             } else {
215                 regs[2] = -1;
216                 regs[3] = TARGET_EINVAL;
217             }
218         }
219         break;
220 
221     case TARGET_SYS_close:
222         if (regs[3] < 3) {
223             regs[2] = regs[3] = 0;
224         } else {
225             regs[2] = close(regs[3]);
226             regs[3] = errno_h2g(errno);
227         }
228         break;
229 
230     case TARGET_SYS_lseek:
231         regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]);
232         regs[3] = errno_h2g(errno);
233         break;
234 
235     case TARGET_SYS_select_one:
236         {
237             uint32_t fd = regs[3];
238             uint32_t rq = regs[4];
239             uint32_t target_tv = regs[5];
240             uint32_t target_tvv[2];
241 
242             struct timeval tv = {0};
243             fd_set fdset;
244 
245             FD_ZERO(&fdset);
246             FD_SET(fd, &fdset);
247 
248             if (target_tv) {
249                 cpu_memory_rw_debug(cs, target_tv,
250                         (uint8_t *)target_tvv, sizeof(target_tvv), 0);
251                 tv.tv_sec = (int32_t)tswap32(target_tvv[0]);
252                 tv.tv_usec = (int32_t)tswap32(target_tvv[1]);
253             }
254             regs[2] = select(fd + 1,
255                     rq == SELECT_ONE_READ   ? &fdset : NULL,
256                     rq == SELECT_ONE_WRITE  ? &fdset : NULL,
257                     rq == SELECT_ONE_EXCEPT ? &fdset : NULL,
258                     target_tv ? &tv : NULL);
259             regs[3] = errno_h2g(errno);
260         }
261         break;
262 
263     case TARGET_SYS_argc:
264         regs[2] = 1;
265         regs[3] = 0;
266         break;
267 
268     case TARGET_SYS_argv_sz:
269         regs[2] = 128;
270         regs[3] = 0;
271         break;
272 
273     case TARGET_SYS_argv:
274         {
275             struct Argv {
276                 uint32_t argptr[2];
277                 char text[120];
278             } argv = {
279                 {0, 0},
280                 "test"
281             };
282 
283             argv.argptr[0] = tswap32(regs[3] + offsetof(struct Argv, text));
284             cpu_memory_rw_debug(cs,
285                                 regs[3], (uint8_t *)&argv, sizeof(argv), 1);
286         }
287         break;
288 
289     case TARGET_SYS_memset:
290         {
291             uint32_t base = regs[3];
292             uint32_t sz = regs[5];
293 
294             while (sz) {
295                 hwaddr len = sz;
296                 void *buf = cpu_physical_memory_map(base, &len, 1);
297 
298                 if (buf && len) {
299                     memset(buf, regs[4], len);
300                     cpu_physical_memory_unmap(buf, len, 1, len);
301                 } else {
302                     len = 1;
303                 }
304                 base += len;
305                 sz -= len;
306             }
307             regs[2] = regs[3];
308             regs[3] = 0;
309         }
310         break;
311 
312     default:
313         qemu_log_mask(LOG_GUEST_ERROR, "%s(%d): not implemented\n", __func__, regs[2]);
314         regs[2] = -1;
315         regs[3] = TARGET_ENOSYS;
316         break;
317     }
318 }
319