[PATCH 10/12] target-info: add -target and program-name selection
Yonggang Luo <[email protected]>
| Newsgroups | org.nongnu.qemu-devel,org.nongnu.qemu-arm,org.nongnu.qemu-riscv |
|---|---|
| Message-ID | <[email protected]> |
- Parse -target before TargetInfo is fixed; CLI overrides the program basename. - Tokens are target_name or target_name-be / -le; -target help lists registered names in sorted order. - When -target is omitted, infer the token from g_get_prgname(): strip .exe, take the longest dash-separated registered suffix, and retry without a trailing w for Windows GUI binaries. - A basename that matches no registered token falls through to the unique default. Combined binaries with no suffix require -target when more than one default TargetInfo is registered. Signed-off-by: Yonggang Luo <[email protected]> --- include/qemu/target-info-qom.h | 10 +- qemu-options.hx | 13 +++ system/vl.c | 28 ++++-- target-info-qom.c | 167 ++++++++++++++++++++++++++++++--- tests/qtest/fuzz/fuzz.c | 2 +- 5 files changed, 197 insertions(+), 23 deletions(-) diff --git a/include/qemu/target-info-qom.h b/include/qemu/target-info-qom.h index 842cf241e0a..3b59cf82d65 100644 --- a/include/qemu/target-info-qom.h +++ b/include/qemu/target-info-qom.h @@ -40,7 +40,15 @@ typedef struct TargetInfoQomClass { OBJECT_DECLARE_TYPE(TargetInfoQom, TargetInfoQomClass, TARGET_INFO) -void target_info_qom_set_target(void); +/** + * target_info_qom_set_target: + * @name: -target token, or NULL/%empty to infer from the program + * basename or the unique default + * + * Tokens are target_name (arch default endian) or target_name-be / + * target_name-le. -target overrides the program basename. + */ +void target_info_qom_set_target(const char *name); /** * get_machine_types_available: diff --git a/qemu-options.hx b/qemu-options.hx index 34970fffc94..638b531aa49 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -223,6 +223,19 @@ SRST as -machine. ERST +DEF("target", HAS_ARG, QEMU_OPTION_target, + "-target name select target ('-target help' for list)\n", QEMU_ARCH_ALL) +SRST +``-target name`` + Select the emulation target (``-target help`` for the list). The + default endian is the architecture default (``riscv64`` is little + endian). The other endian uses a ``-be`` or ``-le`` suffix + (``riscv64-be``, ``ppc64-le``). If omitted, a matching suffix of + the program basename is used (``qemu-system-riscv64-be``, and + ``qemu-system-aarch64w.exe`` on Windows). ``-target`` overrides + the program name. +ERST + DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, "-cpu cpu select CPU ('-cpu help' for list)\n", QEMU_ARCH_ALL) SRST diff --git a/system/vl.c b/system/vl.c index 83b9425de9b..405f434e036 100644 --- a/system/vl.c +++ b/system/vl.c @@ -166,6 +166,7 @@ typedef struct DeviceOption { QTAILQ_ENTRY(DeviceOption) next; } DeviceOption; +static const char *target_option; static const char *cpu_option; static const char *mem_path; static const char *incoming; @@ -2899,19 +2900,12 @@ void qemu_init(int argc, char **argv) os_setup_limits(); - module_call_init(MODULE_INIT_TARGET_INFO); - target_info_qom_set_target(); - - module_init_info(qemu_modinfo); - module_allow_arch(target_name()); - - qemu_init_subsystems(); - - /* first pass of option parsing */ + /* + * First pass: options needed before TargetInfo is fixed. + */ optind = 1; while (optind < argc) { if (argv[optind][0] != '-') { - /* disk image */ optind++; } else { const QEMUOption *popt; @@ -2921,10 +2915,21 @@ void qemu_init(int argc, char **argv) case QEMU_OPTION_nouserconfig: userconfig = false; break; + case QEMU_OPTION_target: + target_option = optarg; + break; } } } + module_call_init(MODULE_INIT_TARGET_INFO); + target_info_qom_set_target(target_option); + + module_init_info(qemu_modinfo); + module_allow_arch(target_name()); + + qemu_init_subsystems(); + machine_opts_dict = qdict_new(); if (userconfig) { qemu_read_default_config_file(&error_fatal); @@ -3687,6 +3692,9 @@ void qemu_init(int argc, char **argv) case QEMU_OPTION_nouserconfig: /* Nothing to be parsed here. Especially, do not error out below. */ break; + case QEMU_OPTION_target: + /* Parsed before TargetInfo is selected. */ + break; #if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) case QEMU_OPTION_daemonize: os_set_daemonize(true); diff --git a/target-info-qom.c b/target-info-qom.c index 2c1c57b05fd..be03573a699 100644 --- a/target-info-qom.c +++ b/target-info-qom.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/help_option.h" #include "qapi/error.h" #include "qom/object.h" #include "qemu/target-info-impl.h" @@ -87,7 +88,139 @@ const TargetInfo *target_info(void) return target_info_ptr; } -void target_info_qom_set_target(void) +static const TargetInfo *target_info_from_class(ObjectClass *oc) +{ + return TARGET_INFO_CLASS(oc)->target_info; +} + +/* Default token is target_name; the other endian is target_name-le or -be. */ +static char *target_info_option_name(const TargetInfo *ti) +{ + if (ti->is_default) { + return g_strdup(ti->target_name); + } + if (ti->endianness == ENDIAN_MODE_BIG) { + return g_strdup_printf("%s-be", ti->target_name); + } + return g_strdup_printf("%s-le", ti->target_name); +} + +static gint target_info_token_name_cmp(gconstpointer a, gconstpointer b, + gpointer data) +{ + const TargetInfo *ta = target_info_from_class((ObjectClass *)a); + const TargetInfo *tb = target_info_from_class((ObjectClass *)b); + g_autofree char *na = target_info_option_name(ta); + g_autofree char *nb = target_info_option_name(tb); + + return strcmp(na, nb); +} + +static void G_NORETURN target_info_list_and_exit(GSList *targets) +{ + printf("Supported targets:\n"); + for (GSList *l = targets; l; l = l->next) { + const TargetInfo *ti = target_info_from_class(l->data); + g_autofree char *opt_name = target_info_option_name(ti); + + printf(" %s%s\n", opt_name, ti->is_default ? " (default)" : ""); + } + exit(0); +} + +static const TargetInfo *target_info_find_default(GSList *targets, + size_t *num_default) +{ + const TargetInfo *chosen = NULL; + + *num_default = 0; + for (GSList *l = targets; l; l = l->next) { + const TargetInfo *ti = target_info_from_class(l->data); + + if (ti->is_default) { + (*num_default)++; + chosen = ti; + } + } + return chosen; +} + +static const TargetInfo *target_info_find_token(GSList *targets, + const char *token) +{ + for (GSList *l = targets; l; l = l->next) { + const TargetInfo *ti = target_info_from_class(l->data); + g_autofree char *opt_name = target_info_option_name(ti); + + if (!strcmp(opt_name, token)) { + return ti; + } + } + return NULL; +} + +static bool target_info_base_match_token(const char *base, const char *token) +{ + size_t nlen = strlen(token); + size_t blen; + + if (!base || !token[0] || !g_str_has_suffix(base, token)) { + return false; + } + blen = strlen(base); + return blen == nlen || base[blen - nlen - 1] == '-'; +} + +static const TargetInfo *target_info_find_prgname(GSList *targets, + const char *base) +{ + const TargetInfo *chosen = NULL; + size_t best_len = 0; + + if (!base || !base[0]) { + return NULL; + } + + for (GSList *l = targets; l; l = l->next) { + const TargetInfo *ti = target_info_from_class(l->data); + g_autofree char *opt_name = target_info_option_name(ti); + size_t nlen = strlen(opt_name); + + if (nlen > best_len && + target_info_base_match_token(base, opt_name)) { + chosen = ti; + best_len = nlen; + } + } + return chosen; +} + +static const TargetInfo *target_info_from_prgname(GSList *targets) +{ + const char *prg = g_get_prgname(); + g_autofree char *base = NULL; + char *dot; + const TargetInfo *chosen; + + if (!prg || !prg[0]) { + return NULL; + } + + base = g_path_get_basename(prg); + dot = strrchr(base, '.'); + if (dot && g_ascii_strcasecmp(dot, ".exe") == 0) { + *dot = '\0'; + } + + chosen = target_info_find_prgname(targets, base); + if (!chosen && strlen(base) > 1 && g_str_has_suffix(base, "w")) { + base[strlen(base) - 1] = '\0'; + chosen = target_info_find_prgname(targets, base); + } + return chosen; +} + +void target_info_qom_set_target(const char *name) { g_autoptr(GSList) targets = object_class_get_list(TYPE_TARGET_INFO, false); const TargetInfo *chosen = NULL; @@ -98,25 +231,37 @@ void target_info_qom_set_target(void) error_setg(&error_fatal, "no target-info is available"); } - if (num_found == 1) { - target_info_ptr = TARGET_INFO_CLASS(targets->data)->target_info; + if (name && is_help_option(name)) { + targets = g_slist_sort_with_data(targets, target_info_token_name_cmp, + NULL); + target_info_list_and_exit(targets); + } + + if (name && name[0]) { + chosen = target_info_find_token(targets, name); + if (!chosen) { + error_setg(&error_fatal, "unknown target '%s'", name); + } + target_info_ptr = chosen; return; } - for (GSList *l = targets; l; l = l->next) { - const TargetInfo *ti = TARGET_INFO_CLASS(l->data)->target_info; + chosen = target_info_from_prgname(targets); + if (chosen) { + target_info_ptr = chosen; + return; + } - if (ti->is_default) { - num_default++; - chosen = ti; - } + if (num_found == 1) { + target_info_ptr = target_info_from_class(targets->data); + return; } + chosen = target_info_find_default(targets, &num_default); if (num_default != 1) { error_setg(&error_fatal, num_default == 0 ? "no default target-info is available" : - "more than one default target-info " - "is available"); + "multiple default targets; use -target"); } target_info_ptr = chosen; diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index a3a131c80f8..489c7138bb9 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -174,7 +174,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) /* Initialize qgraph and modules */ qos_graph_init(); module_call_init(MODULE_INIT_TARGET_INFO); - target_info_qom_set_target(); + target_info_qom_set_target(NULL); module_call_init(MODULE_INIT_FUZZ_TARGET); module_call_init(MODULE_INIT_QOM); module_call_init(MODULE_INIT_LIBQOS); -- 2.52.0.windows.1