qemu/target-arm/arm-semi.c
<<
>>
Prefs
   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
  74static 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
  89static 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
 105static 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
 112static 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
 120static target_ulong arm_semi_syscall_len;
 121
 122#if !defined(CONFIG_USER_ONLY)
 123static target_ulong syscall_err;
 124#endif
 125
 126static 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
 164static 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
 183static 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
 204static 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
 243target_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}
 657