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