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