~ubuntu-branches/ubuntu/vivid/qemu-linaro/vivid

« back to all changes in this revision

Viewing changes to target-arm/arm-semi.c

  • Committer: Ricardo Salveti de Araujo
  • Date: 2012-09-20 18:39:31 UTC
  • mfrom: (12922.1.2 qemu-linaro)
  • Revision ID: ricardo.salveti@linaro.org-20120920183931-sp3cg6kpdl8dmwo9
* New upstream release.
  - support emulated systems with more than 2G of memory. (LP: #1030588)
* Drop powerpc-missing-include.patch - merged upstream.
* Update debian/control:
  - drop perl build dependency.
  - add libfdt-dev build dependency.
* Update debian/qemu-keymaps.install file.
* Update debian/rules:
  - update QEMU_CPU for ARM architecture: armv4l -> armv7l.
  - update conf_audio_drv: default to PulseAudio since PA is the default on
    Ubuntu.
  - enable KVM on ARM architecture.
  - enable flat device tree support (--enable-fdt). (LP: #1030594)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#define ARG(n)                                  \
 
170
({                                              \
 
171
    target_ulong __arg;                         \
 
172
    /* FIXME - handle get_user() failure */     \
 
173
    get_user_ual(__arg, args + (n) * 4);        \
 
174
    __arg;                                      \
 
175
})
 
176
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
 
177
uint32_t do_arm_semihosting(CPUARMState *env)
 
178
{
 
179
    target_ulong args;
 
180
    char * s;
 
181
    int nr;
 
182
    uint32_t ret;
 
183
    uint32_t len;
 
184
#ifdef CONFIG_USER_ONLY
 
185
    TaskState *ts = env->opaque;
 
186
#else
 
187
    CPUARMState *ts = env;
 
188
#endif
 
189
 
 
190
    nr = env->regs[0];
 
191
    args = env->regs[1];
 
192
    switch (nr) {
 
193
    case TARGET_SYS_OPEN:
 
194
        if (!(s = lock_user_string(ARG(0))))
 
195
            /* FIXME - should this error code be -TARGET_EFAULT ? */
 
196
            return (uint32_t)-1;
 
197
        if (ARG(1) >= 12)
 
198
            return (uint32_t)-1;
 
199
        if (strcmp(s, ":tt") == 0) {
 
200
            if (ARG(1) < 4)
 
201
                return STDIN_FILENO;
 
202
            else
 
203
                return STDOUT_FILENO;
 
204
        }
 
205
        if (use_gdb_syscalls()) {
 
206
            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
 
207
                           (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
 
208
            return env->regs[0];
 
209
        } else {
 
210
            ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
 
211
        }
 
212
        unlock_user(s, ARG(0), 0);
 
213
        return ret;
 
214
    case TARGET_SYS_CLOSE:
 
215
        if (use_gdb_syscalls()) {
 
216
            gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
 
217
            return env->regs[0];
 
218
        } else {
 
219
            return set_swi_errno(ts, close(ARG(0)));
 
220
        }
 
221
    case TARGET_SYS_WRITEC:
 
222
        {
 
223
          char c;
 
224
 
 
225
          if (get_user_u8(c, args))
 
226
              /* FIXME - should this error code be -TARGET_EFAULT ? */
 
227
              return (uint32_t)-1;
 
228
          /* Write to debug console.  stderr is near enough.  */
 
229
          if (use_gdb_syscalls()) {
 
230
                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
 
231
                return env->regs[0];
 
232
          } else {
 
233
                return write(STDERR_FILENO, &c, 1);
 
234
          }
 
235
        }
 
236
    case TARGET_SYS_WRITE0:
 
237
        if (!(s = lock_user_string(args)))
 
238
            /* FIXME - should this error code be -TARGET_EFAULT ? */
 
239
            return (uint32_t)-1;
 
240
        len = strlen(s);
 
241
        if (use_gdb_syscalls()) {
 
242
            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
 
243
            ret = env->regs[0];
 
244
        } else {
 
245
            ret = write(STDERR_FILENO, s, len);
 
246
        }
 
247
        unlock_user(s, args, 0);
 
248
        return ret;
 
249
    case TARGET_SYS_WRITE:
 
250
        len = ARG(2);
 
251
        if (use_gdb_syscalls()) {
 
252
            arm_semi_syscall_len = len;
 
253
            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
 
254
            return env->regs[0];
 
255
        } else {
 
256
            if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
 
257
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
258
                return (uint32_t)-1;
 
259
            ret = set_swi_errno(ts, write(ARG(0), s, len));
 
260
            unlock_user(s, ARG(1), 0);
 
261
            if (ret == (uint32_t)-1)
 
262
                return -1;
 
263
            return len - ret;
 
264
        }
 
265
    case TARGET_SYS_READ:
 
266
        len = ARG(2);
 
267
        if (use_gdb_syscalls()) {
 
268
            arm_semi_syscall_len = len;
 
269
            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
 
270
            return env->regs[0];
 
271
        } else {
 
272
            if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
 
273
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
274
                return (uint32_t)-1;
 
275
            do
 
276
              ret = set_swi_errno(ts, read(ARG(0), s, len));
 
277
            while (ret == -1 && errno == EINTR);
 
278
            unlock_user(s, ARG(1), len);
 
279
            if (ret == (uint32_t)-1)
 
280
                return -1;
 
281
            return len - ret;
 
282
        }
 
283
    case TARGET_SYS_READC:
 
284
       /* XXX: Read from debug cosole. Not implemented.  */
 
285
        return 0;
 
286
    case TARGET_SYS_ISTTY:
 
287
        if (use_gdb_syscalls()) {
 
288
            gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
 
289
            return env->regs[0];
 
290
        } else {
 
291
            return isatty(ARG(0));
 
292
        }
 
293
    case TARGET_SYS_SEEK:
 
294
        if (use_gdb_syscalls()) {
 
295
            gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
 
296
            return env->regs[0];
 
297
        } else {
 
298
            ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
 
299
            if (ret == (uint32_t)-1)
 
300
              return -1;
 
301
            return 0;
 
302
        }
 
303
    case TARGET_SYS_FLEN:
 
304
        if (use_gdb_syscalls()) {
 
305
            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
 
306
                           ARG(0), env->regs[13]-64);
 
307
            return env->regs[0];
 
308
        } else {
 
309
            struct stat buf;
 
310
            ret = set_swi_errno(ts, fstat(ARG(0), &buf));
 
311
            if (ret == (uint32_t)-1)
 
312
                return -1;
 
313
            return buf.st_size;
 
314
        }
 
315
    case TARGET_SYS_TMPNAM:
 
316
        /* XXX: Not implemented.  */
 
317
        return -1;
 
318
    case TARGET_SYS_REMOVE:
 
319
        if (use_gdb_syscalls()) {
 
320
            gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
 
321
            ret = env->regs[0];
 
322
        } else {
 
323
            if (!(s = lock_user_string(ARG(0))))
 
324
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
325
                return (uint32_t)-1;
 
326
            ret =  set_swi_errno(ts, remove(s));
 
327
            unlock_user(s, ARG(0), 0);
 
328
        }
 
329
        return ret;
 
330
    case TARGET_SYS_RENAME:
 
331
        if (use_gdb_syscalls()) {
 
332
            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
 
333
                           ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
 
334
            return env->regs[0];
 
335
        } else {
 
336
            char *s2;
 
337
            s = lock_user_string(ARG(0));
 
338
            s2 = lock_user_string(ARG(2));
 
339
            if (!s || !s2)
 
340
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
341
                ret = (uint32_t)-1;
 
342
            else
 
343
                ret = set_swi_errno(ts, rename(s, s2));
 
344
            if (s2)
 
345
                unlock_user(s2, ARG(2), 0);
 
346
            if (s)
 
347
                unlock_user(s, ARG(0), 0);
 
348
            return ret;
 
349
        }
 
350
    case TARGET_SYS_CLOCK:
 
351
        return clock() / (CLOCKS_PER_SEC / 100);
 
352
    case TARGET_SYS_TIME:
 
353
        return set_swi_errno(ts, time(NULL));
 
354
    case TARGET_SYS_SYSTEM:
 
355
        if (use_gdb_syscalls()) {
 
356
            gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
 
357
            return env->regs[0];
 
358
        } else {
 
359
            if (!(s = lock_user_string(ARG(0))))
 
360
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
361
                return (uint32_t)-1;
 
362
            ret = set_swi_errno(ts, system(s));
 
363
            unlock_user(s, ARG(0), 0);
 
364
            return ret;
 
365
        }
 
366
    case TARGET_SYS_ERRNO:
 
367
#ifdef CONFIG_USER_ONLY
 
368
        return ts->swi_errno;
 
369
#else
 
370
        return syscall_err;
 
371
#endif
 
372
    case TARGET_SYS_GET_CMDLINE:
 
373
        {
 
374
            /* Build a command-line from the original argv.
 
375
             *
 
376
             * The inputs are:
 
377
             *     * ARG(0), pointer to a buffer of at least the size
 
378
             *               specified in ARG(1).
 
379
             *     * ARG(1), size of the buffer pointed to by ARG(0) in
 
380
             *               bytes.
 
381
             *
 
382
             * The outputs are:
 
383
             *     * ARG(0), pointer to null-terminated string of the
 
384
             *               command line.
 
385
             *     * ARG(1), length of the string pointed to by ARG(0).
 
386
             */
 
387
 
 
388
            char *output_buffer;
 
389
            size_t input_size = ARG(1);
 
390
            size_t output_size;
 
391
            int status = 0;
 
392
 
 
393
            /* Compute the size of the output string.  */
 
394
#if !defined(CONFIG_USER_ONLY)
 
395
            output_size = strlen(ts->boot_info->kernel_filename)
 
396
                        + 1  /* Separating space.  */
 
397
                        + strlen(ts->boot_info->kernel_cmdline)
 
398
                        + 1; /* Terminating null byte.  */
 
399
#else
 
400
            unsigned int i;
 
401
 
 
402
            output_size = ts->info->arg_end - ts->info->arg_start;
 
403
            if (!output_size) {
 
404
                /* We special-case the "empty command line" case (argc==0).
 
405
                   Just provide the terminating 0. */
 
406
                output_size = 1;
 
407
            }
 
408
#endif
 
409
 
 
410
            if (output_size > input_size) {
 
411
                 /* Not enough space to store command-line arguments.  */
 
412
                return -1;
 
413
            }
 
414
 
 
415
            /* Adjust the command-line length.  */
 
416
            SET_ARG(1, output_size - 1);
 
417
 
 
418
            /* Lock the buffer on the ARM side.  */
 
419
            output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0);
 
420
            if (!output_buffer) {
 
421
                return -1;
 
422
            }
 
423
 
 
424
            /* Copy the command-line arguments.  */
 
425
#if !defined(CONFIG_USER_ONLY)
 
426
            pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
 
427
            pstrcat(output_buffer, output_size, " ");
 
428
            pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
 
429
#else
 
430
            if (output_size == 1) {
 
431
                /* Empty command-line.  */
 
432
                output_buffer[0] = '\0';
 
433
                goto out;
 
434
            }
 
435
 
 
436
            if (copy_from_user(output_buffer, ts->info->arg_start,
 
437
                               output_size)) {
 
438
                status = -1;
 
439
                goto out;
 
440
            }
 
441
 
 
442
            /* Separate arguments by white spaces.  */
 
443
            for (i = 0; i < output_size - 1; i++) {
 
444
                if (output_buffer[i] == 0) {
 
445
                    output_buffer[i] = ' ';
 
446
                }
 
447
            }
 
448
        out:
 
449
#endif
 
450
            /* Unlock the buffer on the ARM side.  */
 
451
            unlock_user(output_buffer, ARG(0), output_size);
 
452
 
 
453
            return status;
 
454
        }
 
455
    case TARGET_SYS_HEAPINFO:
 
456
        {
 
457
            uint32_t *ptr;
 
458
            uint32_t limit;
 
459
 
 
460
#ifdef CONFIG_USER_ONLY
 
461
            /* Some C libraries assume the heap immediately follows .bss, so
 
462
               allocate it using sbrk.  */
 
463
            if (!ts->heap_limit) {
 
464
                abi_ulong ret;
 
465
 
 
466
                ts->heap_base = do_brk(0);
 
467
                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
 
468
                /* Try a big heap, and reduce the size if that fails.  */
 
469
                for (;;) {
 
470
                    ret = do_brk(limit);
 
471
                    if (ret >= limit) {
 
472
                        break;
 
473
                    }
 
474
                    limit = (ts->heap_base >> 1) + (limit >> 1);
 
475
                }
 
476
                ts->heap_limit = limit;
 
477
            }
 
478
 
 
479
            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
 
480
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
481
                return (uint32_t)-1;
 
482
            ptr[0] = tswap32(ts->heap_base);
 
483
            ptr[1] = tswap32(ts->heap_limit);
 
484
            ptr[2] = tswap32(ts->stack_base);
 
485
            ptr[3] = tswap32(0); /* Stack limit.  */
 
486
            unlock_user(ptr, ARG(0), 16);
 
487
#else
 
488
            limit = ram_size;
 
489
            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
 
490
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
491
                return (uint32_t)-1;
 
492
            /* TODO: Make this use the limit of the loaded application.  */
 
493
            ptr[0] = tswap32(limit / 2);
 
494
            ptr[1] = tswap32(limit);
 
495
            ptr[2] = tswap32(limit); /* Stack base */
 
496
            ptr[3] = tswap32(0); /* Stack limit.  */
 
497
            unlock_user(ptr, ARG(0), 16);
 
498
#endif
 
499
            return 0;
 
500
        }
 
501
    case TARGET_SYS_EXIT:
 
502
        gdb_exit(env, 0);
 
503
        exit(0);
 
504
    default:
 
505
        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
 
506
        cpu_dump_state(env, stderr, fprintf, 0);
 
507
        abort();
 
508
    }
 
509
}