]>
Commit | Line | Data |
---|---|---|
f7bbcfb5 MW |
1 | /* |
2 | * Lattice Mico32 semihosting syscall interface | |
3 | * | |
4 | * Copyright (c) 2014 Michael Walle <[email protected]> | |
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 "helper.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 | } |