* tic6x/syscalls.c (cio_getclk_to_host, cio_getclk_from_host): New
        structs.
        (cio_to_host, cio_from_host): Use them.
        (clock): New function.
		
	
		
			
				
	
	
		
			383 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			383 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (c) 2010 CodeSourcery, Inc.
 | |
|    All rights reserved.
 | |
| 
 | |
|    Redistribution and use in source and binary forms, with or without
 | |
|    modification, are permitted provided that the following conditions are met:
 | |
|     * Redistributions of source code must retain the above copyright
 | |
|       notice, this list of conditions and the following disclaimer.
 | |
|     * Redistributions in binary form must reproduce the above copyright
 | |
|       notice, this list of conditions and the following disclaimer in the
 | |
|       documentation and/or other materials provided with the distribution.
 | |
|     * Neither the name of CodeSourcery nor the
 | |
|       names of its contributors may be used to endorse or promote products
 | |
|       derived from this software without specific prior written permission.
 | |
| 
 | |
|     THIS SOFTWARE IS PROVIDED BY CODESOURCERY, INC. ``AS IS'' AND ANY
 | |
|     EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | |
|     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 | |
|     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL CODESOURCERY BE LIABLE
 | |
|     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | |
|     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 | |
|     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 | |
|     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | |
|     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
|     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 | |
|     USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 | |
|     DAMAGE.  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <time.h>
 | |
| #include <sys/time.h>
 | |
| #include <sys/stat.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #define _DTOPEN 0xf0
 | |
| #define _DTCLOSE 0xf1
 | |
| #define _DTREAD 0xf2
 | |
| #define _DTWRITE 0xf3
 | |
| #define _DTLSEEK 0xf4
 | |
| #define _DTUNLINK 0xf5
 | |
| #define _DTGETENV 0xf6
 | |
| #define _DTRENAME 0xf7
 | |
| #define _DTGETTIME 0xf8
 | |
| #define _DTGETCLK 0xf9
 | |
| #define _DTSYNC 0xff
 | |
| 
 | |
| #define CIOBUFSIZ (BUFSIZ + 32)
 | |
| 
 | |
| struct __attribute__((packed)) cio_open_to_host
 | |
| {
 | |
|   /* Suggested file descriptor (little endian).  */
 | |
|   short fd;
 | |
|   /* Flags (little endian).  */
 | |
|   short flags;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_open_from_host
 | |
| {
 | |
|   /* File descriptor (little endian).  */
 | |
|   short fd;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_close_to_host
 | |
| {
 | |
|   /* File descriptor (little endian).  */
 | |
|   short fd;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_close_from_host
 | |
| {
 | |
|   /* Result (little endian).  */
 | |
|   short result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_read_to_host
 | |
| {
 | |
|   /* File descriptor (little endian).  */
 | |
|   short fd;
 | |
|   /* Length (little endian).  */
 | |
|   short length;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_read_from_host
 | |
| {
 | |
|   /* Result (little endian).  */
 | |
|   short result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_write_to_host
 | |
| {
 | |
|   /* File descriptor (little endian).  */
 | |
|   short fd;
 | |
|   /* Length (little endian).  */
 | |
|   short length;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_write_from_host
 | |
| {
 | |
|   /* Result (little endian).  */
 | |
|   short result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_lseek_to_host
 | |
| {
 | |
|   /* File descriptor (little endian).  */
 | |
|   short fd;
 | |
|   /* Offset (little endian).  */
 | |
|   int offset;
 | |
|   /* Whence (little endian).  */
 | |
|   short whence;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_lseek_from_host
 | |
| {
 | |
|   /* Result (little endian).  */
 | |
|   int result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_unlink_to_host
 | |
| {
 | |
|   /* Empty.  */
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_unlink_from_host
 | |
| {
 | |
|   /* Result (little endian).  */
 | |
|   short result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_rename_to_host
 | |
| {
 | |
|   /* Empty.  */
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_rename_from_host
 | |
| {
 | |
|   /* Result (little endian).  */
 | |
|   short result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_gettime_to_host
 | |
| {
 | |
|   /* Empty.  */
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_gettime_from_host
 | |
| {
 | |
|   /* Time (little endian).  */
 | |
|   int time;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_getclk_to_host
 | |
| {
 | |
|   /* Empty.  */
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_getclk_from_host
 | |
| {
 | |
|   /* Clock cycles (little endian).  */
 | |
|   int result;
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_to_host
 | |
| {
 | |
|   /* Data length (target endian).  */
 | |
|   unsigned int length;
 | |
|   /* Command.  */
 | |
|   unsigned char command;
 | |
|   /* Parameters.  */
 | |
|   union
 | |
|   {
 | |
|     unsigned char buf[8];
 | |
|     struct cio_open_to_host open;
 | |
|     struct cio_close_to_host close;
 | |
|     struct cio_read_to_host read;
 | |
|     struct cio_write_to_host write;
 | |
|     struct cio_lseek_to_host lseek;
 | |
|     struct cio_unlink_to_host unlink;
 | |
|     struct cio_rename_to_host rename;
 | |
|     struct cio_gettime_to_host gettime;
 | |
|     struct cio_getclk_to_host getclk;
 | |
|   } parms;
 | |
|   /* Variable-length data.  */
 | |
|   unsigned char data[];
 | |
| };
 | |
| 
 | |
| struct __attribute__((packed)) cio_from_host
 | |
| {
 | |
|   /* Length (target endian).  */
 | |
|   unsigned int length;
 | |
|   /* Parameters.  */
 | |
|   union
 | |
|   {
 | |
|     unsigned char buf[8];
 | |
|     struct cio_open_from_host open;
 | |
|     struct cio_close_from_host close;
 | |
|     struct cio_read_from_host read;
 | |
|     struct cio_write_from_host write;
 | |
|     struct cio_lseek_from_host lseek;
 | |
|     struct cio_unlink_from_host unlink;
 | |
|     struct cio_rename_from_host rename;
 | |
|     struct cio_gettime_from_host gettime;
 | |
|     struct cio_getclk_from_host getclk;
 | |
|   } parms;
 | |
|   /* Data.  */
 | |
|   unsigned char data[];
 | |
| };
 | |
| 
 | |
| union
 | |
| {
 | |
|   unsigned char buf[CIOBUFSIZ];
 | |
|   int align;
 | |
|   union
 | |
|   {
 | |
|     struct cio_to_host to_host;
 | |
|     struct cio_from_host from_host;
 | |
|   } u;
 | |
| } _CIOBUF_ __attribute__((section(".cio")));
 | |
| 
 | |
| #ifdef _BIG_ENDIAN
 | |
| #define SWAPSHORT(s)	((short)((((s) & 0xff) << 8) | (((s) & 0xff00) >> 8)))
 | |
| #define SWAPINT(i)	(__builtin_bswap32 (i))
 | |
| #else
 | |
| #define SWAPSHORT(s)	(s)
 | |
| #define SWAPINT(i)	(i)
 | |
| #endif
 | |
| 
 | |
| static void __attribute__((noinline))
 | |
| do_semi_call (void)
 | |
| {
 | |
|   asm volatile (".globl C$$IO$$\nnop\nC$$IO$$:nop" : "+m" (_CIOBUF_));
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| semi_call_wrapper (unsigned char command, const char *data,
 | |
| 		   unsigned int length)
 | |
| {
 | |
|   _CIOBUF_.u.to_host.length = length;
 | |
|   _CIOBUF_.u.to_host.command = command;
 | |
|   if (data != NULL)
 | |
|     memcpy (_CIOBUF_.u.to_host.data, data, length);
 | |
|   do_semi_call ();
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| semi_call_wrapper2 (unsigned char command, const char *data1,
 | |
| 		    unsigned int length1, const char *data2,
 | |
| 		    unsigned int length2)
 | |
| {
 | |
|   _CIOBUF_.u.to_host.length = length1 + length2;
 | |
|   _CIOBUF_.u.to_host.command = command;
 | |
|   if (data1 != NULL)
 | |
|     memcpy (_CIOBUF_.u.to_host.data, data1, length1);
 | |
|   if (data2 != NULL)
 | |
|     memcpy (_CIOBUF_.u.to_host.data + length1, data2, length2);
 | |
|   do_semi_call ();
 | |
| }
 | |
| 
 | |
| void
 | |
| _exit (int status)
 | |
| {
 | |
|   /* The semihosting interface appears to provide no way to return an
 | |
|      exit status.  */
 | |
|   asm volatile (".globl C$$EXIT\nnop\nC$$EXIT:nop");
 | |
| }
 | |
| 
 | |
| int
 | |
| open (const char *path, int flags, ...)
 | |
| {
 | |
|   /* ??? It's not clear what the suggested fd is for.  */
 | |
|   static short suggest_fd = 3;
 | |
|   short ret_fd;
 | |
|   ++suggest_fd;
 | |
|   _CIOBUF_.u.to_host.parms.open.fd = SWAPSHORT (suggest_fd);
 | |
|   _CIOBUF_.u.to_host.parms.open.flags = SWAPSHORT (flags);
 | |
|   semi_call_wrapper (_DTOPEN, path, strlen (path) + 1);
 | |
|   ret_fd = SWAPSHORT (_CIOBUF_.u.from_host.parms.open.fd);
 | |
|   if (ret_fd == -1)
 | |
|     return -1;
 | |
|   return suggest_fd;
 | |
| }
 | |
| 
 | |
| int
 | |
| close (int fd)
 | |
| {
 | |
|   _CIOBUF_.u.to_host.parms.close.fd = SWAPSHORT (fd);
 | |
|   semi_call_wrapper (_DTCLOSE, NULL, 0);
 | |
|   return SWAPSHORT (_CIOBUF_.u.from_host.parms.close.result);
 | |
| }
 | |
| 
 | |
| int
 | |
| read (int fd, char *ptr, int len)
 | |
| {
 | |
|   if (len > BUFSIZ)
 | |
|     len = BUFSIZ;
 | |
|   _CIOBUF_.u.to_host.parms.read.fd = SWAPSHORT (fd);
 | |
|   _CIOBUF_.u.to_host.parms.read.length = SWAPSHORT (len);
 | |
|   semi_call_wrapper (_DTREAD, NULL, 0);
 | |
|   memcpy (ptr, _CIOBUF_.u.from_host.data, _CIOBUF_.u.from_host.length);
 | |
|   return SWAPSHORT (_CIOBUF_.u.from_host.parms.read.result);
 | |
| }
 | |
| 
 | |
| int
 | |
| write (int fd, char *ptr, int len)
 | |
| {
 | |
|   if (len > BUFSIZ)
 | |
|     len = BUFSIZ;
 | |
|   _CIOBUF_.u.to_host.parms.write.fd = SWAPSHORT (fd);
 | |
|   _CIOBUF_.u.to_host.parms.write.length = SWAPSHORT (len);
 | |
|   semi_call_wrapper (_DTWRITE, ptr, len);
 | |
|   return SWAPSHORT (_CIOBUF_.u.from_host.parms.write.result);
 | |
| }
 | |
| 
 | |
| int
 | |
| lseek (int fd, int offset, int whence)
 | |
| {
 | |
|   _CIOBUF_.u.to_host.parms.lseek.fd = SWAPSHORT (fd);
 | |
|   _CIOBUF_.u.to_host.parms.lseek.offset = SWAPINT (offset);
 | |
|   _CIOBUF_.u.to_host.parms.lseek.whence = SWAPSHORT (whence);
 | |
|   semi_call_wrapper (_DTLSEEK, NULL, 0);
 | |
|   return SWAPINT (_CIOBUF_.u.from_host.parms.lseek.result);
 | |
| }
 | |
| 
 | |
| int
 | |
| unlink (const char *path)
 | |
| {
 | |
|   semi_call_wrapper (_DTUNLINK, path, strlen (path) + 1);
 | |
|   return SWAPSHORT (_CIOBUF_.u.from_host.parms.unlink.result);
 | |
| }
 | |
| 
 | |
| int
 | |
| rename (const char *oldpath, const char *newpath)
 | |
| {
 | |
|   semi_call_wrapper2 (_DTRENAME, oldpath, strlen (oldpath) + 1,
 | |
| 		      newpath, strlen (newpath) + 1);
 | |
|   return SWAPSHORT (_CIOBUF_.u.from_host.parms.rename.result);
 | |
| }
 | |
| 
 | |
| int
 | |
| gettimeofday (struct timeval *tp, void *tzvp)
 | |
| {
 | |
|   struct timezone *tzp = tzvp;
 | |
| 
 | |
|   if (tp)
 | |
|     {
 | |
|       semi_call_wrapper (_DTGETTIME, NULL, 0);
 | |
|       tp->tv_sec = SWAPINT (_CIOBUF_.u.from_host.parms.gettime.time);
 | |
|       tp->tv_usec = 0;
 | |
|     }
 | |
| 
 | |
|   if (tzp)
 | |
|     {
 | |
|       tzp->tz_minuteswest = 0;
 | |
|       tzp->tz_dsttime = 0;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| clock_t
 | |
| clock (void)
 | |
| {
 | |
|   semi_call_wrapper (_DTGETCLK, NULL, 0);
 | |
|   return SWAPINT (_CIOBUF_.u.from_host.parms.getclk.result);
 | |
| }
 | |
| 
 | |
| 
 | |
| int
 | |
| isatty (int file __attribute__((unused)))
 | |
| {
 | |
|   errno = ENOSYS;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fstat (int fd, struct stat *buf)
 | |
| {
 | |
|   buf->st_mode = S_IFCHR;	/* Always pretend to be a tty */
 | |
|   buf->st_blksize = 0;
 | |
| 
 | |
|   return (0);
 | |
| }
 |