Statistics
| Branch: | Revision:

root / arm-semi.c @ a87295e8

History | View | Annotate | Download (11.9 kB)

1
/*
2
 *  Arm "Angel" semihosting syscalls
3
 * 
4
 *  Copyright (c) 2005, 2007 CodeSourcery.
5
 *  Written by Paul Brook.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program; if not, write to the Free Software
19
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
 */
21

    
22
#include <sys/types.h>
23
#include <sys/stat.h>
24
#include <fcntl.h>
25
#include <unistd.h>
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <time.h>
29

    
30
#include "cpu.h"
31
#ifdef CONFIG_USER_ONLY
32
#include "qemu.h"
33

    
34
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
35
#else
36
#include "vl.h"
37
#endif
38

    
39
#define SYS_OPEN        0x01
40
#define SYS_CLOSE       0x02
41
#define SYS_WRITEC      0x03
42
#define SYS_WRITE0      0x04
43
#define SYS_WRITE       0x05
44
#define SYS_READ        0x06
45
#define SYS_READC       0x07
46
#define SYS_ISTTY       0x09
47
#define SYS_SEEK        0x0a
48
#define SYS_FLEN        0x0c
49
#define SYS_TMPNAM      0x0d
50
#define SYS_REMOVE      0x0e
51
#define SYS_RENAME      0x0f
52
#define SYS_CLOCK       0x10
53
#define SYS_TIME        0x11
54
#define SYS_SYSTEM      0x12
55
#define SYS_ERRNO       0x13
56
#define SYS_GET_CMDLINE 0x15
57
#define SYS_HEAPINFO    0x16
58
#define SYS_EXIT        0x18
59

    
60
#ifndef O_BINARY
61
#define O_BINARY 0
62
#endif
63

    
64
#define GDB_O_RDONLY  0x000
65
#define GDB_O_WRONLY  0x001
66
#define GDB_O_RDWR    0x002
67
#define GDB_O_APPEND  0x008
68
#define GDB_O_CREAT   0x200
69
#define GDB_O_TRUNC   0x400
70
#define GDB_O_BINARY  0
71

    
72
static int gdb_open_modeflags[12] = {
73
    GDB_O_RDONLY,
74
    GDB_O_RDONLY | GDB_O_BINARY,
75
    GDB_O_RDWR,
76
    GDB_O_RDWR | GDB_O_BINARY,
77
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
78
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
79
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
80
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
81
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
82
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
83
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
84
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
85
};
86

    
87
static int open_modeflags[12] = {
88
    O_RDONLY,
89
    O_RDONLY | O_BINARY,
90
    O_RDWR,
91
    O_RDWR | O_BINARY,
92
    O_WRONLY | O_CREAT | O_TRUNC,
93
    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
94
    O_RDWR | O_CREAT | O_TRUNC,
95
    O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
96
    O_WRONLY | O_CREAT | O_APPEND,
97
    O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
98
    O_RDWR | O_CREAT | O_APPEND,
99
    O_RDWR | O_CREAT | O_APPEND | O_BINARY
100
};
101

    
102
#ifdef CONFIG_USER_ONLY
103
static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
104
{
105
    if (code == (uint32_t)-1)
106
        ts->swi_errno = errno;
107
    return code;
108
}
109
#else
110
static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
111
{
112
    return code;
113
}
114

    
115
#include "softmmu-semi.h"
116
#endif
117

    
118
static target_ulong arm_semi_syscall_len;
119

    
120
static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
121
{
122
#ifdef CONFIG_USER_ONLY
123
    TaskState *ts = env->opaque;
124
#endif
125
    if (ret == (target_ulong)-1) {
126
#ifdef CONFIG_USER_ONLY
127
        ts->swi_errno = err;
128
#endif
129
        env->regs[0] = ret;
130
    } else {
131
        /* Fixup syscalls that use nonstardard return conventions.  */
132
        switch (env->regs[0]) {
133
        case SYS_WRITE:
134
        case SYS_READ:
135
            env->regs[0] = arm_semi_syscall_len - ret;
136
            break;
137
        case SYS_SEEK:
138
            env->regs[0] = 0;
139
            break;
140
        default:
141
            env->regs[0] = ret;
142
            break;
143
        }
144
    }
145
}
146

    
147
#define ARG(n) tget32(args + (n) * 4)
148
#define SET_ARG(n, val) tput32(args + (n) * 4,val)
149
uint32_t do_arm_semihosting(CPUState *env)
150
{
151
    target_ulong args;
152
    char * s;
153
    int nr;
154
    uint32_t ret;
155
    uint32_t len;
156
#ifdef CONFIG_USER_ONLY
157
    TaskState *ts = env->opaque;
158
#else
159
    CPUState *ts = env;
160
#endif
161

    
162
    nr = env->regs[0];
163
    args = env->regs[1];
164
    switch (nr) {
165
    case SYS_OPEN:
166
        s = lock_user_string(ARG(0));
167
        if (ARG(1) >= 12)
168
          return (uint32_t)-1;
169
        if (strcmp(s, ":tt") == 0) {
170
            if (ARG(1) < 4)
171
                return STDIN_FILENO;
172
            else
173
                return STDOUT_FILENO;
174
        }
175
        if (use_gdb_syscalls()) {
176
            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0), (int)ARG(2),
177
                           gdb_open_modeflags[ARG(1)]);
178
            return env->regs[0];
179
        } else {
180
            ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
181
        }
182
        unlock_user(s, ARG(0), 0);
183
        return ret;
184
    case SYS_CLOSE:
185
        if (use_gdb_syscalls()) {
186
            gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
187
            return env->regs[0];
188
        } else {
189
            return set_swi_errno(ts, close(ARG(0)));
190
        }
191
    case SYS_WRITEC:
192
        {
193
          char c = tget8(args);
194
          /* Write to debug console.  stderr is near enough.  */
195
          if (use_gdb_syscalls()) {
196
                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
197
                return env->regs[0];
198
          } else {
199
                return write(STDERR_FILENO, &c, 1);
200
          }
201
        }
202
    case SYS_WRITE0:
203
        s = lock_user_string(args);
204
        len = strlen(s);
205
        if (use_gdb_syscalls()) {
206
            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
207
            ret = env->regs[0];
208
        } else {
209
            ret = write(STDERR_FILENO, s, len);
210
        }
211
        unlock_user(s, args, 0);
212
        return ret;
213
    case SYS_WRITE:
214
        len = ARG(2);
215
        if (use_gdb_syscalls()) {
216
            arm_semi_syscall_len = len;
217
            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
218
            return env->regs[0];
219
        } else {
220
            s = lock_user(ARG(1), len, 1);
221
            ret = set_swi_errno(ts, write(ARG(0), s, len));
222
            unlock_user(s, ARG(1), 0);
223
            if (ret == (uint32_t)-1)
224
                return -1;
225
            return len - ret;
226
        }
227
    case SYS_READ:
228
        len = ARG(2);
229
        if (use_gdb_syscalls()) {
230
            arm_semi_syscall_len = len;
231
            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
232
            return env->regs[0];
233
        } else {
234
            s = lock_user(ARG(1), len, 0);
235
            do
236
              ret = set_swi_errno(ts, read(ARG(0), s, len));
237
            while (ret == -1 && errno == EINTR);
238
            unlock_user(s, ARG(1), len);
239
            if (ret == (uint32_t)-1)
240
                return -1;
241
            return len - ret;
242
        }
243
    case SYS_READC:
244
       /* XXX: Read from debug cosole. Not implemented.  */
245
        return 0;
246
    case SYS_ISTTY:
247
        if (use_gdb_syscalls()) {
248
            gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
249
            return env->regs[0];
250
        } else {
251
            return isatty(ARG(0));
252
        }
253
    case SYS_SEEK:
254
        if (use_gdb_syscalls()) {
255
            gdb_do_syscall(arm_semi_cb, "fseek,%x,%x,0", ARG(0), ARG(1));
256
            return env->regs[0];
257
        } else {
258
            ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
259
            if (ret == (uint32_t)-1)
260
              return -1;
261
            return 0;
262
        }
263
    case SYS_FLEN:
264
        if (use_gdb_syscalls()) {
265
            /* TODO: Use stat syscall.  */
266
            return -1;
267
        } else {
268
            struct stat buf;
269
            ret = set_swi_errno(ts, fstat(ARG(0), &buf));
270
            if (ret == (uint32_t)-1)
271
                return -1;
272
            return buf.st_size;
273
        }
274
    case SYS_TMPNAM:
275
        /* XXX: Not implemented.  */
276
        return -1;
277
    case SYS_REMOVE:
278
        if (use_gdb_syscalls()) {
279
            gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1));
280
            ret = env->regs[0];
281
        } else {
282
            s = lock_user_string(ARG(0));
283
            ret =  set_swi_errno(ts, remove(s));
284
            unlock_user(s, ARG(0), 0);
285
        }
286
        return ret;
287
    case SYS_RENAME:
288
        if (use_gdb_syscalls()) {
289
            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
290
                           ARG(0), (int)ARG(1), ARG(2), (int)ARG(3));
291
            return env->regs[0];
292
        } else {
293
            char *s2;
294
            s = lock_user_string(ARG(0));
295
            s2 = lock_user_string(ARG(2));
296
            ret = set_swi_errno(ts, rename(s, s2));
297
            unlock_user(s2, ARG(2), 0);
298
            unlock_user(s, ARG(0), 0);
299
            return ret;
300
        }
301
    case SYS_CLOCK:
302
        return clock() / (CLOCKS_PER_SEC / 100);
303
    case SYS_TIME:
304
        return set_swi_errno(ts, time(NULL));
305
    case SYS_SYSTEM:
306
        if (use_gdb_syscalls()) {
307
            gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1));
308
            return env->regs[0];
309
        } else {
310
            s = lock_user_string(ARG(0));
311
            ret = set_swi_errno(ts, system(s));
312
            unlock_user(s, ARG(0), 0);
313
        }
314
    case SYS_ERRNO:
315
#ifdef CONFIG_USER_ONLY
316
        return ts->swi_errno;
317
#else
318
        return 0;
319
#endif
320
    case SYS_GET_CMDLINE:
321
#ifdef CONFIG_USER_ONLY
322
        /* Build a commandline from the original argv.  */
323
        {
324
            char **arg = ts->info->host_argv;
325
            int len = ARG(1);
326
            /* lock the buffer on the ARM side */
327
            char *cmdline_buffer = (char*)lock_user(ARG(0), len, 0);
328

    
329
            s = cmdline_buffer;
330
            while (*arg && len > 2) {
331
                int n = strlen(*arg);
332

    
333
                if (s != cmdline_buffer) {
334
                    *(s++) = ' ';
335
                    len--;
336
                }
337
                if (n >= len)
338
                    n = len - 1;
339
                memcpy(s, *arg, n);
340
                s += n;
341
                len -= n;
342
                arg++;
343
            }
344
            /* Null terminate the string.  */
345
            *s = 0;
346
            len = s - cmdline_buffer;
347

    
348
            /* Unlock the buffer on the ARM side.  */
349
            unlock_user(cmdline_buffer, ARG(0), len);
350

    
351
            /* Adjust the commandline length argument.  */
352
            SET_ARG(1, len);
353

    
354
            /* Return success if commandline fit into buffer.  */
355
            return *arg ? -1 : 0;
356
        }
357
#else
358
      return -1;
359
#endif
360
    case SYS_HEAPINFO:
361
        {
362
            uint32_t *ptr;
363
            uint32_t limit;
364

    
365
#ifdef CONFIG_USER_ONLY
366
            /* Some C libraries assume the heap immediately follows .bss, so
367
               allocate it using sbrk.  */
368
            if (!ts->heap_limit) {
369
                long ret;
370

    
371
                ts->heap_base = do_brk(0);
372
                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
373
                /* Try a big heap, and reduce the size if that fails.  */
374
                for (;;) {
375
                    ret = do_brk(limit);
376
                    if (ret != -1)
377
                        break;
378
                    limit = (ts->heap_base >> 1) + (limit >> 1);
379
                }
380
                ts->heap_limit = limit;
381
            }
382
              
383
            ptr = lock_user(ARG(0), 16, 0);
384
            ptr[0] = tswap32(ts->heap_base);
385
            ptr[1] = tswap32(ts->heap_limit);
386
            ptr[2] = tswap32(ts->stack_base);
387
            ptr[3] = tswap32(0); /* Stack limit.  */
388
            unlock_user(ptr, ARG(0), 16);
389
#else
390
            limit = ram_size;
391
            ptr = lock_user(ARG(0), 16, 0);
392
            /* TODO: Make this use the limit of the loaded application.  */
393
            ptr[0] = tswap32(limit / 2);
394
            ptr[1] = tswap32(limit);
395
            ptr[2] = tswap32(limit); /* Stack base */
396
            ptr[3] = tswap32(0); /* Stack limit.  */
397
            unlock_user(ptr, ARG(0), 16);
398
#endif
399
            return 0;
400
        }
401
    case SYS_EXIT:
402
        exit(0);
403
    default:
404
        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
405
        cpu_dump_state(env, stderr, fprintf, 0);
406
        abort();
407
    }
408
}