text: /* syscalls.c --- implement system calls for the RX simulator.
  
  Copyright (C) 2005-2017 Free Software Foundation, Inc.
  Contributed by Red Hat, Inc.
  
  This file is part of the GNU simulators.
  
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
  
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
  
  
  #include "config.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <fcntl.h>
  #include <unistd.h>
  #include <sys/time.h>
  
  #include "gdb/callback.h"
  
  #include "cpu.h"
  #include "mem.h"
  #include "syscalls.h"
  
  #include "syscall.h"
  
  /* The current syscall callbacks we're using.  */
  static struct host_callback_struct *callbacks;
  
  void
  set_callbacks (struct host_callback_struct *cb)
  {
    callbacks = cb;
  }
  
  struct host_callback_struct *
  get_callbacks (void)
  {
    return callbacks;
  }
  
  
  /* Arguments 1..4 are in R1..R4, remainder on stack.
  
     Return value in R1..R4 as needed.
       structs bigger than 16 bytes: pointer pushed on stack last
  
     We only support arguments that fit in general registers.
  
     The system call number is in R5.  We expect ssycalls to look like
     this in libgloss:
  
     _exit:
     	mov	#SYS_exit, r5
  	int	#255
  	rts
  */
  
  int argp, stackp;
  
  static int
  arg ()
  {
    int rv = 0;
    argp++;
  
    if (argp < 4)
      return get_reg (argp);
  
    rv = mem_get_si (get_reg (sp) + stackp);
    stackp += 4;
    return rv;
  }
  
  static void
  read_target (char *buffer, int address, int count, int asciiz)
  {
    char byte;
    while (count > 0)
      {
        byte = mem_get_qi (address++);
        *buffer++ = byte;
        if (asciiz && (byte == 0))
  	return;
        count--;
      }
  }
  
  static void
  write_target (char *buffer, int address, int count, int asciiz)
  {
    char byte;
    while (count > 0)
      {
        byte = *buffer++;
        mem_put_qi (address++, byte);
        if (asciiz && (byte == 0))
  	return;
        count--;
      }
  }
  
  #define PTRSZ (A16 ? 2 : 3)
  
  static char *callnames[] = {
    "SYS_zero",
    "SYS_exit",
    "SYS_open",
    "SYS_close",
    "SYS_read",
    "SYS_write",
    "SYS_lseek",
    "SYS_unlink",
    "SYS_getpid",
    "SYS_kill",
    "SYS_fstat",
    "SYS_sbrk",
    "SYS_argvlen",
    "SYS_argv",
    "SYS_chdir",
    "SYS_stat",
    "SYS_chmod",
    "SYS_utime",
    "SYS_time",
    "SYS_gettimeofday",
    "SYS_times",
    "SYS_link"
  };
  
  int
  rx_syscall (int id)
  {
    static char buf[256];
    int rv;
  
    argp = 0;
    stackp = 4;
    if (trace)
      printf ("\033[31m/* SYSCALL(%d) = %s */\033[0m\n", id, id <= SYS_link ? callnames[id] : "unknown");
    switch (id)
      {
      case SYS_exit:
        {
  	int ec = arg ();
  	if (verbose)
  	  printf ("[exit %d]\n", ec);
  	return RX_MAKE_EXITED (ec);
        }
        break;
  
      case SYS_open:
        {
  	int path = arg ();
  	/* The open function is defined as taking a variable number of arguments
  	   because the third parameter to it is optional:
  	     open (const char * filename, int flags, ...);
  	   Hence the oflags and cflags arguments will be on the stack and we need
  	   to skip the (empty) argument registers r3 and r4.  */
  	argp = 4;
  	int oflags = arg ();
  	int cflags = arg ();
  
  	read_target (buf, path, 256, 1);
  	if (trace)
  	  printf ("open(\"%s\",0x%x,%#o) = ", buf, oflags, cflags);
  
  	if (callbacks)
  	  /* The callback vector ignores CFLAGS.  */
  	  rv = callbacks->open (callbacks, buf, oflags);
  	else
  	  {
  	    int h_oflags = 0;
  
  	    if (oflags & 0x0001)
  	      h_oflags |= O_WRONLY;
  	    if (oflags & 0x0002)
  	      h_oflags |= O_RDWR;
  	    if (oflags & 0x0200)
  	      h_oflags |= O_CREAT;
  	    if (oflags & 0x0008)
  	      h_oflags |= O_APPEND;
  	    if (oflags & 0x0400)
  	      h_oflags |= O_TRUNC;
  	    rv = open (buf, h_oflags, cflags);
  	  }
  	if (trace)
  	  printf ("%d\n", rv);
  	put_reg (1, rv);
        }
        break;
  
      case SYS_close:
        {
  	int fd = arg ();
  
  	if (callbacks)
  	  rv = callbacks->close (callbacks, fd);
  	else if (fd > 2)
  	  rv = close (fd);
  	else
  	  rv = 0;
  	if (trace)
  	  printf ("close(%d) = %d\n", fd, rv);
  	put_reg (1, rv);
        }
        break;
  
      case SYS_read:
        {
  	int fd = arg ();
  	int addr = arg ();
  	int count = arg ();
  
  	if (count > sizeof (buf))
  	  count = sizeof (buf);
  	if (callbacks)
  	  rv = callbacks->read (callbacks, fd, buf, count);
  	else
  	  rv = read (fd, buf, count);
  	if (trace)
  	  printf ("read(%d,%d) = %d\n", fd, count, rv);
  	if (rv > 0)
  	  write_target (buf, addr, rv, 0);
  	put_reg (1, rv);
        }
        break;
  
      case SYS_write:
        {
  	int fd = arg ();
  	int addr = arg ();
  	int count = arg ();
  
  	if (count > sizeof (buf))
  	  count = sizeof (buf);
  	if (trace)
  	  printf ("write(%d,0x%x,%d)\n", fd, addr, count);
  	read_target (buf, addr, count, 0);
  	if (trace)
  	  fflush (stdout);
  	if (callbacks)
  	  rv = callbacks->write (callbacks, fd, buf, count);
  	else
  	  rv = write (fd, buf, count);
  	if (trace)
  	  printf ("write(%d,%d) = %d\n", fd, count, rv);
  	put_reg (1, rv);
        }
        break;
  
      case SYS_getpid:
        put_reg (1, 42);
        break;
  
      case SYS_gettimeofday:
        {
  	int tvaddr = arg ();
  	struct timeval tv;
  
  	rv = gettimeofday (&tv, 0);
  	if (trace)
  	  printf ("gettimeofday: %ld sec %ld usec to 0x%x\n", tv.tv_sec,
  		  tv.tv_usec, tvaddr);
  	mem_put_si (tvaddr, tv.tv_sec);
  	mem_put_si (tvaddr + 4, tv.tv_usec);
  	put_reg (1, rv);
        }
        break;
  
      case SYS_kill:
        {
  	int pid = arg ();
  	int sig = arg ();
  	if (pid == 42)
  	  {
  	    if (verbose)
  	      printf ("[signal %d]\n", sig);
  	    return RX_MAKE_STOPPED (sig);
  	  }
        }
        break;
  
      case 11:
        {
  	int heaptop_arg = arg ();
  	if (trace)
  	  printf ("sbrk: heap top set to %x\n", heaptop_arg);
  	heaptop = heaptop_arg;
  	if (heapbottom == 0)
  	  heapbottom = heaptop_arg;
        }
        break;
  
      case 255:
        {
  	int addr = arg ();
  	mem_put_si (addr, rx_cycles + mem_usage_cycles());
        }
        break;
  
      }
    return RX_MAKE_STEPPED ();
  }
  