Statistics
| Branch: | Revision:

root / arm-semi.c @ a08784dd

History | View | Annotate | Download (15.1 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, see <http://www.gnu.org/licenses/>.
19
 */
20

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

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

    
33
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
34
#else
35
#include "qemu-common.h"
36
#include "gdbstub.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
#if !defined(CONFIG_USER_ONLY)
121
static target_ulong syscall_err;
122
#endif
123

    
124
static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
125
{
126
#ifdef CONFIG_USER_ONLY
127
    TaskState *ts = env->opaque;
128
#endif
129

    
130
    if (ret == (target_ulong)-1) {
131
#ifdef CONFIG_USER_ONLY
132
        ts->swi_errno = err;
133
#else
134
        syscall_err = err;
135
#endif
136
        env->regs[0] = ret;
137
    } else {
138
        /* Fixup syscalls that use nonstardard return conventions.  */
139
        switch (env->regs[0]) {
140
        case SYS_WRITE:
141
        case SYS_READ:
142
            env->regs[0] = arm_semi_syscall_len - ret;
143
            break;
144
        case SYS_SEEK:
145
            env->regs[0] = 0;
146
            break;
147
        default:
148
            env->regs[0] = ret;
149
            break;
150
        }
151
    }
152
}
153

    
154
static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
155
{
156
    /* The size is always stored in big-endian order, extract
157
       the value. We assume the size always fit in 32 bits.  */
158
    uint32_t size;
159
    cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
160
    env->regs[0] = be32_to_cpu(size);
161
#ifdef CONFIG_USER_ONLY
162
    ((TaskState *)env->opaque)->swi_errno = err;
163
#else
164
    syscall_err = err;
165
#endif
166
}
167

    
168
#define ARG(n)                                        \
169
({                                                \
170
    target_ulong __arg;                                \
171
    /* FIXME - handle get_user() failure */        \
172
    get_user_ual(__arg, args + (n) * 4);        \
173
    __arg;                                        \
174
})
175
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
176
uint32_t do_arm_semihosting(CPUState *env)
177
{
178
    target_ulong args;
179
    char * s;
180
    int nr;
181
    uint32_t ret;
182
    uint32_t len;
183
#ifdef CONFIG_USER_ONLY
184
    TaskState *ts = env->opaque;
185
#else
186
    CPUState *ts = env;
187
#endif
188

    
189
    nr = env->regs[0];
190
    args = env->regs[1];
191
    switch (nr) {
192
    case SYS_OPEN:
193
        if (!(s = lock_user_string(ARG(0))))
194
            /* FIXME - should this error code be -TARGET_EFAULT ? */
195
            return (uint32_t)-1;
196
        if (ARG(1) >= 12)
197
            return (uint32_t)-1;
198
        if (strcmp(s, ":tt") == 0) {
199
            if (ARG(1) < 4)
200
                return STDIN_FILENO;
201
            else
202
                return STDOUT_FILENO;
203
        }
204
        if (use_gdb_syscalls()) {
205
            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
206
                           (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
207
            return env->regs[0];
208
        } else {
209
            ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
210
        }
211
        unlock_user(s, ARG(0), 0);
212
        return ret;
213
    case SYS_CLOSE:
214
        if (use_gdb_syscalls()) {
215
            gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
216
            return env->regs[0];
217
        } else {
218
            return set_swi_errno(ts, close(ARG(0)));
219
        }
220
    case SYS_WRITEC:
221
        {
222
          char c;
223

    
224
          if (get_user_u8(c, args))
225
              /* FIXME - should this error code be -TARGET_EFAULT ? */
226
              return (uint32_t)-1;
227
          /* Write to debug console.  stderr is near enough.  */
228
          if (use_gdb_syscalls()) {
229
                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
230
                return env->regs[0];
231
          } else {
232
                return write(STDERR_FILENO, &c, 1);
233
          }
234
        }
235
    case SYS_WRITE0:
236
        if (!(s = lock_user_string(args)))
237
            /* FIXME - should this error code be -TARGET_EFAULT ? */
238
            return (uint32_t)-1;
239
        len = strlen(s);
240
        if (use_gdb_syscalls()) {
241
            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
242
            ret = env->regs[0];
243
        } else {
244
            ret = write(STDERR_FILENO, s, len);
245
        }
246
        unlock_user(s, args, 0);
247
        return ret;
248
    case SYS_WRITE:
249
        len = ARG(2);
250
        if (use_gdb_syscalls()) {
251
            arm_semi_syscall_len = len;
252
            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
253
            return env->regs[0];
254
        } else {
255
            if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
256
                /* FIXME - should this error code be -TARGET_EFAULT ? */
257
                return (uint32_t)-1;
258
            ret = set_swi_errno(ts, write(ARG(0), s, len));
259
            unlock_user(s, ARG(1), 0);
260
            if (ret == (uint32_t)-1)
261
                return -1;
262
            return len - ret;
263
        }
264
    case SYS_READ:
265
        len = ARG(2);
266
        if (use_gdb_syscalls()) {
267
            arm_semi_syscall_len = len;
268
            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
269
            return env->regs[0];
270
        } else {
271
            if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
272
                /* FIXME - should this error code be -TARGET_EFAULT ? */
273
                return (uint32_t)-1;
274
            do
275
              ret = set_swi_errno(ts, read(ARG(0), s, len));
276
            while (ret == -1 && errno == EINTR);
277
            unlock_user(s, ARG(1), len);
278
            if (ret == (uint32_t)-1)
279
                return -1;
280
            return len - ret;
281
        }
282
    case SYS_READC:
283
       /* XXX: Read from debug cosole. Not implemented.  */
284
        return 0;
285
    case SYS_ISTTY:
286
        if (use_gdb_syscalls()) {
287
            gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
288
            return env->regs[0];
289
        } else {
290
            return isatty(ARG(0));
291
        }
292
    case SYS_SEEK:
293
        if (use_gdb_syscalls()) {
294
            gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
295
            return env->regs[0];
296
        } else {
297
            ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
298
            if (ret == (uint32_t)-1)
299
              return -1;
300
            return 0;
301
        }
302
    case SYS_FLEN:
303
        if (use_gdb_syscalls()) {
304
            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
305
                           ARG(0), env->regs[13]-64);
306
            return env->regs[0];
307
        } else {
308
            struct stat buf;
309
            ret = set_swi_errno(ts, fstat(ARG(0), &buf));
310
            if (ret == (uint32_t)-1)
311
                return -1;
312
            return buf.st_size;
313
        }
314
    case SYS_TMPNAM:
315
        /* XXX: Not implemented.  */
316
        return -1;
317
    case SYS_REMOVE:
318
        if (use_gdb_syscalls()) {
319
            gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
320
            ret = env->regs[0];
321
        } else {
322
            if (!(s = lock_user_string(ARG(0))))
323
                /* FIXME - should this error code be -TARGET_EFAULT ? */
324
                return (uint32_t)-1;
325
            ret =  set_swi_errno(ts, remove(s));
326
            unlock_user(s, ARG(0), 0);
327
        }
328
        return ret;
329
    case SYS_RENAME:
330
        if (use_gdb_syscalls()) {
331
            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
332
                           ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
333
            return env->regs[0];
334
        } else {
335
            char *s2;
336
            s = lock_user_string(ARG(0));
337
            s2 = lock_user_string(ARG(2));
338
            if (!s || !s2)
339
                /* FIXME - should this error code be -TARGET_EFAULT ? */
340
                ret = (uint32_t)-1;
341
            else
342
                ret = set_swi_errno(ts, rename(s, s2));
343
            if (s2)
344
                unlock_user(s2, ARG(2), 0);
345
            if (s)
346
                unlock_user(s, ARG(0), 0);
347
            return ret;
348
        }
349
    case SYS_CLOCK:
350
        return clock() / (CLOCKS_PER_SEC / 100);
351
    case SYS_TIME:
352
        return set_swi_errno(ts, time(NULL));
353
    case SYS_SYSTEM:
354
        if (use_gdb_syscalls()) {
355
            gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
356
            return env->regs[0];
357
        } else {
358
            if (!(s = lock_user_string(ARG(0))))
359
                /* FIXME - should this error code be -TARGET_EFAULT ? */
360
                return (uint32_t)-1;
361
            ret = set_swi_errno(ts, system(s));
362
            unlock_user(s, ARG(0), 0);
363
            return ret;
364
        }
365
    case SYS_ERRNO:
366
#ifdef CONFIG_USER_ONLY
367
        return ts->swi_errno;
368
#else
369
        return syscall_err;
370
#endif
371
    case SYS_GET_CMDLINE:
372
#ifdef CONFIG_USER_ONLY
373
        /* Build a commandline from the original argv.  */
374
        {
375
            char *arm_cmdline_buffer;
376
            const char *host_cmdline_buffer;
377

    
378
            unsigned int i;
379
            unsigned int arm_cmdline_len = ARG(1);
380
            unsigned int host_cmdline_len =
381
                ts->info->arg_end-ts->info->arg_start;
382

    
383
            if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) {
384
                return -1; /* not enough space to store command line */
385
            }
386

    
387
            if (!host_cmdline_len) {
388
                /* We special-case the "empty command line" case (argc==0).
389
                   Just provide the terminating 0. */
390
                arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0);
391
                arm_cmdline_buffer[0] = 0;
392
                unlock_user(arm_cmdline_buffer, ARG(0), 1);
393

    
394
                /* Adjust the commandline length argument. */
395
                SET_ARG(1, 0);
396
                return 0;
397
            }
398

    
399
            /* lock the buffers on the ARM side */
400
            arm_cmdline_buffer =
401
                lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0);
402
            host_cmdline_buffer =
403
                lock_user(VERIFY_READ, ts->info->arg_start,
404
                                       host_cmdline_len, 1);
405

    
406
            if (arm_cmdline_buffer && host_cmdline_buffer)
407
            {
408
                /* the last argument is zero-terminated;
409
                   no need for additional termination */
410
                memcpy(arm_cmdline_buffer, host_cmdline_buffer,
411
                       host_cmdline_len);
412

    
413
                /* separate arguments by white spaces */
414
                for (i = 0; i < host_cmdline_len-1; i++) {
415
                    if (arm_cmdline_buffer[i] == 0) {
416
                        arm_cmdline_buffer[i] = ' ';
417
                    }
418
                }
419

    
420
                /* Adjust the commandline length argument. */
421
                SET_ARG(1, host_cmdline_len-1);
422
            }
423

    
424
            /* Unlock the buffers on the ARM side.  */
425
            unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len);
426
            unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0);
427

    
428
            /* Return success if we could return a commandline.  */
429
            return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1;
430
        }
431
#else
432
        return -1;
433
#endif
434
    case SYS_HEAPINFO:
435
        {
436
            uint32_t *ptr;
437
            uint32_t limit;
438

    
439
#ifdef CONFIG_USER_ONLY
440
            /* Some C libraries assume the heap immediately follows .bss, so
441
               allocate it using sbrk.  */
442
            if (!ts->heap_limit) {
443
                long ret;
444

    
445
                ts->heap_base = do_brk(0);
446
                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
447
                /* Try a big heap, and reduce the size if that fails.  */
448
                for (;;) {
449
                    ret = do_brk(limit);
450
                    if (ret != -1)
451
                        break;
452
                    limit = (ts->heap_base >> 1) + (limit >> 1);
453
                }
454
                ts->heap_limit = limit;
455
            }
456

    
457
            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
458
                /* FIXME - should this error code be -TARGET_EFAULT ? */
459
                return (uint32_t)-1;
460
            ptr[0] = tswap32(ts->heap_base);
461
            ptr[1] = tswap32(ts->heap_limit);
462
            ptr[2] = tswap32(ts->stack_base);
463
            ptr[3] = tswap32(0); /* Stack limit.  */
464
            unlock_user(ptr, ARG(0), 16);
465
#else
466
            limit = ram_size;
467
            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
468
                /* FIXME - should this error code be -TARGET_EFAULT ? */
469
                return (uint32_t)-1;
470
            /* TODO: Make this use the limit of the loaded application.  */
471
            ptr[0] = tswap32(limit / 2);
472
            ptr[1] = tswap32(limit);
473
            ptr[2] = tswap32(limit); /* Stack base */
474
            ptr[3] = tswap32(0); /* Stack limit.  */
475
            unlock_user(ptr, ARG(0), 16);
476
#endif
477
            return 0;
478
        }
479
    case SYS_EXIT:
480
        gdb_exit(env, 0);
481
        exit(0);
482
    default:
483
        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
484
        cpu_dump_state(env, stderr, fprintf, 0);
485
        abort();
486
    }
487
}