]>
Commit | Line | Data |
---|---|---|
f7bbcfb5 MW |
1 | /* |
2 | * Lattice Mico32 semihosting syscall interface | |
3 | * | |
4 | * Copyright (c) 2014 Michael Walle <[email protected]> | |
5 | * | |
fcf5ef2a | 6 | * Based on target/m68k/m68k-semi.c, which is |
f7bbcfb5 MW |
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 | ||
ea99dde1 | 13 | #include "qemu/osdep.h" |
f7bbcfb5 | 14 | #include "cpu.h" |
2ef6175a | 15 | #include "exec/helper-proto.h" |
f7bbcfb5 MW |
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 | } |