Statistics
| Branch: | Revision:

root / target-arm / arm-semi.c @ 31a32289

History | View | Annotate | Download (17 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
#include "hw/arm-misc.h"
38
#endif
39

    
40
#define TARGET_SYS_OPEN        0x01
41
#define TARGET_SYS_CLOSE       0x02
42
#define TARGET_SYS_WRITEC      0x03
43
#define TARGET_SYS_WRITE0      0x04
44
#define TARGET_SYS_WRITE       0x05
45
#define TARGET_SYS_READ        0x06
46
#define TARGET_SYS_READC       0x07
47
#define TARGET_SYS_ISTTY       0x09
48
#define TARGET_SYS_SEEK        0x0a
49
#define TARGET_SYS_FLEN        0x0c
50
#define TARGET_SYS_TMPNAM      0x0d
51
#define TARGET_SYS_REMOVE      0x0e
52
#define TARGET_SYS_RENAME      0x0f
53
#define TARGET_SYS_CLOCK       0x10
54
#define TARGET_SYS_TIME        0x11
55
#define TARGET_SYS_SYSTEM      0x12
56
#define TARGET_SYS_ERRNO       0x13
57
#define TARGET_SYS_GET_CMDLINE 0x15
58
#define TARGET_SYS_HEAPINFO    0x16
59
#define TARGET_SYS_EXIT        0x18
60

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

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

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

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

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

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

    
119
static target_ulong arm_semi_syscall_len;
120

    
121
#if !defined(CONFIG_USER_ONLY)
122
static target_ulong syscall_err;
123
#endif
124

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

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

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

    
169
/* Read the input value from the argument block; fail the semihosting
170
 * call if the memory read fails.
171
 */
172
#define GET_ARG(n) do {                                 \
173
    if (get_user_ual(arg ## n, args + (n) * 4)) {       \
174
        return (uint32_t)-1;                            \
175
    }                                                   \
176
} while (0)
177

    
178
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
179
uint32_t do_arm_semihosting(CPUARMState *env)
180
{
181
    target_ulong args;
182
    target_ulong arg0, arg1, arg2, arg3;
183
    char * s;
184
    int nr;
185
    uint32_t ret;
186
    uint32_t len;
187
#ifdef CONFIG_USER_ONLY
188
    TaskState *ts = env->opaque;
189
#else
190
    CPUARMState *ts = env;
191
#endif
192

    
193
    nr = env->regs[0];
194
    args = env->regs[1];
195
    switch (nr) {
196
    case TARGET_SYS_OPEN:
197
        GET_ARG(0);
198
        GET_ARG(1);
199
        GET_ARG(2);
200
        s = lock_user_string(arg0);
201
        if (!s) {
202
            /* FIXME - should this error code be -TARGET_EFAULT ? */
203
            return (uint32_t)-1;
204
        }
205
        if (arg1 >= 12) {
206
            unlock_user(s, arg0, 0);
207
            return (uint32_t)-1;
208
        }
209
        if (strcmp(s, ":tt") == 0) {
210
            int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
211
            unlock_user(s, arg0, 0);
212
            return result_fileno;
213
        }
214
        if (use_gdb_syscalls()) {
215
            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", arg0,
216
                           (int)arg2+1, gdb_open_modeflags[arg1]);
217
            ret = env->regs[0];
218
        } else {
219
            ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
220
        }
221
        unlock_user(s, arg0, 0);
222
        return ret;
223
    case TARGET_SYS_CLOSE:
224
        GET_ARG(0);
225
        if (use_gdb_syscalls()) {
226
            gdb_do_syscall(arm_semi_cb, "close,%x", arg0);
227
            return env->regs[0];
228
        } else {
229
            return set_swi_errno(ts, close(arg0));
230
        }
231
    case TARGET_SYS_WRITEC:
232
        {
233
          char c;
234

    
235
          if (get_user_u8(c, args))
236
              /* FIXME - should this error code be -TARGET_EFAULT ? */
237
              return (uint32_t)-1;
238
          /* Write to debug console.  stderr is near enough.  */
239
          if (use_gdb_syscalls()) {
240
                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
241
                return env->regs[0];
242
          } else {
243
                return write(STDERR_FILENO, &c, 1);
244
          }
245
        }
246
    case TARGET_SYS_WRITE0:
247
        if (!(s = lock_user_string(args)))
248
            /* FIXME - should this error code be -TARGET_EFAULT ? */
249
            return (uint32_t)-1;
250
        len = strlen(s);
251
        if (use_gdb_syscalls()) {
252
            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
253
            ret = env->regs[0];
254
        } else {
255
            ret = write(STDERR_FILENO, s, len);
256
        }
257
        unlock_user(s, args, 0);
258
        return ret;
259
    case TARGET_SYS_WRITE:
260
        GET_ARG(0);
261
        GET_ARG(1);
262
        GET_ARG(2);
263
        len = arg2;
264
        if (use_gdb_syscalls()) {
265
            arm_semi_syscall_len = len;
266
            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", arg0, arg1, len);
267
            return env->regs[0];
268
        } else {
269
            s = lock_user(VERIFY_READ, arg1, len, 1);
270
            if (!s) {
271
                /* FIXME - should this error code be -TARGET_EFAULT ? */
272
                return (uint32_t)-1;
273
            }
274
            ret = set_swi_errno(ts, write(arg0, s, len));
275
            unlock_user(s, arg1, 0);
276
            if (ret == (uint32_t)-1)
277
                return -1;
278
            return len - ret;
279
        }
280
    case TARGET_SYS_READ:
281
        GET_ARG(0);
282
        GET_ARG(1);
283
        GET_ARG(2);
284
        len = arg2;
285
        if (use_gdb_syscalls()) {
286
            arm_semi_syscall_len = len;
287
            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", arg0, arg1, len);
288
            return env->regs[0];
289
        } else {
290
            s = lock_user(VERIFY_WRITE, arg1, len, 0);
291
            if (!s) {
292
                /* FIXME - should this error code be -TARGET_EFAULT ? */
293
                return (uint32_t)-1;
294
            }
295
            do {
296
                ret = set_swi_errno(ts, read(arg0, s, len));
297
            } while (ret == -1 && errno == EINTR);
298
            unlock_user(s, arg1, len);
299
            if (ret == (uint32_t)-1)
300
                return -1;
301
            return len - ret;
302
        }
303
    case TARGET_SYS_READC:
304
       /* XXX: Read from debug console. Not implemented.  */
305
        return 0;
306
    case TARGET_SYS_ISTTY:
307
        GET_ARG(0);
308
        if (use_gdb_syscalls()) {
309
            gdb_do_syscall(arm_semi_cb, "isatty,%x", arg0);
310
            return env->regs[0];
311
        } else {
312
            return isatty(arg0);
313
        }
314
    case TARGET_SYS_SEEK:
315
        GET_ARG(0);
316
        GET_ARG(1);
317
        if (use_gdb_syscalls()) {
318
            gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", arg0, arg1);
319
            return env->regs[0];
320
        } else {
321
            ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
322
            if (ret == (uint32_t)-1)
323
              return -1;
324
            return 0;
325
        }
326
    case TARGET_SYS_FLEN:
327
        GET_ARG(0);
328
        if (use_gdb_syscalls()) {
329
            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
330
                           arg0, env->regs[13]-64);
331
            return env->regs[0];
332
        } else {
333
            struct stat buf;
334
            ret = set_swi_errno(ts, fstat(arg0, &buf));
335
            if (ret == (uint32_t)-1)
336
                return -1;
337
            return buf.st_size;
338
        }
339
    case TARGET_SYS_TMPNAM:
340
        /* XXX: Not implemented.  */
341
        return -1;
342
    case TARGET_SYS_REMOVE:
343
        GET_ARG(0);
344
        GET_ARG(1);
345
        if (use_gdb_syscalls()) {
346
            gdb_do_syscall(arm_semi_cb, "unlink,%s", arg0, (int)arg1+1);
347
            ret = env->regs[0];
348
        } else {
349
            s = lock_user_string(arg0);
350
            if (!s) {
351
                /* FIXME - should this error code be -TARGET_EFAULT ? */
352
                return (uint32_t)-1;
353
            }
354
            ret =  set_swi_errno(ts, remove(s));
355
            unlock_user(s, arg0, 0);
356
        }
357
        return ret;
358
    case TARGET_SYS_RENAME:
359
        GET_ARG(0);
360
        GET_ARG(1);
361
        GET_ARG(2);
362
        GET_ARG(3);
363
        if (use_gdb_syscalls()) {
364
            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
365
                           arg0, (int)arg1+1, arg2, (int)arg3+1);
366
            return env->regs[0];
367
        } else {
368
            char *s2;
369
            s = lock_user_string(arg0);
370
            s2 = lock_user_string(arg2);
371
            if (!s || !s2)
372
                /* FIXME - should this error code be -TARGET_EFAULT ? */
373
                ret = (uint32_t)-1;
374
            else
375
                ret = set_swi_errno(ts, rename(s, s2));
376
            if (s2)
377
                unlock_user(s2, arg2, 0);
378
            if (s)
379
                unlock_user(s, arg0, 0);
380
            return ret;
381
        }
382
    case TARGET_SYS_CLOCK:
383
        return clock() / (CLOCKS_PER_SEC / 100);
384
    case TARGET_SYS_TIME:
385
        return set_swi_errno(ts, time(NULL));
386
    case TARGET_SYS_SYSTEM:
387
        GET_ARG(0);
388
        GET_ARG(1);
389
        if (use_gdb_syscalls()) {
390
            gdb_do_syscall(arm_semi_cb, "system,%s", arg0, (int)arg1+1);
391
            return env->regs[0];
392
        } else {
393
            s = lock_user_string(arg0);
394
            if (!s) {
395
                /* FIXME - should this error code be -TARGET_EFAULT ? */
396
                return (uint32_t)-1;
397
            }
398
            ret = set_swi_errno(ts, system(s));
399
            unlock_user(s, arg0, 0);
400
            return ret;
401
        }
402
    case TARGET_SYS_ERRNO:
403
#ifdef CONFIG_USER_ONLY
404
        return ts->swi_errno;
405
#else
406
        return syscall_err;
407
#endif
408
    case TARGET_SYS_GET_CMDLINE:
409
        {
410
            /* Build a command-line from the original argv.
411
             *
412
             * The inputs are:
413
             *     * arg0, pointer to a buffer of at least the size
414
             *               specified in arg1.
415
             *     * arg1, size of the buffer pointed to by arg0 in
416
             *               bytes.
417
             *
418
             * The outputs are:
419
             *     * arg0, pointer to null-terminated string of the
420
             *               command line.
421
             *     * arg1, length of the string pointed to by arg0.
422
             */
423

    
424
            char *output_buffer;
425
            size_t input_size;
426
            size_t output_size;
427
            int status = 0;
428
            GET_ARG(0);
429
            GET_ARG(1);
430
            input_size = arg1;
431
            /* Compute the size of the output string.  */
432
#if !defined(CONFIG_USER_ONLY)
433
            output_size = strlen(ts->boot_info->kernel_filename)
434
                        + 1  /* Separating space.  */
435
                        + strlen(ts->boot_info->kernel_cmdline)
436
                        + 1; /* Terminating null byte.  */
437
#else
438
            unsigned int i;
439

    
440
            output_size = ts->info->arg_end - ts->info->arg_start;
441
            if (!output_size) {
442
                /* We special-case the "empty command line" case (argc==0).
443
                   Just provide the terminating 0. */
444
                output_size = 1;
445
            }
446
#endif
447

    
448
            if (output_size > input_size) {
449
                 /* Not enough space to store command-line arguments.  */
450
                return -1;
451
            }
452

    
453
            /* Adjust the command-line length.  */
454
            if (SET_ARG(1, output_size - 1)) {
455
                /* Couldn't write back to argument block */
456
                return -1;
457
            }
458

    
459
            /* Lock the buffer on the ARM side.  */
460
            output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
461
            if (!output_buffer) {
462
                return -1;
463
            }
464

    
465
            /* Copy the command-line arguments.  */
466
#if !defined(CONFIG_USER_ONLY)
467
            pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
468
            pstrcat(output_buffer, output_size, " ");
469
            pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
470
#else
471
            if (output_size == 1) {
472
                /* Empty command-line.  */
473
                output_buffer[0] = '\0';
474
                goto out;
475
            }
476

    
477
            if (copy_from_user(output_buffer, ts->info->arg_start,
478
                               output_size)) {
479
                status = -1;
480
                goto out;
481
            }
482

    
483
            /* Separate arguments by white spaces.  */
484
            for (i = 0; i < output_size - 1; i++) {
485
                if (output_buffer[i] == 0) {
486
                    output_buffer[i] = ' ';
487
                }
488
            }
489
        out:
490
#endif
491
            /* Unlock the buffer on the ARM side.  */
492
            unlock_user(output_buffer, arg0, output_size);
493

    
494
            return status;
495
        }
496
    case TARGET_SYS_HEAPINFO:
497
        {
498
            uint32_t *ptr;
499
            uint32_t limit;
500
            GET_ARG(0);
501

    
502
#ifdef CONFIG_USER_ONLY
503
            /* Some C libraries assume the heap immediately follows .bss, so
504
               allocate it using sbrk.  */
505
            if (!ts->heap_limit) {
506
                abi_ulong ret;
507

    
508
                ts->heap_base = do_brk(0);
509
                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
510
                /* Try a big heap, and reduce the size if that fails.  */
511
                for (;;) {
512
                    ret = do_brk(limit);
513
                    if (ret >= limit) {
514
                        break;
515
                    }
516
                    limit = (ts->heap_base >> 1) + (limit >> 1);
517
                }
518
                ts->heap_limit = limit;
519
            }
520

    
521
            ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
522
            if (!ptr) {
523
                /* FIXME - should this error code be -TARGET_EFAULT ? */
524
                return (uint32_t)-1;
525
            }
526
            ptr[0] = tswap32(ts->heap_base);
527
            ptr[1] = tswap32(ts->heap_limit);
528
            ptr[2] = tswap32(ts->stack_base);
529
            ptr[3] = tswap32(0); /* Stack limit.  */
530
            unlock_user(ptr, arg0, 16);
531
#else
532
            limit = ram_size;
533
            ptr = lock_user(VERIFY_WRITE, arg0, 16, 0);
534
            if (!ptr) {
535
                /* FIXME - should this error code be -TARGET_EFAULT ? */
536
                return (uint32_t)-1;
537
            }
538
            /* TODO: Make this use the limit of the loaded application.  */
539
            ptr[0] = tswap32(limit / 2);
540
            ptr[1] = tswap32(limit);
541
            ptr[2] = tswap32(limit); /* Stack base */
542
            ptr[3] = tswap32(0); /* Stack limit.  */
543
            unlock_user(ptr, arg0, 16);
544
#endif
545
            return 0;
546
        }
547
    case TARGET_SYS_EXIT:
548
        gdb_exit(env, 0);
549
        exit(0);
550
    default:
551
        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
552
        cpu_dump_state(env, stderr, fprintf, 0);
553
        abort();
554
    }
555
}