Add qemu 2.4.0
[kvmfornfv.git] / qemu / target-lm32 / lm32-semi.c
1 /*
2  *  Lattice Mico32 semihosting syscall interface
3  *
4  *  Copyright (c) 2014 Michael Walle <michael@walle.cc>
5  *
6  * Based on target-m68k/m68k-semi.c, which is
7  *  Copyright (c) 2005-2007 CodeSourcery.
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  */
12
13 #include <errno.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <stddef.h>
17 #include "cpu.h"
18 #include "exec/helper-proto.h"
19 #include "qemu/log.h"
20 #include "exec/softmmu-semi.h"
21
22 enum {
23     TARGET_SYS_exit    = 1,
24     TARGET_SYS_open    = 2,
25     TARGET_SYS_close   = 3,
26     TARGET_SYS_read    = 4,
27     TARGET_SYS_write   = 5,
28     TARGET_SYS_lseek   = 6,
29     TARGET_SYS_fstat   = 10,
30     TARGET_SYS_stat    = 15,
31 };
32
33 enum {
34     NEWLIB_O_RDONLY    =   0x0,
35     NEWLIB_O_WRONLY    =   0x1,
36     NEWLIB_O_RDWR      =   0x2,
37     NEWLIB_O_APPEND    =   0x8,
38     NEWLIB_O_CREAT     = 0x200,
39     NEWLIB_O_TRUNC     = 0x400,
40     NEWLIB_O_EXCL      = 0x800,
41 };
42
43 static int translate_openflags(int flags)
44 {
45     int hf;
46
47     if (flags & NEWLIB_O_WRONLY) {
48         hf = O_WRONLY;
49     } else if (flags & NEWLIB_O_RDWR) {
50         hf = O_RDWR;
51     } else {
52         hf = O_RDONLY;
53     }
54
55     if (flags & NEWLIB_O_APPEND) {
56         hf |= O_APPEND;
57     }
58
59     if (flags & NEWLIB_O_CREAT) {
60         hf |= O_CREAT;
61     }
62
63     if (flags & NEWLIB_O_TRUNC) {
64         hf |= O_TRUNC;
65     }
66
67     if (flags & NEWLIB_O_EXCL) {
68         hf |= O_EXCL;
69     }
70
71     return hf;
72 }
73
74 struct newlib_stat {
75     int16_t     newlib_st_dev;     /* device */
76     uint16_t    newlib_st_ino;     /* inode */
77     uint16_t    newlib_st_mode;    /* protection */
78     uint16_t    newlib_st_nlink;   /* number of hard links */
79     uint16_t    newlib_st_uid;     /* user ID of owner */
80     uint16_t    newlib_st_gid;     /* group ID of owner */
81     int16_t     newlib_st_rdev;    /* device type (if inode device) */
82     int32_t     newlib_st_size;    /* total size, in bytes */
83     int32_t     newlib_st_atime;   /* time of last access */
84     uint32_t    newlib_st_spare1;
85     int32_t     newlib_st_mtime;   /* time of last modification */
86     uint32_t    newlib_st_spare2;
87     int32_t     newlib_st_ctime;   /* time of last change */
88     uint32_t    newlib_st_spare3;
89 } QEMU_PACKED;
90
91 static int translate_stat(CPULM32State *env, target_ulong addr,
92         struct stat *s)
93 {
94     struct newlib_stat *p;
95
96     p = lock_user(VERIFY_WRITE, addr, sizeof(struct newlib_stat), 0);
97     if (!p) {
98         return 0;
99     }
100     p->newlib_st_dev = cpu_to_be16(s->st_dev);
101     p->newlib_st_ino = cpu_to_be16(s->st_ino);
102     p->newlib_st_mode = cpu_to_be16(s->st_mode);
103     p->newlib_st_nlink = cpu_to_be16(s->st_nlink);
104     p->newlib_st_uid = cpu_to_be16(s->st_uid);
105     p->newlib_st_gid = cpu_to_be16(s->st_gid);
106     p->newlib_st_rdev = cpu_to_be16(s->st_rdev);
107     p->newlib_st_size = cpu_to_be32(s->st_size);
108     p->newlib_st_atime = cpu_to_be32(s->st_atime);
109     p->newlib_st_mtime = cpu_to_be32(s->st_mtime);
110     p->newlib_st_ctime = cpu_to_be32(s->st_ctime);
111     unlock_user(p, addr, sizeof(struct newlib_stat));
112
113     return 1;
114 }
115
116 bool lm32_cpu_do_semihosting(CPUState *cs)
117 {
118     LM32CPU *cpu = LM32_CPU(cs);
119     CPULM32State *env = &cpu->env;
120
121     int ret = -1;
122     target_ulong nr, arg0, arg1, arg2;
123     void *p;
124     struct stat s;
125
126     nr = env->regs[R_R8];
127     arg0 = env->regs[R_R1];
128     arg1 = env->regs[R_R2];
129     arg2 = env->regs[R_R3];
130
131     switch (nr) {
132     case TARGET_SYS_exit:
133         /* void _exit(int rc) */
134         exit(arg0);
135
136     case TARGET_SYS_open:
137         /* int open(const char *pathname, int flags) */
138         p = lock_user_string(arg0);
139         if (!p) {
140             ret = -1;
141         } else {
142             ret = open(p, translate_openflags(arg2));
143             unlock_user(p, arg0, 0);
144         }
145         break;
146
147     case TARGET_SYS_read:
148         /* ssize_t read(int fd, const void *buf, size_t count) */
149         p = lock_user(VERIFY_WRITE, arg1, arg2, 0);
150         if (!p) {
151             ret = -1;
152         } else {
153             ret = read(arg0, p, arg2);
154             unlock_user(p, arg1, arg2);
155         }
156         break;
157
158     case TARGET_SYS_write:
159         /* ssize_t write(int fd, const void *buf, size_t count) */
160         p = lock_user(VERIFY_READ, arg1, arg2, 1);
161         if (!p) {
162             ret = -1;
163         } else {
164             ret = write(arg0, p, arg2);
165             unlock_user(p, arg1, 0);
166         }
167         break;
168
169     case TARGET_SYS_close:
170         /* int close(int fd) */
171         /* don't close stdin/stdout/stderr */
172         if (arg0 > 2) {
173             ret = close(arg0);
174         } else {
175             ret = 0;
176         }
177         break;
178
179     case TARGET_SYS_lseek:
180         /* off_t lseek(int fd, off_t offset, int whence */
181         ret = lseek(arg0, arg1, arg2);
182         break;
183
184     case TARGET_SYS_stat:
185         /* int stat(const char *path, struct stat *buf) */
186         p = lock_user_string(arg0);
187         if (!p) {
188             ret = -1;
189         } else {
190             ret = stat(p, &s);
191             unlock_user(p, arg0, 0);
192             if (translate_stat(env, arg1, &s) == 0) {
193                 ret = -1;
194             }
195         }
196         break;
197
198     case TARGET_SYS_fstat:
199         /* int stat(int fd, struct stat *buf) */
200         ret = fstat(arg0, &s);
201         if (ret == 0) {
202             if (translate_stat(env, arg1, &s) == 0) {
203                 ret = -1;
204             }
205         }
206         break;
207
208     default:
209         /* unhandled */
210         return false;
211     }
212
213     env->regs[R_R1] = ret;
214     return true;
215 }