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