 54fc33fdc4
			
		
	
	
		54fc33fdc4
		
	
	
	
	
		
			
			The label 'uhi_done' is a simple 'return' call and can be removed for a bit more clarity in the code. CC: Aurelien Jarno <aurelien@aurel32.net> CC: Aleksandar Markovic <amarkovic@wavecomp.com> CC: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> Reviewed-by: Aleksandar Markovic <amarkovic@wavecomp.com> Signed-off-by: Aleksandar Markovic <amarkovic@wavecomp.com> Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com> Message-Id: <20200106182425.20312-7-danielhb413@gmail.com>
		
			
				
	
	
		
			374 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			374 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Unified Hosting Interface syscalls.
 | |
|  *
 | |
|  * Copyright (c) 2015 Imagination Technologies
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library 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
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "cpu.h"
 | |
| #include "qemu/log.h"
 | |
| #include "exec/helper-proto.h"
 | |
| #include "exec/softmmu-semi.h"
 | |
| #include "hw/semihosting/semihost.h"
 | |
| #include "hw/semihosting/console.h"
 | |
| 
 | |
| typedef enum UHIOp {
 | |
|     UHI_exit = 1,
 | |
|     UHI_open = 2,
 | |
|     UHI_close = 3,
 | |
|     UHI_read = 4,
 | |
|     UHI_write = 5,
 | |
|     UHI_lseek = 6,
 | |
|     UHI_unlink = 7,
 | |
|     UHI_fstat = 8,
 | |
|     UHI_argc = 9,
 | |
|     UHI_argnlen = 10,
 | |
|     UHI_argn = 11,
 | |
|     UHI_plog = 13,
 | |
|     UHI_assert = 14,
 | |
|     UHI_pread = 19,
 | |
|     UHI_pwrite = 20,
 | |
|     UHI_link = 22
 | |
| } UHIOp;
 | |
| 
 | |
| typedef struct UHIStat {
 | |
|     int16_t uhi_st_dev;
 | |
|     uint16_t uhi_st_ino;
 | |
|     uint32_t uhi_st_mode;
 | |
|     uint16_t uhi_st_nlink;
 | |
|     uint16_t uhi_st_uid;
 | |
|     uint16_t uhi_st_gid;
 | |
|     int16_t uhi_st_rdev;
 | |
|     uint64_t uhi_st_size;
 | |
|     uint64_t uhi_st_atime;
 | |
|     uint64_t uhi_st_spare1;
 | |
|     uint64_t uhi_st_mtime;
 | |
|     uint64_t uhi_st_spare2;
 | |
|     uint64_t uhi_st_ctime;
 | |
|     uint64_t uhi_st_spare3;
 | |
|     uint64_t uhi_st_blksize;
 | |
|     uint64_t uhi_st_blocks;
 | |
|     uint64_t uhi_st_spare4[2];
 | |
| } UHIStat;
 | |
| 
 | |
| enum UHIOpenFlags {
 | |
|     UHIOpen_RDONLY = 0x0,
 | |
|     UHIOpen_WRONLY = 0x1,
 | |
|     UHIOpen_RDWR   = 0x2,
 | |
|     UHIOpen_APPEND = 0x8,
 | |
|     UHIOpen_CREAT  = 0x200,
 | |
|     UHIOpen_TRUNC  = 0x400,
 | |
|     UHIOpen_EXCL   = 0x800
 | |
| };
 | |
| 
 | |
| /* Errno values taken from asm-mips/errno.h */
 | |
| static uint16_t host_to_mips_errno[] = {
 | |
|     [ENAMETOOLONG] = 78,
 | |
| #ifdef EOVERFLOW
 | |
|     [EOVERFLOW]    = 79,
 | |
| #endif
 | |
| #ifdef ELOOP
 | |
|     [ELOOP]        = 90,
 | |
| #endif
 | |
| };
 | |
| 
 | |
| static int errno_mips(int err)
 | |
| {
 | |
|     if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
 | |
|         return EINVAL;
 | |
|     } else if (host_to_mips_errno[err]) {
 | |
|         return host_to_mips_errno[err];
 | |
|     } else {
 | |
|         return err;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
 | |
|                                target_ulong vaddr)
 | |
| {
 | |
|     hwaddr len = sizeof(struct UHIStat);
 | |
|     UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
 | |
|     if (!dst) {
 | |
|         errno = EFAULT;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     dst->uhi_st_dev = tswap16(src->st_dev);
 | |
|     dst->uhi_st_ino = tswap16(src->st_ino);
 | |
|     dst->uhi_st_mode = tswap32(src->st_mode);
 | |
|     dst->uhi_st_nlink = tswap16(src->st_nlink);
 | |
|     dst->uhi_st_uid = tswap16(src->st_uid);
 | |
|     dst->uhi_st_gid = tswap16(src->st_gid);
 | |
|     dst->uhi_st_rdev = tswap16(src->st_rdev);
 | |
|     dst->uhi_st_size = tswap64(src->st_size);
 | |
|     dst->uhi_st_atime = tswap64(src->st_atime);
 | |
|     dst->uhi_st_mtime = tswap64(src->st_mtime);
 | |
|     dst->uhi_st_ctime = tswap64(src->st_ctime);
 | |
| #ifdef _WIN32
 | |
|     dst->uhi_st_blksize = 0;
 | |
|     dst->uhi_st_blocks = 0;
 | |
| #else
 | |
|     dst->uhi_st_blksize = tswap64(src->st_blksize);
 | |
|     dst->uhi_st_blocks = tswap64(src->st_blocks);
 | |
| #endif
 | |
|     unlock_user(dst, vaddr, len);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int get_open_flags(target_ulong target_flags)
 | |
| {
 | |
|     int open_flags = 0;
 | |
| 
 | |
|     if (target_flags & UHIOpen_RDWR) {
 | |
|         open_flags |= O_RDWR;
 | |
|     } else if (target_flags & UHIOpen_WRONLY) {
 | |
|         open_flags |= O_WRONLY;
 | |
|     } else {
 | |
|         open_flags |= O_RDONLY;
 | |
|     }
 | |
| 
 | |
|     open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
 | |
|     open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
 | |
|     open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
 | |
|     open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
 | |
| 
 | |
|     return open_flags;
 | |
| }
 | |
| 
 | |
| static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
 | |
|                          target_ulong len, target_ulong offset)
 | |
| {
 | |
|     int num_of_bytes;
 | |
|     void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
 | |
|     if (!dst) {
 | |
|         errno = EFAULT;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (offset) {
 | |
| #ifdef _WIN32
 | |
|         num_of_bytes = 0;
 | |
| #else
 | |
|         num_of_bytes = pwrite(fd, dst, len, offset);
 | |
| #endif
 | |
|     } else {
 | |
|         num_of_bytes = write(fd, dst, len);
 | |
|     }
 | |
| 
 | |
|     unlock_user(dst, vaddr, 0);
 | |
|     return num_of_bytes;
 | |
| }
 | |
| 
 | |
| static int read_from_file(CPUMIPSState *env, target_ulong fd,
 | |
|                           target_ulong vaddr, target_ulong len,
 | |
|                           target_ulong offset)
 | |
| {
 | |
|     int num_of_bytes;
 | |
|     void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
 | |
|     if (!dst) {
 | |
|         errno = EFAULT;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (offset) {
 | |
| #ifdef _WIN32
 | |
|         num_of_bytes = 0;
 | |
| #else
 | |
|         num_of_bytes = pread(fd, dst, len, offset);
 | |
| #endif
 | |
|     } else {
 | |
|         num_of_bytes = read(fd, dst, len);
 | |
|     }
 | |
| 
 | |
|     unlock_user(dst, vaddr, len);
 | |
|     return num_of_bytes;
 | |
| }
 | |
| 
 | |
| static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
 | |
|                                target_ulong vaddr)
 | |
| {
 | |
|     int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
 | |
|     char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
 | |
|     if (!dst) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     strcpy(dst, semihosting_get_arg(arg_num));
 | |
| 
 | |
|     unlock_user(dst, vaddr, strsize);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #define GET_TARGET_STRING(p, addr)              \
 | |
|     do {                                        \
 | |
|         p = lock_user_string(addr);             \
 | |
|         if (!p) {                               \
 | |
|             gpr[2] = -1;                        \
 | |
|             gpr[3] = EFAULT;                    \
 | |
|             return;                             \
 | |
|         }                                       \
 | |
|     } while (0)
 | |
| 
 | |
| #define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
 | |
|     do {                                                \
 | |
|         p = lock_user_string(addr);                     \
 | |
|         if (!p) {                                       \
 | |
|             gpr[2] = -1;                                \
 | |
|             gpr[3] = EFAULT;                            \
 | |
|             return;                                     \
 | |
|         }                                               \
 | |
|         p2 = lock_user_string(addr2);                   \
 | |
|         if (!p2) {                                      \
 | |
|             unlock_user(p, addr, 0);                    \
 | |
|             gpr[2] = -1;                                \
 | |
|             gpr[3] = EFAULT;                            \
 | |
|             return;                                     \
 | |
|         }                                               \
 | |
|     } while (0)
 | |
| 
 | |
| #define FREE_TARGET_STRING(p, gpr)              \
 | |
|     do {                                        \
 | |
|         unlock_user(p, gpr, 0);                 \
 | |
|     } while (0)
 | |
| 
 | |
| void helper_do_semihosting(CPUMIPSState *env)
 | |
| {
 | |
|     target_ulong *gpr = env->active_tc.gpr;
 | |
|     const UHIOp op = gpr[25];
 | |
|     char *p, *p2;
 | |
| 
 | |
|     switch (op) {
 | |
|     case UHI_exit:
 | |
|         qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
 | |
|         exit(gpr[4]);
 | |
|     case UHI_open:
 | |
|         GET_TARGET_STRING(p, gpr[4]);
 | |
|         if (!strcmp("/dev/stdin", p)) {
 | |
|             gpr[2] = 0;
 | |
|         } else if (!strcmp("/dev/stdout", p)) {
 | |
|             gpr[2] = 1;
 | |
|         } else if (!strcmp("/dev/stderr", p)) {
 | |
|             gpr[2] = 2;
 | |
|         } else {
 | |
|             gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
 | |
|             gpr[3] = errno_mips(errno);
 | |
|         }
 | |
|         FREE_TARGET_STRING(p, gpr[4]);
 | |
|         break;
 | |
|     case UHI_close:
 | |
|         if (gpr[4] < 3) {
 | |
|             /* ignore closing stdin/stdout/stderr */
 | |
|             gpr[2] = 0;
 | |
|             return;
 | |
|         }
 | |
|         gpr[2] = close(gpr[4]);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         break;
 | |
|     case UHI_read:
 | |
|         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         break;
 | |
|     case UHI_write:
 | |
|         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         break;
 | |
|     case UHI_lseek:
 | |
|         gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         break;
 | |
|     case UHI_unlink:
 | |
|         GET_TARGET_STRING(p, gpr[4]);
 | |
|         gpr[2] = remove(p);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         FREE_TARGET_STRING(p, gpr[4]);
 | |
|         break;
 | |
|     case UHI_fstat:
 | |
|         {
 | |
|             struct stat sbuf;
 | |
|             memset(&sbuf, 0, sizeof(sbuf));
 | |
|             gpr[2] = fstat(gpr[4], &sbuf);
 | |
|             gpr[3] = errno_mips(errno);
 | |
|             if (gpr[2]) {
 | |
|                 return;
 | |
|             }
 | |
|             gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
 | |
|             gpr[3] = errno_mips(errno);
 | |
|         }
 | |
|         break;
 | |
|     case UHI_argc:
 | |
|         gpr[2] = semihosting_get_argc();
 | |
|         break;
 | |
|     case UHI_argnlen:
 | |
|         if (gpr[4] >= semihosting_get_argc()) {
 | |
|             gpr[2] = -1;
 | |
|             return;
 | |
|         }
 | |
|         gpr[2] = strlen(semihosting_get_arg(gpr[4]));
 | |
|         break;
 | |
|     case UHI_argn:
 | |
|         if (gpr[4] >= semihosting_get_argc()) {
 | |
|             gpr[2] = -1;
 | |
|             return;
 | |
|         }
 | |
|         gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
 | |
|         break;
 | |
|     case UHI_plog:
 | |
|         GET_TARGET_STRING(p, gpr[4]);
 | |
|         p2 = strstr(p, "%d");
 | |
|         if (p2) {
 | |
|             int char_num = p2 - p;
 | |
|             GString *s = g_string_new_len(p, char_num);
 | |
|             g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2);
 | |
|             gpr[2] = qemu_semihosting_log_out(s->str, s->len);
 | |
|             g_string_free(s, true);
 | |
|         } else {
 | |
|             gpr[2] = qemu_semihosting_log_out(p, strlen(p));
 | |
|         }
 | |
|         FREE_TARGET_STRING(p, gpr[4]);
 | |
|         break;
 | |
|     case UHI_assert:
 | |
|         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
 | |
|         printf("assertion '");
 | |
|         printf("\"%s\"", p);
 | |
|         printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
 | |
|         FREE_TARGET_STRING(p2, gpr[5]);
 | |
|         FREE_TARGET_STRING(p, gpr[4]);
 | |
|         abort();
 | |
|         break;
 | |
|     case UHI_pread:
 | |
|         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         break;
 | |
|     case UHI_pwrite:
 | |
|         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         break;
 | |
| #ifndef _WIN32
 | |
|     case UHI_link:
 | |
|         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
 | |
|         gpr[2] = link(p, p2);
 | |
|         gpr[3] = errno_mips(errno);
 | |
|         FREE_TARGET_STRING(p2, gpr[5]);
 | |
|         FREE_TARGET_STRING(p, gpr[4]);
 | |
|         break;
 | |
| #endif
 | |
|     default:
 | |
|         fprintf(stderr, "Unknown UHI operation %d\n", op);
 | |
|         abort();
 | |
|     }
 | |
|     return;
 | |
| }
 |