Add qemu 2.4.0
[kvmfornfv.git] / qemu / target-mips / mips-semi.c
1 /*
2  * Unified Hosting Interface syscalls.
3  *
4  * Copyright (c) 2015 Imagination Technologies
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <sys/stat.h>
21 #include "cpu.h"
22 #include "exec/helper-proto.h"
23 #include "exec/softmmu-semi.h"
24 #include "exec/semihost.h"
25
26 typedef enum UHIOp {
27     UHI_exit = 1,
28     UHI_open = 2,
29     UHI_close = 3,
30     UHI_read = 4,
31     UHI_write = 5,
32     UHI_lseek = 6,
33     UHI_unlink = 7,
34     UHI_fstat = 8,
35     UHI_argc = 9,
36     UHI_argnlen = 10,
37     UHI_argn = 11,
38     UHI_plog = 13,
39     UHI_assert = 14,
40     UHI_pread = 19,
41     UHI_pwrite = 20,
42     UHI_link = 22
43 } UHIOp;
44
45 typedef struct UHIStat {
46     int16_t uhi_st_dev;
47     uint16_t uhi_st_ino;
48     uint32_t uhi_st_mode;
49     uint16_t uhi_st_nlink;
50     uint16_t uhi_st_uid;
51     uint16_t uhi_st_gid;
52     int16_t uhi_st_rdev;
53     uint64_t uhi_st_size;
54     uint64_t uhi_st_atime;
55     uint64_t uhi_st_spare1;
56     uint64_t uhi_st_mtime;
57     uint64_t uhi_st_spare2;
58     uint64_t uhi_st_ctime;
59     uint64_t uhi_st_spare3;
60     uint64_t uhi_st_blksize;
61     uint64_t uhi_st_blocks;
62     uint64_t uhi_st_spare4[2];
63 } UHIStat;
64
65 enum UHIOpenFlags {
66     UHIOpen_RDONLY = 0x0,
67     UHIOpen_WRONLY = 0x1,
68     UHIOpen_RDWR   = 0x2,
69     UHIOpen_APPEND = 0x8,
70     UHIOpen_CREAT  = 0x200,
71     UHIOpen_TRUNC  = 0x400,
72     UHIOpen_EXCL   = 0x800
73 };
74
75 /* Errno values taken from asm-mips/errno.h */
76 static uint16_t host_to_mips_errno[] = {
77     [ENAMETOOLONG] = 78,
78 #ifdef EOVERFLOW
79     [EOVERFLOW]    = 79,
80 #endif
81 #ifdef ELOOP
82     [ELOOP]        = 90,
83 #endif
84 };
85
86 static int errno_mips(int err)
87 {
88     if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
89         return EINVAL;
90     } else if (host_to_mips_errno[err]) {
91         return host_to_mips_errno[err];
92     } else {
93         return err;
94     }
95 }
96
97 static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
98                                target_ulong vaddr)
99 {
100     hwaddr len = sizeof(struct UHIStat);
101     UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
102     if (!dst) {
103         errno = EFAULT;
104         return -1;
105     }
106
107     dst->uhi_st_dev = tswap16(src->st_dev);
108     dst->uhi_st_ino = tswap16(src->st_ino);
109     dst->uhi_st_mode = tswap32(src->st_mode);
110     dst->uhi_st_nlink = tswap16(src->st_nlink);
111     dst->uhi_st_uid = tswap16(src->st_uid);
112     dst->uhi_st_gid = tswap16(src->st_gid);
113     dst->uhi_st_rdev = tswap16(src->st_rdev);
114     dst->uhi_st_size = tswap64(src->st_size);
115     dst->uhi_st_atime = tswap64(src->st_atime);
116     dst->uhi_st_mtime = tswap64(src->st_mtime);
117     dst->uhi_st_ctime = tswap64(src->st_ctime);
118 #ifdef _WIN32
119     dst->uhi_st_blksize = 0;
120     dst->uhi_st_blocks = 0;
121 #else
122     dst->uhi_st_blksize = tswap64(src->st_blksize);
123     dst->uhi_st_blocks = tswap64(src->st_blocks);
124 #endif
125     unlock_user(dst, vaddr, len);
126     return 0;
127 }
128
129 static int get_open_flags(target_ulong target_flags)
130 {
131     int open_flags = 0;
132
133     if (target_flags & UHIOpen_RDWR) {
134         open_flags |= O_RDWR;
135     } else if (target_flags & UHIOpen_WRONLY) {
136         open_flags |= O_WRONLY;
137     } else {
138         open_flags |= O_RDONLY;
139     }
140
141     open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
142     open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
143     open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
144     open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
145
146     return open_flags;
147 }
148
149 static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
150                          target_ulong len, target_ulong offset)
151 {
152     int num_of_bytes;
153     void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
154     if (!dst) {
155         errno = EFAULT;
156         return -1;
157     }
158
159     if (offset) {
160 #ifdef _WIN32
161         num_of_bytes = 0;
162 #else
163         num_of_bytes = pwrite(fd, dst, len, offset);
164 #endif
165     } else {
166         num_of_bytes = write(fd, dst, len);
167     }
168
169     unlock_user(dst, vaddr, 0);
170     return num_of_bytes;
171 }
172
173 static int read_from_file(CPUMIPSState *env, target_ulong fd,
174                           target_ulong vaddr, target_ulong len,
175                           target_ulong offset)
176 {
177     int num_of_bytes;
178     void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
179     if (!dst) {
180         errno = EFAULT;
181         return -1;
182     }
183
184     if (offset) {
185 #ifdef _WIN32
186         num_of_bytes = 0;
187 #else
188         num_of_bytes = pread(fd, dst, len, offset);
189 #endif
190     } else {
191         num_of_bytes = read(fd, dst, len);
192     }
193
194     unlock_user(dst, vaddr, len);
195     return num_of_bytes;
196 }
197
198 static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
199                                target_ulong vaddr)
200 {
201     int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
202     char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
203     if (!dst) {
204         return -1;
205     }
206
207     strcpy(dst, semihosting_get_arg(arg_num));
208
209     unlock_user(dst, vaddr, strsize);
210     return 0;
211 }
212
213 #define GET_TARGET_STRING(p, addr)              \
214     do {                                        \
215         p = lock_user_string(addr);             \
216         if (!p) {                               \
217             gpr[2] = -1;                        \
218             gpr[3] = EFAULT;                    \
219             goto uhi_done;                      \
220         }                                       \
221     } while (0)
222
223 #define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
224     do {                                                \
225         p = lock_user_string(addr);                     \
226         if (!p) {                                       \
227             gpr[2] = -1;                                \
228             gpr[3] = EFAULT;                            \
229             goto uhi_done;                              \
230         }                                               \
231         p2 = lock_user_string(addr2);                   \
232         if (!p2) {                                      \
233             unlock_user(p, addr, 0);                    \
234             gpr[2] = -1;                                \
235             gpr[3] = EFAULT;                            \
236             goto uhi_done;                              \
237         }                                               \
238     } while (0)
239
240 #define FREE_TARGET_STRING(p, gpr)              \
241     do {                                        \
242         unlock_user(p, gpr, 0);                 \
243     } while (0)
244
245 void helper_do_semihosting(CPUMIPSState *env)
246 {
247     target_ulong *gpr = env->active_tc.gpr;
248     const UHIOp op = gpr[25];
249     char *p, *p2;
250
251     switch (op) {
252     case UHI_exit:
253         qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
254         exit(gpr[4]);
255     case UHI_open:
256         GET_TARGET_STRING(p, gpr[4]);
257         if (!strcmp("/dev/stdin", p)) {
258             gpr[2] = 0;
259         } else if (!strcmp("/dev/stdout", p)) {
260             gpr[2] = 1;
261         } else if (!strcmp("/dev/stderr", p)) {
262             gpr[2] = 2;
263         } else {
264             gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
265             gpr[3] = errno_mips(errno);
266         }
267         FREE_TARGET_STRING(p, gpr[4]);
268         break;
269     case UHI_close:
270         if (gpr[4] < 3) {
271             /* ignore closing stdin/stdout/stderr */
272             gpr[2] = 0;
273             goto uhi_done;
274         }
275         gpr[2] = close(gpr[4]);
276         gpr[3] = errno_mips(errno);
277         break;
278     case UHI_read:
279         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
280         gpr[3] = errno_mips(errno);
281         break;
282     case UHI_write:
283         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
284         gpr[3] = errno_mips(errno);
285         break;
286     case UHI_lseek:
287         gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
288         gpr[3] = errno_mips(errno);
289         break;
290     case UHI_unlink:
291         GET_TARGET_STRING(p, gpr[4]);
292         gpr[2] = remove(p);
293         gpr[3] = errno_mips(errno);
294         FREE_TARGET_STRING(p, gpr[4]);
295         break;
296     case UHI_fstat:
297         {
298             struct stat sbuf;
299             memset(&sbuf, 0, sizeof(sbuf));
300             gpr[2] = fstat(gpr[4], &sbuf);
301             gpr[3] = errno_mips(errno);
302             if (gpr[2]) {
303                 goto uhi_done;
304             }
305             gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
306             gpr[3] = errno_mips(errno);
307         }
308         break;
309     case UHI_argc:
310         gpr[2] = semihosting_get_argc();
311         break;
312     case UHI_argnlen:
313         if (gpr[4] >= semihosting_get_argc()) {
314             gpr[2] = -1;
315             goto uhi_done;
316         }
317         gpr[2] = strlen(semihosting_get_arg(gpr[4]));
318         break;
319     case UHI_argn:
320         if (gpr[4] >= semihosting_get_argc()) {
321             gpr[2] = -1;
322             goto uhi_done;
323         }
324         gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
325         break;
326     case UHI_plog:
327         GET_TARGET_STRING(p, gpr[4]);
328         p2 = strstr(p, "%d");
329         if (p2) {
330             int char_num = p2 - p;
331             char *buf = g_malloc(char_num + 1);
332             strncpy(buf, p, char_num);
333             buf[char_num] = '\0';
334             gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2);
335             g_free(buf);
336         } else {
337             gpr[2] = printf("%s", p);
338         }
339         FREE_TARGET_STRING(p, gpr[4]);
340         break;
341     case UHI_assert:
342         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
343         printf("assertion '");
344         printf("\"%s\"", p);
345         printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
346         FREE_TARGET_STRING(p2, gpr[5]);
347         FREE_TARGET_STRING(p, gpr[4]);
348         abort();
349         break;
350     case UHI_pread:
351         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
352         gpr[3] = errno_mips(errno);
353         break;
354     case UHI_pwrite:
355         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
356         gpr[3] = errno_mips(errno);
357         break;
358 #ifndef _WIN32
359     case UHI_link:
360         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
361         gpr[2] = link(p, p2);
362         gpr[3] = errno_mips(errno);
363         FREE_TARGET_STRING(p2, gpr[5]);
364         FREE_TARGET_STRING(p, gpr[4]);
365         break;
366 #endif
367     default:
368         fprintf(stderr, "Unknown UHI operation %d\n", op);
369         abort();
370     }
371 uhi_done:
372     return;
373 }