xref: /qemu/target/mips/tcg/system/mips-semi.c (revision 26e7e982b267e71d40cd20e9e234fedef6770a90)
13b3c1694SLeon Alrae /*
23b3c1694SLeon Alrae  * Unified Hosting Interface syscalls.
33b3c1694SLeon Alrae  *
43b3c1694SLeon Alrae  * Copyright (c) 2015 Imagination Technologies
53b3c1694SLeon Alrae  *
63b3c1694SLeon Alrae  * This library is free software; you can redistribute it and/or
73b3c1694SLeon Alrae  * modify it under the terms of the GNU Lesser General Public
83b3c1694SLeon Alrae  * License as published by the Free Software Foundation; either
93b3c1694SLeon Alrae  * version 2 of the License, or (at your option) any later version.
103b3c1694SLeon Alrae  *
113b3c1694SLeon Alrae  * This library is distributed in the hope that it will be useful,
123b3c1694SLeon Alrae  * but WITHOUT ANY WARRANTY; without even the implied warranty of
133b3c1694SLeon Alrae  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
143b3c1694SLeon Alrae  * Lesser General Public License for more details.
153b3c1694SLeon Alrae  *
163b3c1694SLeon Alrae  * You should have received a copy of the GNU Lesser General Public
173b3c1694SLeon Alrae  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
183b3c1694SLeon Alrae  */
193b3c1694SLeon Alrae 
203b3c1694SLeon Alrae #include <sys/stat.h>
213b3c1694SLeon Alrae #include "cpu.h"
223b3c1694SLeon Alrae #include "exec/helper-proto.h"
233b3c1694SLeon Alrae #include "exec/softmmu-semi.h"
243b3c1694SLeon Alrae #include "exec/semihost.h"
253b3c1694SLeon Alrae 
263b3c1694SLeon Alrae typedef enum UHIOp {
273b3c1694SLeon Alrae     UHI_exit = 1,
283b3c1694SLeon Alrae     UHI_open = 2,
293b3c1694SLeon Alrae     UHI_close = 3,
303b3c1694SLeon Alrae     UHI_read = 4,
313b3c1694SLeon Alrae     UHI_write = 5,
323b3c1694SLeon Alrae     UHI_lseek = 6,
333b3c1694SLeon Alrae     UHI_unlink = 7,
343b3c1694SLeon Alrae     UHI_fstat = 8,
353b3c1694SLeon Alrae     UHI_argc = 9,
363b3c1694SLeon Alrae     UHI_argnlen = 10,
373b3c1694SLeon Alrae     UHI_argn = 11,
383b3c1694SLeon Alrae     UHI_plog = 13,
393b3c1694SLeon Alrae     UHI_assert = 14,
403b3c1694SLeon Alrae     UHI_pread = 19,
413b3c1694SLeon Alrae     UHI_pwrite = 20,
423b3c1694SLeon Alrae     UHI_link = 22
433b3c1694SLeon Alrae } UHIOp;
443b3c1694SLeon Alrae 
453b3c1694SLeon Alrae typedef struct UHIStat {
463b3c1694SLeon Alrae     int16_t uhi_st_dev;
473b3c1694SLeon Alrae     uint16_t uhi_st_ino;
483b3c1694SLeon Alrae     uint32_t uhi_st_mode;
493b3c1694SLeon Alrae     uint16_t uhi_st_nlink;
503b3c1694SLeon Alrae     uint16_t uhi_st_uid;
513b3c1694SLeon Alrae     uint16_t uhi_st_gid;
523b3c1694SLeon Alrae     int16_t uhi_st_rdev;
533b3c1694SLeon Alrae     uint64_t uhi_st_size;
543b3c1694SLeon Alrae     uint64_t uhi_st_atime;
553b3c1694SLeon Alrae     uint64_t uhi_st_spare1;
563b3c1694SLeon Alrae     uint64_t uhi_st_mtime;
573b3c1694SLeon Alrae     uint64_t uhi_st_spare2;
583b3c1694SLeon Alrae     uint64_t uhi_st_ctime;
593b3c1694SLeon Alrae     uint64_t uhi_st_spare3;
603b3c1694SLeon Alrae     uint64_t uhi_st_blksize;
613b3c1694SLeon Alrae     uint64_t uhi_st_blocks;
623b3c1694SLeon Alrae     uint64_t uhi_st_spare4[2];
633b3c1694SLeon Alrae } UHIStat;
643b3c1694SLeon Alrae 
653b3c1694SLeon Alrae enum UHIOpenFlags {
663b3c1694SLeon Alrae     UHIOpen_RDONLY = 0x0,
673b3c1694SLeon Alrae     UHIOpen_WRONLY = 0x1,
683b3c1694SLeon Alrae     UHIOpen_RDWR   = 0x2,
693b3c1694SLeon Alrae     UHIOpen_APPEND = 0x8,
703b3c1694SLeon Alrae     UHIOpen_CREAT  = 0x200,
713b3c1694SLeon Alrae     UHIOpen_TRUNC  = 0x400,
723b3c1694SLeon Alrae     UHIOpen_EXCL   = 0x800
733b3c1694SLeon Alrae };
743b3c1694SLeon Alrae 
752c44b19cSLeon Alrae /* Errno values taken from asm-mips/errno.h */
762c44b19cSLeon Alrae static uint16_t host_to_mips_errno[] = {
772c44b19cSLeon Alrae     [ENAMETOOLONG] = 78,
782c44b19cSLeon Alrae #ifdef EOVERFLOW
792c44b19cSLeon Alrae     [EOVERFLOW]    = 79,
802c44b19cSLeon Alrae #endif
812c44b19cSLeon Alrae #ifdef ELOOP
822c44b19cSLeon Alrae     [ELOOP]        = 90,
832c44b19cSLeon Alrae #endif
842c44b19cSLeon Alrae };
852c44b19cSLeon Alrae 
862c44b19cSLeon Alrae static int errno_mips(int err)
872c44b19cSLeon Alrae {
882c44b19cSLeon Alrae     if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) {
892c44b19cSLeon Alrae         return EINVAL;
902c44b19cSLeon Alrae     } else if (host_to_mips_errno[err]) {
912c44b19cSLeon Alrae         return host_to_mips_errno[err];
922c44b19cSLeon Alrae     } else {
932c44b19cSLeon Alrae         return err;
942c44b19cSLeon Alrae     }
952c44b19cSLeon Alrae }
962c44b19cSLeon Alrae 
973b3c1694SLeon Alrae static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src,
983b3c1694SLeon Alrae                                target_ulong vaddr)
993b3c1694SLeon Alrae {
1003b3c1694SLeon Alrae     hwaddr len = sizeof(struct UHIStat);
1013b3c1694SLeon Alrae     UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
1023b3c1694SLeon Alrae     if (!dst) {
1033b3c1694SLeon Alrae         errno = EFAULT;
1043b3c1694SLeon Alrae         return -1;
1053b3c1694SLeon Alrae     }
1063b3c1694SLeon Alrae 
1073b3c1694SLeon Alrae     dst->uhi_st_dev = tswap16(src->st_dev);
1083b3c1694SLeon Alrae     dst->uhi_st_ino = tswap16(src->st_ino);
1093b3c1694SLeon Alrae     dst->uhi_st_mode = tswap32(src->st_mode);
1103b3c1694SLeon Alrae     dst->uhi_st_nlink = tswap16(src->st_nlink);
1113b3c1694SLeon Alrae     dst->uhi_st_uid = tswap16(src->st_uid);
1123b3c1694SLeon Alrae     dst->uhi_st_gid = tswap16(src->st_gid);
1133b3c1694SLeon Alrae     dst->uhi_st_rdev = tswap16(src->st_rdev);
1143b3c1694SLeon Alrae     dst->uhi_st_size = tswap64(src->st_size);
1153b3c1694SLeon Alrae     dst->uhi_st_atime = tswap64(src->st_atime);
1163b3c1694SLeon Alrae     dst->uhi_st_mtime = tswap64(src->st_mtime);
1173b3c1694SLeon Alrae     dst->uhi_st_ctime = tswap64(src->st_ctime);
1183b3c1694SLeon Alrae #ifdef _WIN32
1193b3c1694SLeon Alrae     dst->uhi_st_blksize = 0;
1203b3c1694SLeon Alrae     dst->uhi_st_blocks = 0;
1213b3c1694SLeon Alrae #else
1223b3c1694SLeon Alrae     dst->uhi_st_blksize = tswap64(src->st_blksize);
1233b3c1694SLeon Alrae     dst->uhi_st_blocks = tswap64(src->st_blocks);
1243b3c1694SLeon Alrae #endif
1253b3c1694SLeon Alrae     unlock_user(dst, vaddr, len);
1263b3c1694SLeon Alrae     return 0;
1273b3c1694SLeon Alrae }
1283b3c1694SLeon Alrae 
1293b3c1694SLeon Alrae static int get_open_flags(target_ulong target_flags)
1303b3c1694SLeon Alrae {
1313b3c1694SLeon Alrae     int open_flags = 0;
1323b3c1694SLeon Alrae 
1333b3c1694SLeon Alrae     if (target_flags & UHIOpen_RDWR) {
1343b3c1694SLeon Alrae         open_flags |= O_RDWR;
1353b3c1694SLeon Alrae     } else if (target_flags & UHIOpen_WRONLY) {
1363b3c1694SLeon Alrae         open_flags |= O_WRONLY;
1373b3c1694SLeon Alrae     } else {
1383b3c1694SLeon Alrae         open_flags |= O_RDONLY;
1393b3c1694SLeon Alrae     }
1403b3c1694SLeon Alrae 
1413b3c1694SLeon Alrae     open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0;
1423b3c1694SLeon Alrae     open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0;
1433b3c1694SLeon Alrae     open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0;
1443b3c1694SLeon Alrae     open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0;
1453b3c1694SLeon Alrae 
1463b3c1694SLeon Alrae     return open_flags;
1473b3c1694SLeon Alrae }
1483b3c1694SLeon Alrae 
1493b3c1694SLeon Alrae static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr,
1503b3c1694SLeon Alrae                          target_ulong len, target_ulong offset)
1513b3c1694SLeon Alrae {
1523b3c1694SLeon Alrae     int num_of_bytes;
1533b3c1694SLeon Alrae     void *dst = lock_user(VERIFY_READ, vaddr, len, 1);
1543b3c1694SLeon Alrae     if (!dst) {
1553b3c1694SLeon Alrae         errno = EFAULT;
1563b3c1694SLeon Alrae         return -1;
1573b3c1694SLeon Alrae     }
1583b3c1694SLeon Alrae 
1593b3c1694SLeon Alrae     if (offset) {
1603b3c1694SLeon Alrae #ifdef _WIN32
1613b3c1694SLeon Alrae         num_of_bytes = 0;
1623b3c1694SLeon Alrae #else
1633b3c1694SLeon Alrae         num_of_bytes = pwrite(fd, dst, len, offset);
1643b3c1694SLeon Alrae #endif
1653b3c1694SLeon Alrae     } else {
1663b3c1694SLeon Alrae         num_of_bytes = write(fd, dst, len);
1673b3c1694SLeon Alrae     }
1683b3c1694SLeon Alrae 
1693b3c1694SLeon Alrae     unlock_user(dst, vaddr, 0);
1703b3c1694SLeon Alrae     return num_of_bytes;
1713b3c1694SLeon Alrae }
1723b3c1694SLeon Alrae 
1733b3c1694SLeon Alrae static int read_from_file(CPUMIPSState *env, target_ulong fd,
1743b3c1694SLeon Alrae                           target_ulong vaddr, target_ulong len,
1753b3c1694SLeon Alrae                           target_ulong offset)
1763b3c1694SLeon Alrae {
1773b3c1694SLeon Alrae     int num_of_bytes;
1783b3c1694SLeon Alrae     void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0);
1793b3c1694SLeon Alrae     if (!dst) {
1803b3c1694SLeon Alrae         errno = EFAULT;
1813b3c1694SLeon Alrae         return -1;
1823b3c1694SLeon Alrae     }
1833b3c1694SLeon Alrae 
1843b3c1694SLeon Alrae     if (offset) {
1853b3c1694SLeon Alrae #ifdef _WIN32
1863b3c1694SLeon Alrae         num_of_bytes = 0;
1873b3c1694SLeon Alrae #else
1883b3c1694SLeon Alrae         num_of_bytes = pread(fd, dst, len, offset);
1893b3c1694SLeon Alrae #endif
1903b3c1694SLeon Alrae     } else {
1913b3c1694SLeon Alrae         num_of_bytes = read(fd, dst, len);
1923b3c1694SLeon Alrae     }
1933b3c1694SLeon Alrae 
1943b3c1694SLeon Alrae     unlock_user(dst, vaddr, len);
1953b3c1694SLeon Alrae     return num_of_bytes;
1963b3c1694SLeon Alrae }
1973b3c1694SLeon Alrae 
1983b3c1694SLeon Alrae static int copy_argn_to_target(CPUMIPSState *env, int arg_num,
1993b3c1694SLeon Alrae                                target_ulong vaddr)
2003b3c1694SLeon Alrae {
2013b3c1694SLeon Alrae     int strsize = strlen(semihosting_get_arg(arg_num)) + 1;
2023b3c1694SLeon Alrae     char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0);
2033b3c1694SLeon Alrae     if (!dst) {
2043b3c1694SLeon Alrae         return -1;
2053b3c1694SLeon Alrae     }
2063b3c1694SLeon Alrae 
2073b3c1694SLeon Alrae     strcpy(dst, semihosting_get_arg(arg_num));
2083b3c1694SLeon Alrae 
2093b3c1694SLeon Alrae     unlock_user(dst, vaddr, strsize);
2103b3c1694SLeon Alrae     return 0;
2113b3c1694SLeon Alrae }
2123b3c1694SLeon Alrae 
2133b3c1694SLeon Alrae #define GET_TARGET_STRING(p, addr)              \
2143b3c1694SLeon Alrae     do {                                        \
2153b3c1694SLeon Alrae         p = lock_user_string(addr);             \
2163b3c1694SLeon Alrae         if (!p) {                               \
2173b3c1694SLeon Alrae             gpr[2] = -1;                        \
2183b3c1694SLeon Alrae             gpr[3] = EFAULT;                    \
2193b3c1694SLeon Alrae             goto uhi_done;                      \
2203b3c1694SLeon Alrae         }                                       \
2213b3c1694SLeon Alrae     } while (0)
2223b3c1694SLeon Alrae 
22326e7e982SLeon Alrae #define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \
22426e7e982SLeon Alrae     do {                                                \
22526e7e982SLeon Alrae         p = lock_user_string(addr);                     \
22626e7e982SLeon Alrae         if (!p) {                                       \
22726e7e982SLeon Alrae             gpr[2] = -1;                                \
22826e7e982SLeon Alrae             gpr[3] = EFAULT;                            \
22926e7e982SLeon Alrae             goto uhi_done;                              \
23026e7e982SLeon Alrae         }                                               \
23126e7e982SLeon Alrae         p2 = lock_user_string(addr2);                   \
23226e7e982SLeon Alrae         if (!p2) {                                      \
23326e7e982SLeon Alrae             unlock_user(p, addr, 0);                    \
23426e7e982SLeon Alrae             gpr[2] = -1;                                \
23526e7e982SLeon Alrae             gpr[3] = EFAULT;                            \
23626e7e982SLeon Alrae             goto uhi_done;                              \
23726e7e982SLeon Alrae         }                                               \
23826e7e982SLeon Alrae     } while (0)
23926e7e982SLeon Alrae 
2403b3c1694SLeon Alrae #define FREE_TARGET_STRING(p, gpr)              \
2413b3c1694SLeon Alrae     do {                                        \
2423b3c1694SLeon Alrae         unlock_user(p, gpr, 0);                 \
2433b3c1694SLeon Alrae     } while (0)
2443b3c1694SLeon Alrae 
2453b3c1694SLeon Alrae void helper_do_semihosting(CPUMIPSState *env)
2463b3c1694SLeon Alrae {
2473b3c1694SLeon Alrae     target_ulong *gpr = env->active_tc.gpr;
2483b3c1694SLeon Alrae     const UHIOp op = gpr[25];
2493b3c1694SLeon Alrae     char *p, *p2;
2503b3c1694SLeon Alrae 
2513b3c1694SLeon Alrae     switch (op) {
2523b3c1694SLeon Alrae     case UHI_exit:
2533b3c1694SLeon Alrae         qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]);
2543b3c1694SLeon Alrae         exit(gpr[4]);
2553b3c1694SLeon Alrae     case UHI_open:
2563b3c1694SLeon Alrae         GET_TARGET_STRING(p, gpr[4]);
2573b3c1694SLeon Alrae         if (!strcmp("/dev/stdin", p)) {
2583b3c1694SLeon Alrae             gpr[2] = 0;
2593b3c1694SLeon Alrae         } else if (!strcmp("/dev/stdout", p)) {
2603b3c1694SLeon Alrae             gpr[2] = 1;
2613b3c1694SLeon Alrae         } else if (!strcmp("/dev/stderr", p)) {
2623b3c1694SLeon Alrae             gpr[2] = 2;
2633b3c1694SLeon Alrae         } else {
2643b3c1694SLeon Alrae             gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]);
2652c44b19cSLeon Alrae             gpr[3] = errno_mips(errno);
2663b3c1694SLeon Alrae         }
2673b3c1694SLeon Alrae         FREE_TARGET_STRING(p, gpr[4]);
2683b3c1694SLeon Alrae         break;
2693b3c1694SLeon Alrae     case UHI_close:
2703b3c1694SLeon Alrae         if (gpr[4] < 3) {
2713b3c1694SLeon Alrae             /* ignore closing stdin/stdout/stderr */
2723b3c1694SLeon Alrae             gpr[2] = 0;
2733b3c1694SLeon Alrae             goto uhi_done;
2743b3c1694SLeon Alrae         }
2753b3c1694SLeon Alrae         gpr[2] = close(gpr[4]);
2762c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
2773b3c1694SLeon Alrae         break;
2783b3c1694SLeon Alrae     case UHI_read:
2793b3c1694SLeon Alrae         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0);
2802c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
2813b3c1694SLeon Alrae         break;
2823b3c1694SLeon Alrae     case UHI_write:
2833b3c1694SLeon Alrae         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0);
2842c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
2853b3c1694SLeon Alrae         break;
2863b3c1694SLeon Alrae     case UHI_lseek:
2873b3c1694SLeon Alrae         gpr[2] = lseek(gpr[4], gpr[5], gpr[6]);
2882c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
2893b3c1694SLeon Alrae         break;
2903b3c1694SLeon Alrae     case UHI_unlink:
2913b3c1694SLeon Alrae         GET_TARGET_STRING(p, gpr[4]);
2923b3c1694SLeon Alrae         gpr[2] = remove(p);
2932c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
2943b3c1694SLeon Alrae         FREE_TARGET_STRING(p, gpr[4]);
2953b3c1694SLeon Alrae         break;
2963b3c1694SLeon Alrae     case UHI_fstat:
2973b3c1694SLeon Alrae         {
2983b3c1694SLeon Alrae             struct stat sbuf;
2993b3c1694SLeon Alrae             memset(&sbuf, 0, sizeof(sbuf));
3003b3c1694SLeon Alrae             gpr[2] = fstat(gpr[4], &sbuf);
3012c44b19cSLeon Alrae             gpr[3] = errno_mips(errno);
3023b3c1694SLeon Alrae             if (gpr[2]) {
3033b3c1694SLeon Alrae                 goto uhi_done;
3043b3c1694SLeon Alrae             }
3053b3c1694SLeon Alrae             gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]);
3062c44b19cSLeon Alrae             gpr[3] = errno_mips(errno);
3073b3c1694SLeon Alrae         }
3083b3c1694SLeon Alrae         break;
3093b3c1694SLeon Alrae     case UHI_argc:
3103b3c1694SLeon Alrae         gpr[2] = semihosting_get_argc();
3113b3c1694SLeon Alrae         break;
3123b3c1694SLeon Alrae     case UHI_argnlen:
3133b3c1694SLeon Alrae         if (gpr[4] >= semihosting_get_argc()) {
3143b3c1694SLeon Alrae             gpr[2] = -1;
3153b3c1694SLeon Alrae             goto uhi_done;
3163b3c1694SLeon Alrae         }
3173b3c1694SLeon Alrae         gpr[2] = strlen(semihosting_get_arg(gpr[4]));
3183b3c1694SLeon Alrae         break;
3193b3c1694SLeon Alrae     case UHI_argn:
3203b3c1694SLeon Alrae         if (gpr[4] >= semihosting_get_argc()) {
3213b3c1694SLeon Alrae             gpr[2] = -1;
3223b3c1694SLeon Alrae             goto uhi_done;
3233b3c1694SLeon Alrae         }
3243b3c1694SLeon Alrae         gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]);
3253b3c1694SLeon Alrae         break;
3263b3c1694SLeon Alrae     case UHI_plog:
3273b3c1694SLeon Alrae         GET_TARGET_STRING(p, gpr[4]);
3283b3c1694SLeon Alrae         p2 = strstr(p, "%d");
3293b3c1694SLeon Alrae         if (p2) {
3303b3c1694SLeon Alrae             int char_num = p2 - p;
3313b3c1694SLeon Alrae             char *buf = g_malloc(char_num + 1);
3323b3c1694SLeon Alrae             strncpy(buf, p, char_num);
3333b3c1694SLeon Alrae             buf[char_num] = '\0';
3343b3c1694SLeon Alrae             gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2);
3353b3c1694SLeon Alrae             g_free(buf);
3363b3c1694SLeon Alrae         } else {
3373b3c1694SLeon Alrae             gpr[2] = printf("%s", p);
3383b3c1694SLeon Alrae         }
3393b3c1694SLeon Alrae         FREE_TARGET_STRING(p, gpr[4]);
3403b3c1694SLeon Alrae         break;
3413b3c1694SLeon Alrae     case UHI_assert:
34226e7e982SLeon Alrae         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
3433b3c1694SLeon Alrae         printf("assertion '");
3443b3c1694SLeon Alrae         printf("\"%s\"", p);
3453b3c1694SLeon Alrae         printf("': file \"%s\", line %d\n", p2, (int)gpr[6]);
3463b3c1694SLeon Alrae         FREE_TARGET_STRING(p2, gpr[5]);
3473b3c1694SLeon Alrae         FREE_TARGET_STRING(p, gpr[4]);
3483b3c1694SLeon Alrae         abort();
3493b3c1694SLeon Alrae         break;
3503b3c1694SLeon Alrae     case UHI_pread:
3513b3c1694SLeon Alrae         gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
3522c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
3533b3c1694SLeon Alrae         break;
3543b3c1694SLeon Alrae     case UHI_pwrite:
3553b3c1694SLeon Alrae         gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]);
3562c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
3573b3c1694SLeon Alrae         break;
3583b3c1694SLeon Alrae #ifndef _WIN32
3593b3c1694SLeon Alrae     case UHI_link:
36026e7e982SLeon Alrae         GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]);
3613b3c1694SLeon Alrae         gpr[2] = link(p, p2);
3622c44b19cSLeon Alrae         gpr[3] = errno_mips(errno);
3633b3c1694SLeon Alrae         FREE_TARGET_STRING(p2, gpr[5]);
3643b3c1694SLeon Alrae         FREE_TARGET_STRING(p, gpr[4]);
3653b3c1694SLeon Alrae         break;
3663b3c1694SLeon Alrae #endif
3673b3c1694SLeon Alrae     default:
3683b3c1694SLeon Alrae         fprintf(stderr, "Unknown UHI operation %d\n", op);
3693b3c1694SLeon Alrae         abort();
3703b3c1694SLeon Alrae     }
3713b3c1694SLeon Alrae uhi_done:
3723b3c1694SLeon Alrae     return;
3733b3c1694SLeon Alrae }
374