~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

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

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

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 "qemu/osdep.h"
 
22
 
 
23
#include "cpu.h"
 
24
#include "exec/semihost.h"
 
25
#ifdef CONFIG_USER_ONLY
 
26
#include "qemu.h"
 
27
 
 
28
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
 
29
#else
 
30
#include "qemu-common.h"
 
31
#include "exec/gdbstub.h"
 
32
#include "hw/arm/arm.h"
 
33
#include "qemu/cutils.h"
 
34
#endif
 
35
 
 
36
#define TARGET_SYS_OPEN        0x01
 
37
#define TARGET_SYS_CLOSE       0x02
 
38
#define TARGET_SYS_WRITEC      0x03
 
39
#define TARGET_SYS_WRITE0      0x04
 
40
#define TARGET_SYS_WRITE       0x05
 
41
#define TARGET_SYS_READ        0x06
 
42
#define TARGET_SYS_READC       0x07
 
43
#define TARGET_SYS_ISTTY       0x09
 
44
#define TARGET_SYS_SEEK        0x0a
 
45
#define TARGET_SYS_FLEN        0x0c
 
46
#define TARGET_SYS_TMPNAM      0x0d
 
47
#define TARGET_SYS_REMOVE      0x0e
 
48
#define TARGET_SYS_RENAME      0x0f
 
49
#define TARGET_SYS_CLOCK       0x10
 
50
#define TARGET_SYS_TIME        0x11
 
51
#define TARGET_SYS_SYSTEM      0x12
 
52
#define TARGET_SYS_ERRNO       0x13
 
53
#define TARGET_SYS_GET_CMDLINE 0x15
 
54
#define TARGET_SYS_HEAPINFO    0x16
 
55
#define TARGET_SYS_EXIT        0x18
 
56
#define TARGET_SYS_SYNCCACHE   0x19
 
57
 
 
58
/* ADP_Stopped_ApplicationExit is used for exit(0),
 
59
 * anything else is implemented as exit(1) */
 
60
#define ADP_Stopped_ApplicationExit     (0x20026)
 
61
 
 
62
#ifndef O_BINARY
 
63
#define O_BINARY 0
 
64
#endif
 
65
 
 
66
#define GDB_O_RDONLY  0x000
 
67
#define GDB_O_WRONLY  0x001
 
68
#define GDB_O_RDWR    0x002
 
69
#define GDB_O_APPEND  0x008
 
70
#define GDB_O_CREAT   0x200
 
71
#define GDB_O_TRUNC   0x400
 
72
#define GDB_O_BINARY  0
 
73
 
 
74
static int gdb_open_modeflags[12] = {
 
75
    GDB_O_RDONLY,
 
76
    GDB_O_RDONLY | GDB_O_BINARY,
 
77
    GDB_O_RDWR,
 
78
    GDB_O_RDWR | GDB_O_BINARY,
 
79
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
 
80
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
 
81
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
 
82
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
 
83
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
 
84
    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
 
85
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
 
86
    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
 
87
};
 
88
 
 
89
static int open_modeflags[12] = {
 
90
    O_RDONLY,
 
91
    O_RDONLY | O_BINARY,
 
92
    O_RDWR,
 
93
    O_RDWR | O_BINARY,
 
94
    O_WRONLY | O_CREAT | O_TRUNC,
 
95
    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
 
96
    O_RDWR | O_CREAT | O_TRUNC,
 
97
    O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
 
98
    O_WRONLY | O_CREAT | O_APPEND,
 
99
    O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
 
100
    O_RDWR | O_CREAT | O_APPEND,
 
101
    O_RDWR | O_CREAT | O_APPEND | O_BINARY
 
102
};
 
103
 
 
104
#ifdef CONFIG_USER_ONLY
 
105
static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
 
106
{
 
107
    if (code == (uint32_t)-1)
 
108
        ts->swi_errno = errno;
 
109
    return code;
 
110
}
 
111
#else
 
112
static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
 
113
{
 
114
    return code;
 
115
}
 
116
 
 
117
#include "exec/softmmu-semi.h"
 
118
#endif
 
119
 
 
120
static target_ulong arm_semi_syscall_len;
 
121
 
 
122
#if !defined(CONFIG_USER_ONLY)
 
123
static target_ulong syscall_err;
 
124
#endif
 
125
 
 
126
static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err)
 
127
{
 
128
    ARMCPU *cpu = ARM_CPU(cs);
 
129
    CPUARMState *env = &cpu->env;
 
130
#ifdef CONFIG_USER_ONLY
 
131
    TaskState *ts = cs->opaque;
 
132
#endif
 
133
    target_ulong reg0 = is_a64(env) ? env->xregs[0] : env->regs[0];
 
134
 
 
135
    if (ret == (target_ulong)-1) {
 
136
#ifdef CONFIG_USER_ONLY
 
137
        ts->swi_errno = err;
 
138
#else
 
139
        syscall_err = err;
 
140
#endif
 
141
        reg0 = ret;
 
142
    } else {
 
143
        /* Fixup syscalls that use nonstardard return conventions.  */
 
144
        switch (reg0) {
 
145
        case TARGET_SYS_WRITE:
 
146
        case TARGET_SYS_READ:
 
147
            reg0 = arm_semi_syscall_len - ret;
 
148
            break;
 
149
        case TARGET_SYS_SEEK:
 
150
            reg0 = 0;
 
151
            break;
 
152
        default:
 
153
            reg0 = ret;
 
154
            break;
 
155
        }
 
156
    }
 
157
    if (is_a64(env)) {
 
158
        env->xregs[0] = reg0;
 
159
    } else {
 
160
        env->regs[0] = reg0;
 
161
    }
 
162
}
 
163
 
 
164
static target_ulong arm_flen_buf(ARMCPU *cpu)
 
165
{
 
166
    /* Return an address in target memory of 64 bytes where the remote
 
167
     * gdb should write its stat struct. (The format of this structure
 
168
     * is defined by GDB's remote protocol and is not target-specific.)
 
169
     * We put this on the guest's stack just below SP.
 
170
     */
 
171
    CPUARMState *env = &cpu->env;
 
172
    target_ulong sp;
 
173
 
 
174
    if (is_a64(env)) {
 
175
        sp = env->xregs[31];
 
176
    } else {
 
177
        sp = env->regs[13];
 
178
    }
 
179
 
 
180
    return sp - 64;
 
181
}
 
182
 
 
183
static void arm_semi_flen_cb(CPUState *cs, target_ulong ret, target_ulong err)
 
184
{
 
185
    ARMCPU *cpu = ARM_CPU(cs);
 
186
    CPUARMState *env = &cpu->env;
 
187
    /* The size is always stored in big-endian order, extract
 
188
       the value. We assume the size always fit in 32 bits.  */
 
189
    uint32_t size;
 
190
    cpu_memory_rw_debug(cs, arm_flen_buf(cpu) + 32, (uint8_t *)&size, 4, 0);
 
191
    size = be32_to_cpu(size);
 
192
    if (is_a64(env)) {
 
193
        env->xregs[0] = size;
 
194
    } else {
 
195
        env->regs[0] = size;
 
196
    }
 
197
#ifdef CONFIG_USER_ONLY
 
198
    ((TaskState *)cs->opaque)->swi_errno = err;
 
199
#else
 
200
    syscall_err = err;
 
201
#endif
 
202
}
 
203
 
 
204
static target_ulong arm_gdb_syscall(ARMCPU *cpu, gdb_syscall_complete_cb cb,
 
205
                                    const char *fmt, ...)
 
206
{
 
207
    va_list va;
 
208
    CPUARMState *env = &cpu->env;
 
209
 
 
210
    va_start(va, fmt);
 
211
    gdb_do_syscallv(cb, fmt, va);
 
212
    va_end(va);
 
213
 
 
214
    /* FIXME: we are implicitly relying on the syscall completing
 
215
     * before this point, which is not guaranteed. We should
 
216
     * put in an explicit synchronization between this and
 
217
     * the callback function.
 
218
     */
 
219
 
 
220
    return is_a64(env) ? env->xregs[0] : env->regs[0];
 
221
}
 
222
 
 
223
/* Read the input value from the argument block; fail the semihosting
 
224
 * call if the memory read fails.
 
225
 */
 
226
#define GET_ARG(n) do {                                 \
 
227
    if (is_a64(env)) {                                  \
 
228
        if (get_user_u64(arg ## n, args + (n) * 8)) {   \
 
229
            return -1;                                  \
 
230
        }                                               \
 
231
    } else {                                            \
 
232
        if (get_user_u32(arg ## n, args + (n) * 4)) {   \
 
233
            return -1;                                  \
 
234
        }                                               \
 
235
    }                                                   \
 
236
} while (0)
 
237
 
 
238
#define SET_ARG(n, val)                                 \
 
239
    (is_a64(env) ?                                      \
 
240
     put_user_u64(val, args + (n) * 8) :                \
 
241
     put_user_u32(val, args + (n) * 4))
 
242
 
 
243
target_ulong do_arm_semihosting(CPUARMState *env)
 
244
{
 
245
    ARMCPU *cpu = arm_env_get_cpu(env);
 
246
    CPUState *cs = CPU(cpu);
 
247
    target_ulong args;
 
248
    target_ulong arg0, arg1, arg2, arg3;
 
249
    char * s;
 
250
    int nr;
 
251
    uint32_t ret;
 
252
    uint32_t len;
 
253
#ifdef CONFIG_USER_ONLY
 
254
    TaskState *ts = cs->opaque;
 
255
#else
 
256
    CPUARMState *ts = env;
 
257
#endif
 
258
 
 
259
    if (is_a64(env)) {
 
260
        /* Note that the syscall number is in W0, not X0 */
 
261
        nr = env->xregs[0] & 0xffffffffU;
 
262
        args = env->xregs[1];
 
263
    } else {
 
264
        nr = env->regs[0];
 
265
        args = env->regs[1];
 
266
    }
 
267
 
 
268
    switch (nr) {
 
269
    case TARGET_SYS_OPEN:
 
270
        GET_ARG(0);
 
271
        GET_ARG(1);
 
272
        GET_ARG(2);
 
273
        s = lock_user_string(arg0);
 
274
        if (!s) {
 
275
            /* FIXME - should this error code be -TARGET_EFAULT ? */
 
276
            return (uint32_t)-1;
 
277
        }
 
278
        if (arg1 >= 12) {
 
279
            unlock_user(s, arg0, 0);
 
280
            return (uint32_t)-1;
 
281
        }
 
282
        if (strcmp(s, ":tt") == 0) {
 
283
            int result_fileno = arg1 < 4 ? STDIN_FILENO : STDOUT_FILENO;
 
284
            unlock_user(s, arg0, 0);
 
285
            return result_fileno;
 
286
        }
 
287
        if (use_gdb_syscalls()) {
 
288
            ret = arm_gdb_syscall(cpu, arm_semi_cb, "open,%s,%x,1a4", arg0,
 
289
                                  (int)arg2+1, gdb_open_modeflags[arg1]);
 
290
        } else {
 
291
            ret = set_swi_errno(ts, open(s, open_modeflags[arg1], 0644));
 
292
        }
 
293
        unlock_user(s, arg0, 0);
 
294
        return ret;
 
295
    case TARGET_SYS_CLOSE:
 
296
        GET_ARG(0);
 
297
        if (use_gdb_syscalls()) {
 
298
            return arm_gdb_syscall(cpu, arm_semi_cb, "close,%x", arg0);
 
299
        } else {
 
300
            return set_swi_errno(ts, close(arg0));
 
301
        }
 
302
    case TARGET_SYS_WRITEC:
 
303
        {
 
304
          char c;
 
305
 
 
306
          if (get_user_u8(c, args))
 
307
              /* FIXME - should this error code be -TARGET_EFAULT ? */
 
308
              return (uint32_t)-1;
 
309
          /* Write to debug console.  stderr is near enough.  */
 
310
          if (use_gdb_syscalls()) {
 
311
                return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,1", args);
 
312
          } else {
 
313
                return write(STDERR_FILENO, &c, 1);
 
314
          }
 
315
        }
 
316
    case TARGET_SYS_WRITE0:
 
317
        if (!(s = lock_user_string(args)))
 
318
            /* FIXME - should this error code be -TARGET_EFAULT ? */
 
319
            return (uint32_t)-1;
 
320
        len = strlen(s);
 
321
        if (use_gdb_syscalls()) {
 
322
            return arm_gdb_syscall(cpu, arm_semi_cb, "write,2,%x,%x",
 
323
                                   args, len);
 
324
        } else {
 
325
            ret = write(STDERR_FILENO, s, len);
 
326
        }
 
327
        unlock_user(s, args, 0);
 
328
        return ret;
 
329
    case TARGET_SYS_WRITE:
 
330
        GET_ARG(0);
 
331
        GET_ARG(1);
 
332
        GET_ARG(2);
 
333
        len = arg2;
 
334
        if (use_gdb_syscalls()) {
 
335
            arm_semi_syscall_len = len;
 
336
            return arm_gdb_syscall(cpu, arm_semi_cb, "write,%x,%x,%x",
 
337
                                   arg0, arg1, len);
 
338
        } else {
 
339
            s = lock_user(VERIFY_READ, arg1, len, 1);
 
340
            if (!s) {
 
341
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
342
                return (uint32_t)-1;
 
343
            }
 
344
            ret = set_swi_errno(ts, write(arg0, s, len));
 
345
            unlock_user(s, arg1, 0);
 
346
            if (ret == (uint32_t)-1)
 
347
                return -1;
 
348
            return len - ret;
 
349
        }
 
350
    case TARGET_SYS_READ:
 
351
        GET_ARG(0);
 
352
        GET_ARG(1);
 
353
        GET_ARG(2);
 
354
        len = arg2;
 
355
        if (use_gdb_syscalls()) {
 
356
            arm_semi_syscall_len = len;
 
357
            return arm_gdb_syscall(cpu, arm_semi_cb, "read,%x,%x,%x",
 
358
                                   arg0, arg1, len);
 
359
        } else {
 
360
            s = lock_user(VERIFY_WRITE, arg1, len, 0);
 
361
            if (!s) {
 
362
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
363
                return (uint32_t)-1;
 
364
            }
 
365
            do {
 
366
                ret = set_swi_errno(ts, read(arg0, s, len));
 
367
            } while (ret == -1 && errno == EINTR);
 
368
            unlock_user(s, arg1, len);
 
369
            if (ret == (uint32_t)-1)
 
370
                return -1;
 
371
            return len - ret;
 
372
        }
 
373
    case TARGET_SYS_READC:
 
374
       /* XXX: Read from debug console. Not implemented.  */
 
375
        return 0;
 
376
    case TARGET_SYS_ISTTY:
 
377
        GET_ARG(0);
 
378
        if (use_gdb_syscalls()) {
 
379
            return arm_gdb_syscall(cpu, arm_semi_cb, "isatty,%x", arg0);
 
380
        } else {
 
381
            return isatty(arg0);
 
382
        }
 
383
    case TARGET_SYS_SEEK:
 
384
        GET_ARG(0);
 
385
        GET_ARG(1);
 
386
        if (use_gdb_syscalls()) {
 
387
            return arm_gdb_syscall(cpu, arm_semi_cb, "lseek,%x,%x,0",
 
388
                                   arg0, arg1);
 
389
        } else {
 
390
            ret = set_swi_errno(ts, lseek(arg0, arg1, SEEK_SET));
 
391
            if (ret == (uint32_t)-1)
 
392
              return -1;
 
393
            return 0;
 
394
        }
 
395
    case TARGET_SYS_FLEN:
 
396
        GET_ARG(0);
 
397
        if (use_gdb_syscalls()) {
 
398
            return arm_gdb_syscall(cpu, arm_semi_flen_cb, "fstat,%x,%x",
 
399
                                   arg0, arm_flen_buf(cpu));
 
400
        } else {
 
401
            struct stat buf;
 
402
            ret = set_swi_errno(ts, fstat(arg0, &buf));
 
403
            if (ret == (uint32_t)-1)
 
404
                return -1;
 
405
            return buf.st_size;
 
406
        }
 
407
    case TARGET_SYS_TMPNAM:
 
408
        /* XXX: Not implemented.  */
 
409
        return -1;
 
410
    case TARGET_SYS_REMOVE:
 
411
        GET_ARG(0);
 
412
        GET_ARG(1);
 
413
        if (use_gdb_syscalls()) {
 
414
            ret = arm_gdb_syscall(cpu, arm_semi_cb, "unlink,%s",
 
415
                                  arg0, (int)arg1+1);
 
416
        } else {
 
417
            s = lock_user_string(arg0);
 
418
            if (!s) {
 
419
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
420
                return (uint32_t)-1;
 
421
            }
 
422
            ret =  set_swi_errno(ts, remove(s));
 
423
            unlock_user(s, arg0, 0);
 
424
        }
 
425
        return ret;
 
426
    case TARGET_SYS_RENAME:
 
427
        GET_ARG(0);
 
428
        GET_ARG(1);
 
429
        GET_ARG(2);
 
430
        GET_ARG(3);
 
431
        if (use_gdb_syscalls()) {
 
432
            return arm_gdb_syscall(cpu, arm_semi_cb, "rename,%s,%s",
 
433
                                   arg0, (int)arg1+1, arg2, (int)arg3+1);
 
434
        } else {
 
435
            char *s2;
 
436
            s = lock_user_string(arg0);
 
437
            s2 = lock_user_string(arg2);
 
438
            if (!s || !s2)
 
439
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
440
                ret = (uint32_t)-1;
 
441
            else
 
442
                ret = set_swi_errno(ts, rename(s, s2));
 
443
            if (s2)
 
444
                unlock_user(s2, arg2, 0);
 
445
            if (s)
 
446
                unlock_user(s, arg0, 0);
 
447
            return ret;
 
448
        }
 
449
    case TARGET_SYS_CLOCK:
 
450
        return clock() / (CLOCKS_PER_SEC / 100);
 
451
    case TARGET_SYS_TIME:
 
452
        return set_swi_errno(ts, time(NULL));
 
453
    case TARGET_SYS_SYSTEM:
 
454
        GET_ARG(0);
 
455
        GET_ARG(1);
 
456
        if (use_gdb_syscalls()) {
 
457
            return arm_gdb_syscall(cpu, arm_semi_cb, "system,%s",
 
458
                                   arg0, (int)arg1+1);
 
459
        } else {
 
460
            s = lock_user_string(arg0);
 
461
            if (!s) {
 
462
                /* FIXME - should this error code be -TARGET_EFAULT ? */
 
463
                return (uint32_t)-1;
 
464
            }
 
465
            ret = set_swi_errno(ts, system(s));
 
466
            unlock_user(s, arg0, 0);
 
467
            return ret;
 
468
        }
 
469
    case TARGET_SYS_ERRNO:
 
470
#ifdef CONFIG_USER_ONLY
 
471
        return ts->swi_errno;
 
472
#else
 
473
        return syscall_err;
 
474
#endif
 
475
    case TARGET_SYS_GET_CMDLINE:
 
476
        {
 
477
            /* Build a command-line from the original argv.
 
478
             *
 
479
             * The inputs are:
 
480
             *     * arg0, pointer to a buffer of at least the size
 
481
             *               specified in arg1.
 
482
             *     * arg1, size of the buffer pointed to by arg0 in
 
483
             *               bytes.
 
484
             *
 
485
             * The outputs are:
 
486
             *     * arg0, pointer to null-terminated string of the
 
487
             *               command line.
 
488
             *     * arg1, length of the string pointed to by arg0.
 
489
             */
 
490
 
 
491
            char *output_buffer;
 
492
            size_t input_size;
 
493
            size_t output_size;
 
494
            int status = 0;
 
495
#if !defined(CONFIG_USER_ONLY)
 
496
            const char *cmdline;
 
497
#endif
 
498
            GET_ARG(0);
 
499
            GET_ARG(1);
 
500
            input_size = arg1;
 
501
            /* Compute the size of the output string.  */
 
502
#if !defined(CONFIG_USER_ONLY)
 
503
            cmdline = semihosting_get_cmdline();
 
504
            if (cmdline == NULL) {
 
505
                cmdline = ""; /* Default to an empty line. */
 
506
            }
 
507
            output_size = strlen(cmdline) + 1; /* Count terminating 0. */
 
508
#else
 
509
            unsigned int i;
 
510
 
 
511
            output_size = ts->info->arg_end - ts->info->arg_start;
 
512
            if (!output_size) {
 
513
                /* We special-case the "empty command line" case (argc==0).
 
514
                   Just provide the terminating 0. */
 
515
                output_size = 1;
 
516
            }
 
517
#endif
 
518
 
 
519
            if (output_size > input_size) {
 
520
                 /* Not enough space to store command-line arguments.  */
 
521
                return -1;
 
522
            }
 
523
 
 
524
            /* Adjust the command-line length.  */
 
525
            if (SET_ARG(1, output_size - 1)) {
 
526
                /* Couldn't write back to argument block */
 
527
                return -1;
 
528
            }
 
529
 
 
530
            /* Lock the buffer on the ARM side.  */
 
531
            output_buffer = lock_user(VERIFY_WRITE, arg0, output_size, 0);
 
532
            if (!output_buffer) {
 
533
                return -1;
 
534
            }
 
535
 
 
536
            /* Copy the command-line arguments.  */
 
537
#if !defined(CONFIG_USER_ONLY)
 
538
            pstrcpy(output_buffer, output_size, cmdline);
 
539
#else
 
540
            if (output_size == 1) {
 
541
                /* Empty command-line.  */
 
542
                output_buffer[0] = '\0';
 
543
                goto out;
 
544
            }
 
545
 
 
546
            if (copy_from_user(output_buffer, ts->info->arg_start,
 
547
                               output_size)) {
 
548
                status = -1;
 
549
                goto out;
 
550
            }
 
551
 
 
552
            /* Separate arguments by white spaces.  */
 
553
            for (i = 0; i < output_size - 1; i++) {
 
554
                if (output_buffer[i] == 0) {
 
555
                    output_buffer[i] = ' ';
 
556
                }
 
557
            }
 
558
        out:
 
559
#endif
 
560
            /* Unlock the buffer on the ARM side.  */
 
561
            unlock_user(output_buffer, arg0, output_size);
 
562
 
 
563
            return status;
 
564
        }
 
565
    case TARGET_SYS_HEAPINFO:
 
566
        {
 
567
            target_ulong retvals[4];
 
568
            target_ulong limit;
 
569
            int i;
 
570
 
 
571
            GET_ARG(0);
 
572
 
 
573
#ifdef CONFIG_USER_ONLY
 
574
            /* Some C libraries assume the heap immediately follows .bss, so
 
575
               allocate it using sbrk.  */
 
576
            if (!ts->heap_limit) {
 
577
                abi_ulong ret;
 
578
 
 
579
                ts->heap_base = do_brk(0);
 
580
                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
 
581
                /* Try a big heap, and reduce the size if that fails.  */
 
582
                for (;;) {
 
583
                    ret = do_brk(limit);
 
584
                    if (ret >= limit) {
 
585
                        break;
 
586
                    }
 
587
                    limit = (ts->heap_base >> 1) + (limit >> 1);
 
588
                }
 
589
                ts->heap_limit = limit;
 
590
            }
 
591
 
 
592
            retvals[0] = ts->heap_base;
 
593
            retvals[1] = ts->heap_limit;
 
594
            retvals[2] = ts->stack_base;
 
595
            retvals[3] = 0; /* Stack limit.  */
 
596
#else
 
597
            limit = ram_size;
 
598
            /* TODO: Make this use the limit of the loaded application.  */
 
599
            retvals[0] = limit / 2;
 
600
            retvals[1] = limit;
 
601
            retvals[2] = limit; /* Stack base */
 
602
            retvals[3] = 0; /* Stack limit.  */
 
603
#endif
 
604
 
 
605
            for (i = 0; i < ARRAY_SIZE(retvals); i++) {
 
606
                bool fail;
 
607
 
 
608
                if (is_a64(env)) {
 
609
                    fail = put_user_u64(retvals[i], arg0 + i * 8);
 
610
                } else {
 
611
                    fail = put_user_u32(retvals[i], arg0 + i * 4);
 
612
                }
 
613
 
 
614
                if (fail) {
 
615
                    /* Couldn't write back to argument block */
 
616
                    return -1;
 
617
                }
 
618
            }
 
619
            return 0;
 
620
        }
 
621
    case TARGET_SYS_EXIT:
 
622
        if (is_a64(env)) {
 
623
            /* The A64 version of this call takes a parameter block,
 
624
             * so the application-exit type can return a subcode which
 
625
             * is the exit status code from the application.
 
626
             */
 
627
            GET_ARG(0);
 
628
            GET_ARG(1);
 
629
 
 
630
            if (arg0 == ADP_Stopped_ApplicationExit) {
 
631
                ret = arg1;
 
632
            } else {
 
633
                ret = 1;
 
634
            }
 
635
        } else {
 
636
            /* ARM specifies only Stopped_ApplicationExit as normal
 
637
             * exit, everything else is considered an error */
 
638
            ret = (args == ADP_Stopped_ApplicationExit) ? 0 : 1;
 
639
        }
 
640
        gdb_exit(env, ret);
 
641
        exit(ret);
 
642
    case TARGET_SYS_SYNCCACHE:
 
643
        /* Clean the D-cache and invalidate the I-cache for the specified
 
644
         * virtual address range. This is a nop for us since we don't
 
645
         * implement caches. This is only present on A64.
 
646
         */
 
647
        if (is_a64(env)) {
 
648
            return 0;
 
649
        }
 
650
        /* fall through -- invalid for A32/T32 */
 
651
    default:
 
652
        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
 
653
        cpu_dump_state(cs, stderr, fprintf, 0);
 
654
        abort();
 
655
    }
 
656
}