qemu/target/xtensa/xtensa-semi.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab.
   3 * All rights reserved.
   4 *
   5 * Redistribution and use in source and binary forms, with or without
   6 * modification, are permitted provided that the following conditions are met:
   7 *     * Redistributions of source code must retain the above copyright
   8 *       notice, this list of conditions and the following disclaimer.
   9 *     * Redistributions in binary form must reproduce the above copyright
  10 *       notice, this list of conditions and the following disclaimer in the
  11 *       documentation and/or other materials provided with the distribution.
  12 *     * Neither the name of the Open Source and Linux Lab nor the
  13 *       names of its contributors may be used to endorse or promote products
  14 *       derived from this software without specific prior written permission.
  15 *
  16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26 */
  27
  28#include "qemu/osdep.h"
  29#include "cpu.h"
  30#include "chardev/char-fe.h"
  31#include "exec/helper-proto.h"
  32#include "semihosting/semihost.h"
  33#include "qapi/error.h"
  34#include "qemu/log.h"
  35
  36enum {
  37    TARGET_SYS_exit = 1,
  38    TARGET_SYS_read = 3,
  39    TARGET_SYS_write = 4,
  40    TARGET_SYS_open = 5,
  41    TARGET_SYS_close = 6,
  42    TARGET_SYS_lseek = 19,
  43    TARGET_SYS_select_one = 29,
  44
  45    TARGET_SYS_argc = 1000,
  46    TARGET_SYS_argv_sz = 1001,
  47    TARGET_SYS_argv = 1002,
  48    TARGET_SYS_memset = 1004,
  49};
  50
  51enum {
  52    SELECT_ONE_READ   = 1,
  53    SELECT_ONE_WRITE  = 2,
  54    SELECT_ONE_EXCEPT = 3,
  55};
  56
  57enum {
  58    TARGET_EPERM        =  1,
  59    TARGET_ENOENT       =  2,
  60    TARGET_ESRCH        =  3,
  61    TARGET_EINTR        =  4,
  62    TARGET_EIO          =  5,
  63    TARGET_ENXIO        =  6,
  64    TARGET_E2BIG        =  7,
  65    TARGET_ENOEXEC      =  8,
  66    TARGET_EBADF        =  9,
  67    TARGET_ECHILD       = 10,
  68    TARGET_EAGAIN       = 11,
  69    TARGET_ENOMEM       = 12,
  70    TARGET_EACCES       = 13,
  71    TARGET_EFAULT       = 14,
  72    TARGET_ENOTBLK      = 15,
  73    TARGET_EBUSY        = 16,
  74    TARGET_EEXIST       = 17,
  75    TARGET_EXDEV        = 18,
  76    TARGET_ENODEV       = 19,
  77    TARGET_ENOTDIR      = 20,
  78    TARGET_EISDIR       = 21,
  79    TARGET_EINVAL       = 22,
  80    TARGET_ENFILE       = 23,
  81    TARGET_EMFILE       = 24,
  82    TARGET_ENOTTY       = 25,
  83    TARGET_ETXTBSY      = 26,
  84    TARGET_EFBIG        = 27,
  85    TARGET_ENOSPC       = 28,
  86    TARGET_ESPIPE       = 29,
  87    TARGET_EROFS        = 30,
  88    TARGET_EMLINK       = 31,
  89    TARGET_EPIPE        = 32,
  90    TARGET_EDOM         = 33,
  91    TARGET_ERANGE       = 34,
  92    TARGET_ENOSYS       = 88,
  93    TARGET_ELOOP        = 92,
  94};
  95
  96static uint32_t errno_h2g(int host_errno)
  97{
  98    switch (host_errno) {
  99    case 0:         return 0;
 100    case EPERM:     return TARGET_EPERM;
 101    case ENOENT:    return TARGET_ENOENT;
 102    case ESRCH:     return TARGET_ESRCH;
 103    case EINTR:     return TARGET_EINTR;
 104    case EIO:       return TARGET_EIO;
 105    case ENXIO:     return TARGET_ENXIO;
 106    case E2BIG:     return TARGET_E2BIG;
 107    case ENOEXEC:   return TARGET_ENOEXEC;
 108    case EBADF:     return TARGET_EBADF;
 109    case ECHILD:    return TARGET_ECHILD;
 110    case EAGAIN:    return TARGET_EAGAIN;
 111    case ENOMEM:    return TARGET_ENOMEM;
 112    case EACCES:    return TARGET_EACCES;
 113    case EFAULT:    return TARGET_EFAULT;
 114#ifdef ENOTBLK
 115    case ENOTBLK:   return TARGET_ENOTBLK;
 116#endif
 117    case EBUSY:     return TARGET_EBUSY;
 118    case EEXIST:    return TARGET_EEXIST;
 119    case EXDEV:     return TARGET_EXDEV;
 120    case ENODEV:    return TARGET_ENODEV;
 121    case ENOTDIR:   return TARGET_ENOTDIR;
 122    case EISDIR:    return TARGET_EISDIR;
 123    case EINVAL:    return TARGET_EINVAL;
 124    case ENFILE:    return TARGET_ENFILE;
 125    case EMFILE:    return TARGET_EMFILE;
 126    case ENOTTY:    return TARGET_ENOTTY;
 127#ifdef ETXTBSY
 128    case ETXTBSY:   return TARGET_ETXTBSY;
 129#endif
 130    case EFBIG:     return TARGET_EFBIG;
 131    case ENOSPC:    return TARGET_ENOSPC;
 132    case ESPIPE:    return TARGET_ESPIPE;
 133    case EROFS:     return TARGET_EROFS;
 134    case EMLINK:    return TARGET_EMLINK;
 135    case EPIPE:     return TARGET_EPIPE;
 136    case EDOM:      return TARGET_EDOM;
 137    case ERANGE:    return TARGET_ERANGE;
 138    case ENOSYS:    return TARGET_ENOSYS;
 139#ifdef ELOOP
 140    case ELOOP:     return TARGET_ELOOP;
 141#endif
 142    };
 143
 144    return TARGET_EINVAL;
 145}
 146
 147typedef struct XtensaSimConsole {
 148    CharBackend be;
 149    struct {
 150        char buffer[16];
 151        size_t offset;
 152    } input;
 153} XtensaSimConsole;
 154
 155static XtensaSimConsole *sim_console;
 156
 157static IOCanReadHandler sim_console_can_read;
 158static int sim_console_can_read(void *opaque)
 159{
 160    XtensaSimConsole *p = opaque;
 161
 162    return sizeof(p->input.buffer) - p->input.offset;
 163}
 164
 165static IOReadHandler sim_console_read;
 166static void sim_console_read(void *opaque, const uint8_t *buf, int size)
 167{
 168    XtensaSimConsole *p = opaque;
 169    size_t copy = sizeof(p->input.buffer) - p->input.offset;
 170
 171    if (size < copy) {
 172        copy = size;
 173    }
 174    memcpy(p->input.buffer + p->input.offset, buf, copy);
 175    p->input.offset += copy;
 176}
 177
 178void xtensa_sim_open_console(Chardev *chr)
 179{
 180    static XtensaSimConsole console;
 181
 182    qemu_chr_fe_init(&console.be, chr, &error_abort);
 183    qemu_chr_fe_set_handlers(&console.be,
 184                             sim_console_can_read,
 185                             sim_console_read,
 186                             NULL, NULL, &console,
 187                             NULL, true);
 188    sim_console = &console;
 189}
 190
 191void HELPER(simcall)(CPUXtensaState *env)
 192{
 193    CPUState *cs = env_cpu(env);
 194    uint32_t *regs = env->regs;
 195
 196    switch (regs[2]) {
 197    case TARGET_SYS_exit:
 198        exit(regs[3]);
 199        break;
 200
 201    case TARGET_SYS_read:
 202    case TARGET_SYS_write:
 203        {
 204            bool is_write = regs[2] == TARGET_SYS_write;
 205            uint32_t fd = regs[3];
 206            uint32_t vaddr = regs[4];
 207            uint32_t len = regs[5];
 208            uint32_t len_done = 0;
 209
 210            while (len > 0) {
 211                hwaddr paddr = cpu_get_phys_page_debug(cs, vaddr);
 212                uint32_t page_left =
 213                    TARGET_PAGE_SIZE - (vaddr & (TARGET_PAGE_SIZE - 1));
 214                uint32_t io_sz = page_left < len ? page_left : len;
 215                hwaddr sz = io_sz;
 216                void *buf = cpu_physical_memory_map(paddr, &sz, !is_write);
 217                uint32_t io_done;
 218                bool error = false;
 219
 220                if (buf) {
 221                    vaddr += io_sz;
 222                    len -= io_sz;
 223                    if (fd < 3 && sim_console) {
 224                        if (is_write && (fd == 1 || fd == 2)) {
 225                            io_done = qemu_chr_fe_write_all(&sim_console->be,
 226                                                            buf, io_sz);
 227                            regs[3] = errno_h2g(errno);
 228                        } else if (!is_write && fd == 0) {
 229                            if (sim_console->input.offset) {
 230                                io_done = sim_console->input.offset;
 231                                if (io_sz < io_done) {
 232                                    io_done = io_sz;
 233                                }
 234                                memcpy(buf, sim_console->input.buffer, io_done);
 235                                memmove(sim_console->input.buffer,
 236                                        sim_console->input.buffer + io_done,
 237                                        sim_console->input.offset - io_done);
 238                                sim_console->input.offset -= io_done;
 239                                qemu_chr_fe_accept_input(&sim_console->be);
 240                            } else {
 241                                io_done = -1;
 242                                regs[3] = TARGET_EAGAIN;
 243                            }
 244                        } else {
 245                            qemu_log_mask(LOG_GUEST_ERROR,
 246                                          "%s fd %d is not supported with chardev console\n",
 247                                          is_write ?
 248                                          "writing to" : "reading from", fd);
 249                            io_done = -1;
 250                            regs[3] = TARGET_EBADF;
 251                        }
 252                    } else {
 253                        io_done = is_write ?
 254                            write(fd, buf, io_sz) :
 255                            read(fd, buf, io_sz);
 256                        regs[3] = errno_h2g(errno);
 257                    }
 258                    if (io_done == -1) {
 259                        error = true;
 260                        io_done = 0;
 261                    }
 262                    cpu_physical_memory_unmap(buf, sz, !is_write, io_done);
 263                } else {
 264                    error = true;
 265                    regs[3] = TARGET_EINVAL;
 266                    break;
 267                }
 268                if (error) {
 269                    if (!len_done) {
 270                        len_done = -1;
 271                    }
 272                    break;
 273                }
 274                len_done += io_done;
 275                if (io_done < io_sz) {
 276                    break;
 277                }
 278            }
 279            regs[2] = len_done;
 280        }
 281        break;
 282
 283    case TARGET_SYS_open:
 284        {
 285            char name[1024];
 286            int rc;
 287            int i;
 288
 289            for (i = 0; i < ARRAY_SIZE(name); ++i) {
 290                rc = cpu_memory_rw_debug(cs, regs[3] + i,
 291                                         (uint8_t *)name + i, 1, 0);
 292                if (rc != 0 || name[i] == 0) {
 293                    break;
 294                }
 295            }
 296
 297            if (rc == 0 && i < ARRAY_SIZE(name)) {
 298                regs[2] = open(name, regs[4], regs[5]);
 299                regs[3] = errno_h2g(errno);
 300            } else {
 301                regs[2] = -1;
 302                regs[3] = TARGET_EINVAL;
 303            }
 304        }
 305        break;
 306
 307    case TARGET_SYS_close:
 308        if (regs[3] < 3) {
 309            regs[2] = regs[3] = 0;
 310        } else {
 311            regs[2] = close(regs[3]);
 312            regs[3] = errno_h2g(errno);
 313        }
 314        break;
 315
 316    case TARGET_SYS_lseek:
 317        regs[2] = lseek(regs[3], (off_t)(int32_t)regs[4], regs[5]);
 318        regs[3] = errno_h2g(errno);
 319        break;
 320
 321    case TARGET_SYS_select_one:
 322        {
 323            uint32_t fd = regs[3];
 324            uint32_t rq = regs[4];
 325            uint32_t target_tv = regs[5];
 326            uint32_t target_tvv[2];
 327
 328            struct timeval tv = {0};
 329
 330            if (target_tv) {
 331                cpu_memory_rw_debug(cs, target_tv,
 332                        (uint8_t *)target_tvv, sizeof(target_tvv), 0);
 333                tv.tv_sec = (int32_t)tswap32(target_tvv[0]);
 334                tv.tv_usec = (int32_t)tswap32(target_tvv[1]);
 335            }
 336            if (fd < 3 && sim_console) {
 337                if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) {
 338                    regs[2] = 1;
 339                } else if (fd == 0 && rq == SELECT_ONE_READ) {
 340                    regs[2] = sim_console->input.offset > 0;
 341                } else {
 342                    regs[2] = 0;
 343                }
 344                regs[3] = 0;
 345            } else {
 346                fd_set fdset;
 347
 348                FD_ZERO(&fdset);
 349                FD_SET(fd, &fdset);
 350                regs[2] = select(fd + 1,
 351                                 rq == SELECT_ONE_READ   ? &fdset : NULL,
 352                                 rq == SELECT_ONE_WRITE  ? &fdset : NULL,
 353                                 rq == SELECT_ONE_EXCEPT ? &fdset : NULL,
 354                                 target_tv ? &tv : NULL);
 355                regs[3] = errno_h2g(errno);
 356            }
 357        }
 358        break;
 359
 360    case TARGET_SYS_argc:
 361        regs[2] = semihosting_get_argc();
 362        regs[3] = 0;
 363        break;
 364
 365    case TARGET_SYS_argv_sz:
 366        {
 367            int argc = semihosting_get_argc();
 368            int sz = (argc + 1) * sizeof(uint32_t);
 369            int i;
 370
 371            for (i = 0; i < argc; ++i) {
 372                sz += 1 + strlen(semihosting_get_arg(i));
 373            }
 374            regs[2] = sz;
 375            regs[3] = 0;
 376        }
 377        break;
 378
 379    case TARGET_SYS_argv:
 380        {
 381            int argc = semihosting_get_argc();
 382            int str_offset = (argc + 1) * sizeof(uint32_t);
 383            int i;
 384            uint32_t argptr;
 385
 386            for (i = 0; i < argc; ++i) {
 387                const char *str = semihosting_get_arg(i);
 388                int str_size = strlen(str) + 1;
 389
 390                argptr = tswap32(regs[3] + str_offset);
 391
 392                cpu_memory_rw_debug(cs,
 393                                    regs[3] + i * sizeof(uint32_t),
 394                                    (uint8_t *)&argptr, sizeof(argptr), 1);
 395                cpu_memory_rw_debug(cs,
 396                                    regs[3] + str_offset,
 397                                    (uint8_t *)str, str_size, 1);
 398                str_offset += str_size;
 399            }
 400            argptr = 0;
 401            cpu_memory_rw_debug(cs,
 402                                regs[3] + i * sizeof(uint32_t),
 403                                (uint8_t *)&argptr, sizeof(argptr), 1);
 404            regs[3] = 0;
 405        }
 406        break;
 407
 408    case TARGET_SYS_memset:
 409        {
 410            uint32_t base = regs[3];
 411            uint32_t sz = regs[5];
 412
 413            while (sz) {
 414                hwaddr len = sz;
 415                void *buf = cpu_physical_memory_map(base, &len, 1);
 416
 417                if (buf && len) {
 418                    memset(buf, regs[4], len);
 419                    cpu_physical_memory_unmap(buf, len, 1, len);
 420                } else {
 421                    len = 1;
 422                }
 423                base += len;
 424                sz -= len;
 425            }
 426            regs[2] = regs[3];
 427            regs[3] = 0;
 428        }
 429        break;
 430
 431    default:
 432        qemu_log_mask(LOG_GUEST_ERROR, "%s(%d): not implemented\n", __func__, regs[2]);
 433        regs[2] = -1;
 434        regs[3] = TARGET_ENOSYS;
 435        break;
 436    }
 437}
 438