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 989975214SChetan Pant * version 2.1 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 20c684822aSPeter Maydell #include "qemu/osdep.h" 213b3c1694SLeon Alrae #include "cpu.h" 2263c91552SPaolo Bonzini #include "qemu/log.h" 23c89a14adSRichard Henderson #include "semihosting/softmmu-uaccess.h" 246b5fe137SPhilippe Mathieu-Daudé #include "semihosting/semihost.h" 256b5fe137SPhilippe Mathieu-Daudé #include "semihosting/console.h" 26*8ec7e3c5SRichard Henderson #include "internal.h" 273b3c1694SLeon Alrae 283b3c1694SLeon Alrae typedef enum UHIOp { 293b3c1694SLeon Alrae UHI_exit = 1, 303b3c1694SLeon Alrae UHI_open = 2, 313b3c1694SLeon Alrae UHI_close = 3, 323b3c1694SLeon Alrae UHI_read = 4, 333b3c1694SLeon Alrae UHI_write = 5, 343b3c1694SLeon Alrae UHI_lseek = 6, 353b3c1694SLeon Alrae UHI_unlink = 7, 363b3c1694SLeon Alrae UHI_fstat = 8, 373b3c1694SLeon Alrae UHI_argc = 9, 383b3c1694SLeon Alrae UHI_argnlen = 10, 393b3c1694SLeon Alrae UHI_argn = 11, 403b3c1694SLeon Alrae UHI_plog = 13, 413b3c1694SLeon Alrae UHI_assert = 14, 423b3c1694SLeon Alrae UHI_pread = 19, 433b3c1694SLeon Alrae UHI_pwrite = 20, 443b3c1694SLeon Alrae UHI_link = 22 453b3c1694SLeon Alrae } UHIOp; 463b3c1694SLeon Alrae 473b3c1694SLeon Alrae typedef struct UHIStat { 483b3c1694SLeon Alrae int16_t uhi_st_dev; 493b3c1694SLeon Alrae uint16_t uhi_st_ino; 503b3c1694SLeon Alrae uint32_t uhi_st_mode; 513b3c1694SLeon Alrae uint16_t uhi_st_nlink; 523b3c1694SLeon Alrae uint16_t uhi_st_uid; 533b3c1694SLeon Alrae uint16_t uhi_st_gid; 543b3c1694SLeon Alrae int16_t uhi_st_rdev; 553b3c1694SLeon Alrae uint64_t uhi_st_size; 563b3c1694SLeon Alrae uint64_t uhi_st_atime; 573b3c1694SLeon Alrae uint64_t uhi_st_spare1; 583b3c1694SLeon Alrae uint64_t uhi_st_mtime; 593b3c1694SLeon Alrae uint64_t uhi_st_spare2; 603b3c1694SLeon Alrae uint64_t uhi_st_ctime; 613b3c1694SLeon Alrae uint64_t uhi_st_spare3; 623b3c1694SLeon Alrae uint64_t uhi_st_blksize; 633b3c1694SLeon Alrae uint64_t uhi_st_blocks; 643b3c1694SLeon Alrae uint64_t uhi_st_spare4[2]; 653b3c1694SLeon Alrae } UHIStat; 663b3c1694SLeon Alrae 673b3c1694SLeon Alrae enum UHIOpenFlags { 683b3c1694SLeon Alrae UHIOpen_RDONLY = 0x0, 693b3c1694SLeon Alrae UHIOpen_WRONLY = 0x1, 703b3c1694SLeon Alrae UHIOpen_RDWR = 0x2, 713b3c1694SLeon Alrae UHIOpen_APPEND = 0x8, 723b3c1694SLeon Alrae UHIOpen_CREAT = 0x200, 733b3c1694SLeon Alrae UHIOpen_TRUNC = 0x400, 743b3c1694SLeon Alrae UHIOpen_EXCL = 0x800 753b3c1694SLeon Alrae }; 763b3c1694SLeon Alrae 77d859a77dSPhilippe Mathieu-Daudé static int errno_mips(int host_errno) 78d859a77dSPhilippe Mathieu-Daudé { 792c44b19cSLeon Alrae /* Errno values taken from asm-mips/errno.h */ 80d859a77dSPhilippe Mathieu-Daudé switch (host_errno) { 81d859a77dSPhilippe Mathieu-Daudé case 0: return 0; 82d859a77dSPhilippe Mathieu-Daudé case ENAMETOOLONG: return 78; 832c44b19cSLeon Alrae #ifdef EOVERFLOW 84d859a77dSPhilippe Mathieu-Daudé case EOVERFLOW: return 79; 852c44b19cSLeon Alrae #endif 862c44b19cSLeon Alrae #ifdef ELOOP 87d859a77dSPhilippe Mathieu-Daudé case ELOOP: return 90; 882c44b19cSLeon Alrae #endif 89d859a77dSPhilippe Mathieu-Daudé default: return EINVAL; 902c44b19cSLeon Alrae } 912c44b19cSLeon Alrae } 922c44b19cSLeon Alrae 933b3c1694SLeon Alrae static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, 943b3c1694SLeon Alrae target_ulong vaddr) 953b3c1694SLeon Alrae { 963b3c1694SLeon Alrae hwaddr len = sizeof(struct UHIStat); 973b3c1694SLeon Alrae UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); 983b3c1694SLeon Alrae if (!dst) { 993b3c1694SLeon Alrae errno = EFAULT; 1003b3c1694SLeon Alrae return -1; 1013b3c1694SLeon Alrae } 1023b3c1694SLeon Alrae 1033b3c1694SLeon Alrae dst->uhi_st_dev = tswap16(src->st_dev); 1043b3c1694SLeon Alrae dst->uhi_st_ino = tswap16(src->st_ino); 1053b3c1694SLeon Alrae dst->uhi_st_mode = tswap32(src->st_mode); 1063b3c1694SLeon Alrae dst->uhi_st_nlink = tswap16(src->st_nlink); 1073b3c1694SLeon Alrae dst->uhi_st_uid = tswap16(src->st_uid); 1083b3c1694SLeon Alrae dst->uhi_st_gid = tswap16(src->st_gid); 1093b3c1694SLeon Alrae dst->uhi_st_rdev = tswap16(src->st_rdev); 1103b3c1694SLeon Alrae dst->uhi_st_size = tswap64(src->st_size); 1113b3c1694SLeon Alrae dst->uhi_st_atime = tswap64(src->st_atime); 1123b3c1694SLeon Alrae dst->uhi_st_mtime = tswap64(src->st_mtime); 1133b3c1694SLeon Alrae dst->uhi_st_ctime = tswap64(src->st_ctime); 1143b3c1694SLeon Alrae #ifdef _WIN32 1153b3c1694SLeon Alrae dst->uhi_st_blksize = 0; 1163b3c1694SLeon Alrae dst->uhi_st_blocks = 0; 1173b3c1694SLeon Alrae #else 1183b3c1694SLeon Alrae dst->uhi_st_blksize = tswap64(src->st_blksize); 1193b3c1694SLeon Alrae dst->uhi_st_blocks = tswap64(src->st_blocks); 1203b3c1694SLeon Alrae #endif 1213b3c1694SLeon Alrae unlock_user(dst, vaddr, len); 1223b3c1694SLeon Alrae return 0; 1233b3c1694SLeon Alrae } 1243b3c1694SLeon Alrae 1253b3c1694SLeon Alrae static int get_open_flags(target_ulong target_flags) 1263b3c1694SLeon Alrae { 1273b3c1694SLeon Alrae int open_flags = 0; 1283b3c1694SLeon Alrae 1293b3c1694SLeon Alrae if (target_flags & UHIOpen_RDWR) { 1303b3c1694SLeon Alrae open_flags |= O_RDWR; 1313b3c1694SLeon Alrae } else if (target_flags & UHIOpen_WRONLY) { 1323b3c1694SLeon Alrae open_flags |= O_WRONLY; 1333b3c1694SLeon Alrae } else { 1343b3c1694SLeon Alrae open_flags |= O_RDONLY; 1353b3c1694SLeon Alrae } 1363b3c1694SLeon Alrae 1373b3c1694SLeon Alrae open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; 1383b3c1694SLeon Alrae open_flags |= (target_flags & UHIOpen_CREAT) ? O_CREAT : 0; 1393b3c1694SLeon Alrae open_flags |= (target_flags & UHIOpen_TRUNC) ? O_TRUNC : 0; 1403b3c1694SLeon Alrae open_flags |= (target_flags & UHIOpen_EXCL) ? O_EXCL : 0; 1413b3c1694SLeon Alrae 1423b3c1694SLeon Alrae return open_flags; 1433b3c1694SLeon Alrae } 1443b3c1694SLeon Alrae 1453b3c1694SLeon Alrae static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, 1463b3c1694SLeon Alrae target_ulong len, target_ulong offset) 1473b3c1694SLeon Alrae { 1483b3c1694SLeon Alrae int num_of_bytes; 1493b3c1694SLeon Alrae void *dst = lock_user(VERIFY_READ, vaddr, len, 1); 1503b3c1694SLeon Alrae if (!dst) { 1513b3c1694SLeon Alrae errno = EFAULT; 1523b3c1694SLeon Alrae return -1; 1533b3c1694SLeon Alrae } 1543b3c1694SLeon Alrae 1553b3c1694SLeon Alrae if (offset) { 1563b3c1694SLeon Alrae #ifdef _WIN32 1573b3c1694SLeon Alrae num_of_bytes = 0; 1583b3c1694SLeon Alrae #else 1593b3c1694SLeon Alrae num_of_bytes = pwrite(fd, dst, len, offset); 1603b3c1694SLeon Alrae #endif 1613b3c1694SLeon Alrae } else { 1623b3c1694SLeon Alrae num_of_bytes = write(fd, dst, len); 1633b3c1694SLeon Alrae } 1643b3c1694SLeon Alrae 1653b3c1694SLeon Alrae unlock_user(dst, vaddr, 0); 1663b3c1694SLeon Alrae return num_of_bytes; 1673b3c1694SLeon Alrae } 1683b3c1694SLeon Alrae 1693b3c1694SLeon Alrae static int read_from_file(CPUMIPSState *env, target_ulong fd, 1703b3c1694SLeon Alrae target_ulong vaddr, target_ulong len, 1713b3c1694SLeon Alrae target_ulong offset) 1723b3c1694SLeon Alrae { 1733b3c1694SLeon Alrae int num_of_bytes; 1743b3c1694SLeon Alrae void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); 1753b3c1694SLeon Alrae if (!dst) { 1763b3c1694SLeon Alrae errno = EFAULT; 1773b3c1694SLeon Alrae return -1; 1783b3c1694SLeon Alrae } 1793b3c1694SLeon Alrae 1803b3c1694SLeon Alrae if (offset) { 1813b3c1694SLeon Alrae #ifdef _WIN32 1823b3c1694SLeon Alrae num_of_bytes = 0; 1833b3c1694SLeon Alrae #else 1843b3c1694SLeon Alrae num_of_bytes = pread(fd, dst, len, offset); 1853b3c1694SLeon Alrae #endif 1863b3c1694SLeon Alrae } else { 1873b3c1694SLeon Alrae num_of_bytes = read(fd, dst, len); 1883b3c1694SLeon Alrae } 1893b3c1694SLeon Alrae 1903b3c1694SLeon Alrae unlock_user(dst, vaddr, len); 1913b3c1694SLeon Alrae return num_of_bytes; 1923b3c1694SLeon Alrae } 1933b3c1694SLeon Alrae 1943b3c1694SLeon Alrae static int copy_argn_to_target(CPUMIPSState *env, int arg_num, 1953b3c1694SLeon Alrae target_ulong vaddr) 1963b3c1694SLeon Alrae { 1973b3c1694SLeon Alrae int strsize = strlen(semihosting_get_arg(arg_num)) + 1; 1983b3c1694SLeon Alrae char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); 1993b3c1694SLeon Alrae if (!dst) { 2003b3c1694SLeon Alrae return -1; 2013b3c1694SLeon Alrae } 2023b3c1694SLeon Alrae 2033b3c1694SLeon Alrae strcpy(dst, semihosting_get_arg(arg_num)); 2043b3c1694SLeon Alrae 2053b3c1694SLeon Alrae unlock_user(dst, vaddr, strsize); 2063b3c1694SLeon Alrae return 0; 2073b3c1694SLeon Alrae } 2083b3c1694SLeon Alrae 2093b3c1694SLeon Alrae #define GET_TARGET_STRING(p, addr) \ 2103b3c1694SLeon Alrae do { \ 2113b3c1694SLeon Alrae p = lock_user_string(addr); \ 2123b3c1694SLeon Alrae if (!p) { \ 2133b3c1694SLeon Alrae gpr[2] = -1; \ 2143b3c1694SLeon Alrae gpr[3] = EFAULT; \ 21554fc33fdSDaniel Henrique Barboza return; \ 2163b3c1694SLeon Alrae } \ 2173b3c1694SLeon Alrae } while (0) 2183b3c1694SLeon Alrae 21926e7e982SLeon Alrae #define GET_TARGET_STRINGS_2(p, addr, p2, addr2) \ 22026e7e982SLeon Alrae do { \ 22126e7e982SLeon Alrae p = lock_user_string(addr); \ 22226e7e982SLeon Alrae if (!p) { \ 22326e7e982SLeon Alrae gpr[2] = -1; \ 22426e7e982SLeon Alrae gpr[3] = EFAULT; \ 22554fc33fdSDaniel Henrique Barboza return; \ 22626e7e982SLeon Alrae } \ 22726e7e982SLeon Alrae p2 = lock_user_string(addr2); \ 22826e7e982SLeon Alrae if (!p2) { \ 22926e7e982SLeon Alrae unlock_user(p, addr, 0); \ 23026e7e982SLeon Alrae gpr[2] = -1; \ 23126e7e982SLeon Alrae gpr[3] = EFAULT; \ 23254fc33fdSDaniel Henrique Barboza return; \ 23326e7e982SLeon Alrae } \ 23426e7e982SLeon Alrae } while (0) 23526e7e982SLeon Alrae 2363b3c1694SLeon Alrae #define FREE_TARGET_STRING(p, gpr) \ 2373b3c1694SLeon Alrae do { \ 2383b3c1694SLeon Alrae unlock_user(p, gpr, 0); \ 2393b3c1694SLeon Alrae } while (0) 2403b3c1694SLeon Alrae 241*8ec7e3c5SRichard Henderson void mips_semihosting(CPUMIPSState *env) 2423b3c1694SLeon Alrae { 2433b3c1694SLeon Alrae target_ulong *gpr = env->active_tc.gpr; 2443b3c1694SLeon Alrae const UHIOp op = gpr[25]; 2453b3c1694SLeon Alrae char *p, *p2; 2463b3c1694SLeon Alrae 2473b3c1694SLeon Alrae switch (op) { 2483b3c1694SLeon Alrae case UHI_exit: 2493b3c1694SLeon Alrae qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); 2503b3c1694SLeon Alrae exit(gpr[4]); 2513b3c1694SLeon Alrae case UHI_open: 2523b3c1694SLeon Alrae GET_TARGET_STRING(p, gpr[4]); 2533b3c1694SLeon Alrae if (!strcmp("/dev/stdin", p)) { 2543b3c1694SLeon Alrae gpr[2] = 0; 2553b3c1694SLeon Alrae } else if (!strcmp("/dev/stdout", p)) { 2563b3c1694SLeon Alrae gpr[2] = 1; 2573b3c1694SLeon Alrae } else if (!strcmp("/dev/stderr", p)) { 2583b3c1694SLeon Alrae gpr[2] = 2; 2593b3c1694SLeon Alrae } else { 2603b3c1694SLeon Alrae gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); 2612c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2623b3c1694SLeon Alrae } 2633b3c1694SLeon Alrae FREE_TARGET_STRING(p, gpr[4]); 2643b3c1694SLeon Alrae break; 2653b3c1694SLeon Alrae case UHI_close: 2663b3c1694SLeon Alrae if (gpr[4] < 3) { 2673b3c1694SLeon Alrae /* ignore closing stdin/stdout/stderr */ 2683b3c1694SLeon Alrae gpr[2] = 0; 26954fc33fdSDaniel Henrique Barboza return; 2703b3c1694SLeon Alrae } 2713b3c1694SLeon Alrae gpr[2] = close(gpr[4]); 2722c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2733b3c1694SLeon Alrae break; 2743b3c1694SLeon Alrae case UHI_read: 2753b3c1694SLeon Alrae gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); 2762c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2773b3c1694SLeon Alrae break; 2783b3c1694SLeon Alrae case UHI_write: 2793b3c1694SLeon Alrae gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); 2802c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2813b3c1694SLeon Alrae break; 2823b3c1694SLeon Alrae case UHI_lseek: 2833b3c1694SLeon Alrae gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); 2842c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2853b3c1694SLeon Alrae break; 2863b3c1694SLeon Alrae case UHI_unlink: 2873b3c1694SLeon Alrae GET_TARGET_STRING(p, gpr[4]); 2883b3c1694SLeon Alrae gpr[2] = remove(p); 2892c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2903b3c1694SLeon Alrae FREE_TARGET_STRING(p, gpr[4]); 2913b3c1694SLeon Alrae break; 2923b3c1694SLeon Alrae case UHI_fstat: 2933b3c1694SLeon Alrae { 2943b3c1694SLeon Alrae struct stat sbuf; 2953b3c1694SLeon Alrae memset(&sbuf, 0, sizeof(sbuf)); 2963b3c1694SLeon Alrae gpr[2] = fstat(gpr[4], &sbuf); 2972c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 2983b3c1694SLeon Alrae if (gpr[2]) { 29954fc33fdSDaniel Henrique Barboza return; 3003b3c1694SLeon Alrae } 3013b3c1694SLeon Alrae gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); 3022c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 3033b3c1694SLeon Alrae } 3043b3c1694SLeon Alrae break; 3053b3c1694SLeon Alrae case UHI_argc: 3063b3c1694SLeon Alrae gpr[2] = semihosting_get_argc(); 3073b3c1694SLeon Alrae break; 3083b3c1694SLeon Alrae case UHI_argnlen: 3093b3c1694SLeon Alrae if (gpr[4] >= semihosting_get_argc()) { 3103b3c1694SLeon Alrae gpr[2] = -1; 31154fc33fdSDaniel Henrique Barboza return; 3123b3c1694SLeon Alrae } 3133b3c1694SLeon Alrae gpr[2] = strlen(semihosting_get_arg(gpr[4])); 3143b3c1694SLeon Alrae break; 3153b3c1694SLeon Alrae case UHI_argn: 3163b3c1694SLeon Alrae if (gpr[4] >= semihosting_get_argc()) { 3173b3c1694SLeon Alrae gpr[2] = -1; 31854fc33fdSDaniel Henrique Barboza return; 3193b3c1694SLeon Alrae } 3203b3c1694SLeon Alrae gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); 3213b3c1694SLeon Alrae break; 3223b3c1694SLeon Alrae case UHI_plog: 3233b3c1694SLeon Alrae GET_TARGET_STRING(p, gpr[4]); 3243b3c1694SLeon Alrae p2 = strstr(p, "%d"); 3253b3c1694SLeon Alrae if (p2) { 3263b3c1694SLeon Alrae int char_num = p2 - p; 32754eb6cdaSAlex Bennée GString *s = g_string_new_len(p, char_num); 32854eb6cdaSAlex Bennée g_string_append_printf(s, "%d%s", (int)gpr[5], p2 + 2); 32954eb6cdaSAlex Bennée gpr[2] = qemu_semihosting_log_out(s->str, s->len); 33054eb6cdaSAlex Bennée g_string_free(s, true); 3313b3c1694SLeon Alrae } else { 33254eb6cdaSAlex Bennée gpr[2] = qemu_semihosting_log_out(p, strlen(p)); 3333b3c1694SLeon Alrae } 3343b3c1694SLeon Alrae FREE_TARGET_STRING(p, gpr[4]); 3353b3c1694SLeon Alrae break; 3363b3c1694SLeon Alrae case UHI_assert: 33726e7e982SLeon Alrae GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); 3383b3c1694SLeon Alrae printf("assertion '"); 3393b3c1694SLeon Alrae printf("\"%s\"", p); 3403b3c1694SLeon Alrae printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); 3413b3c1694SLeon Alrae FREE_TARGET_STRING(p2, gpr[5]); 3423b3c1694SLeon Alrae FREE_TARGET_STRING(p, gpr[4]); 3433b3c1694SLeon Alrae abort(); 3443b3c1694SLeon Alrae break; 3453b3c1694SLeon Alrae case UHI_pread: 3463b3c1694SLeon Alrae gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); 3472c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 3483b3c1694SLeon Alrae break; 3493b3c1694SLeon Alrae case UHI_pwrite: 3503b3c1694SLeon Alrae gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); 3512c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 3523b3c1694SLeon Alrae break; 3533b3c1694SLeon Alrae #ifndef _WIN32 3543b3c1694SLeon Alrae case UHI_link: 35526e7e982SLeon Alrae GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); 3563b3c1694SLeon Alrae gpr[2] = link(p, p2); 3572c44b19cSLeon Alrae gpr[3] = errno_mips(errno); 3583b3c1694SLeon Alrae FREE_TARGET_STRING(p2, gpr[5]); 3593b3c1694SLeon Alrae FREE_TARGET_STRING(p, gpr[4]); 3603b3c1694SLeon Alrae break; 3613b3c1694SLeon Alrae #endif 3623b3c1694SLeon Alrae default: 3633b3c1694SLeon Alrae fprintf(stderr, "Unknown UHI operation %d\n", op); 3643b3c1694SLeon Alrae abort(); 3653b3c1694SLeon Alrae } 3663b3c1694SLeon Alrae return; 3673b3c1694SLeon Alrae } 368