File:  [Qemu by Fabrice Bellard] / qemu / arm-semi.c
Revision 1.1.1.7 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:56:34 2018 UTC (3 years, 3 months ago) by root
Branches: qemu, MAIN
CVS tags: qemu1001, qemu1000, qemu0151, HEAD
qemu 0.15.1

    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 SYS_OPEN        0x01
   41: #define SYS_CLOSE       0x02
   42: #define SYS_WRITEC      0x03
   43: #define SYS_WRITE0      0x04
   44: #define SYS_WRITE       0x05
   45: #define SYS_READ        0x06
   46: #define SYS_READC       0x07
   47: #define SYS_ISTTY       0x09
   48: #define SYS_SEEK        0x0a
   49: #define SYS_FLEN        0x0c
   50: #define SYS_TMPNAM      0x0d
   51: #define SYS_REMOVE      0x0e
   52: #define SYS_RENAME      0x0f
   53: #define SYS_CLOCK       0x10
   54: #define SYS_TIME        0x11
   55: #define SYS_SYSTEM      0x12
   56: #define SYS_ERRNO       0x13
   57: #define SYS_GET_CMDLINE 0x15
   58: #define SYS_HEAPINFO    0x16
   59: #define 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(CPUState *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(CPUState *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 SYS_WRITE:
  142:         case SYS_READ:
  143:             env->regs[0] = arm_semi_syscall_len - ret;
  144:             break;
  145:         case 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(CPUState *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(CPUState *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:     CPUState *ts = env;
  188: #endif
  189: 
  190:     nr = env->regs[0];
  191:     args = env->regs[1];
  192:     switch (nr) {
  193:     case 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 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 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 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 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 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 SYS_READC:
  284:        /* XXX: Read from debug cosole. Not implemented.  */
  285:         return 0;
  286:     case 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 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 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 SYS_TMPNAM:
  316:         /* XXX: Not implemented.  */
  317:         return -1;
  318:     case 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 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 SYS_CLOCK:
  351:         return clock() / (CLOCKS_PER_SEC / 100);
  352:     case SYS_TIME:
  353:         return set_swi_errno(ts, time(NULL));
  354:     case 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 SYS_ERRNO:
  367: #ifdef CONFIG_USER_ONLY
  368:         return ts->swi_errno;
  369: #else
  370:         return syscall_err;
  371: #endif
  372:     case 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 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 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: }

unix.superglobalmegacorp.com