Add new command line option "-semihosting-arg". It is used for passing input arguments to the guest in semihosting mode. The option can be used multiple times. If n arguments are passed, then argument count (semihosting_argc) will be equal to n+1 as semihosting_argv[0] points at the program name. However, if no arguments are passed then argument count will be 0.
Also tweak Malta's pseudo-bootloader. On CPU reset the $4 register is set to -1 when semihosting is enabled in order to indicate that the UHI operations should be used to obtain input arguments. Signed-off-by: Leon Alrae <leon.al...@imgtec.com> --- hw/mips/mips_malta.c | 8 +++++++- include/sysemu/sysemu.h | 2 ++ qemu-options.hx | 8 ++++++++ target-mips/mips-semi.c | 38 +++++++++++++++++++++++++++++++++++++- target-mips/translate.c | 7 +++++++ vl.c | 28 ++++++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 2 deletions(-) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 5845158..2dfe964 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -634,7 +634,13 @@ static void write_bootloader (CPUMIPSState *env, uint8_t *base, /* Second part of the bootloader */ p = (uint32_t *) (base + 0x580); - stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + + if (semihosting_enabled) { + /* Preserve a0 content when semihosting is enabled. */ + stl_p(p++, 0x00000000); /* nop */ + } else { + stl_p(p++, 0x24040002); /* addiu a0, zero, 2 */ + } stl_p(p++, 0x3c1d0000 | (((ENVP_ADDR - 64) >> 16) & 0xffff)); /* lui sp, high(ENVP_ADDR) */ stl_p(p++, 0x37bd0000 | ((ENVP_ADDR - 64) & 0xffff)); /* ori sp, sp, low(ENVP_ADDR) */ stl_p(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 1ab7063..7d63da2 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -125,6 +125,8 @@ extern int graphic_rotate; extern int no_quit; extern int no_shutdown; extern int semihosting_enabled; +extern const char **semihosting_argv; +extern int semihosting_argc; extern int old_param; extern int boot_menu; extern bool boot_strict; diff --git a/qemu-options.hx b/qemu-options.hx index 99ad1ae..bd058d0 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3240,6 +3240,14 @@ Enable semihosting and define where the semihosting calls will be addressed, to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means @code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only). ETEXI +DEF("semihosting-arg", HAS_ARG, QEMU_OPTION_semihosting_arg, + "-semihosting-arg arguments passed to the guest program\n", + QEMU_ARCH_MIPS) +STEXI +@item -semihosting-arg +@findex -semihosting-arg +Arguments passed to the guest program (MIPS only). +ETEXI DEF("old-param", 0, QEMU_OPTION_old_param, "-old-param old param mode\n", QEMU_ARCH_ARM) STEXI diff --git a/target-mips/mips-semi.c b/target-mips/mips-semi.c index 3bf7b2a..63f2700 100644 --- a/target-mips/mips-semi.c +++ b/target-mips/mips-semi.c @@ -21,6 +21,9 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/softmmu-semi.h" +#ifndef CONFIG_USER_ONLY +#include "sysemu/sysemu.h" +#endif typedef enum UHIOp { UHI_exit = 1, @@ -71,6 +74,12 @@ enum UHIOpenFlags { UHIOpen_EXCL = 0x800 }; +#ifdef CONFIG_USER_ONLY +/* Suppress compiler errors in linux-user. */ +static const char **semihosting_argv; +static int semihosting_argc; +#endif + static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, target_ulong vaddr) { @@ -169,6 +178,21 @@ static int read_from_file(CPUMIPSState *env, target_ulong fd, return num_of_bytes; } +static int copy_argn_to_target(CPUMIPSState *env, int arg_num, + target_ulong vaddr) +{ + int strsize = strlen(semihosting_argv[arg_num]) + 1; + char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); + if (!dst) { + return -1; + } + + strcpy(dst, semihosting_argv[arg_num]); + + unlock_user(dst, vaddr, strsize); + return 0; +} + #define GET_TARGET_STRING(p, addr) \ do { \ p = lock_user_string(addr); \ @@ -248,9 +272,21 @@ void helper_do_semihosting(CPUMIPSState *env) } break; case UHI_argc: + gpr[2] = semihosting_argc; + break; case UHI_argnlen: + if (gpr[4] >= semihosting_argc) { + gpr[2] = -1; + goto uhi_done; + } + gpr[2] = strlen(semihosting_argv[gpr[4]]); + break; case UHI_argn: - /* TODO */ + if (gpr[4] >= semihosting_argc) { + gpr[2] = -1; + goto uhi_done; + } + gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); break; case UHI_plog: GET_TARGET_STRING(p, gpr[4]); diff --git a/target-mips/translate.c b/target-mips/translate.c index 82fa5a4..678c3d5 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -19652,6 +19652,13 @@ void cpu_state_reset(CPUMIPSState *env) restore_rounding_mode(env); restore_flush_mode(env); cs->exception_index = EXCP_NONE; + +#ifndef CONFIG_USER_ONLY + if (semihosting_enabled) { + /* When $4 is -1 the UHI interface will be used for argc and argv */ + env->active_tc.gpr[4] = -1; + } +#endif } void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, int pc_pos) diff --git a/vl.c b/vl.c index e1ffd0a..b2d3422 100644 --- a/vl.c +++ b/vl.c @@ -169,6 +169,8 @@ const char *watchdog; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; int semihosting_enabled = 0; +const char **semihosting_argv; +int semihosting_argc; int old_param = 0; const char *qemu_name; int alt_grab = 0; @@ -3560,6 +3562,22 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_semihosting_arg: + if (semihosting_argc == 0) { + /* If arguments are present then the first argument goes to + argv[1] as argv[0] is reserved for the program name */ + semihosting_argc = 2; + semihosting_argv = + g_malloc(semihosting_argc * sizeof(void *)); + semihosting_argv[1] = optarg; + } else { + semihosting_argc++; + semihosting_argv = + g_realloc(semihosting_argv, + semihosting_argc * sizeof(void *)); + semihosting_argv[semihosting_argc - 1] = optarg; + } + break; case QEMU_OPTION_tdf: fprintf(stderr, "Warning: user space PIT time drift fix " "is no longer supported.\n"); @@ -4078,6 +4096,16 @@ int main(int argc, char **argv, char **envp) current_machine->kernel_cmdline = (char *)kernel_cmdline; } + if (semihosting_argc) { + if (kernel_filename) { + semihosting_argv[0] = kernel_filename; + } else if (bios_name) { + semihosting_argv[0] = bios_name; + } else { + semihosting_argv[0] = ""; + } + } + linux_boot = (kernel_filename != NULL); if (!linux_boot && *kernel_cmdline != '\0') { -- 2.1.0